2 # -*- coding: utf-8 -*-
3 # SPDX-License-Identifier: Apache-2.0
8 from onapsdk.configuration import settings
9 from natural.date import delta
10 from xtesting.core import testcase
11 from kubernetes import client, config
12 from kubernetes.stream import stream
13 from urllib3.exceptions import MaxRetryError, NewConnectionError
14 from jinja2 import Environment, PackageLoader, select_autoescape
16 from ..base import BaseStep
17 from .resources import Pod, Container, Service, Job
18 from .resources import Deployment, StatefulSet, DaemonSet, Pvc, ReplicaSet
19 from .resources import ConfigMap, Secret, Ingress
20 from onaptests.utils.exceptions import StatusCheckException
22 NAMESPACE = settings.K8S_ONAP_NAMESPACE
24 FULL_LOGS_CONTAINERS = [
25 'dcae-bootstrap', 'dcae-cloudify-manager', 'aai-resources',
26 'aai-traversal', 'aai-modelloader', 'sdnc', 'so', 'so-bpmn-infra',
27 'so-openstack-adapter', 'so-sdc-controller', 'mariadb-galera', 'sdc-be',
31 # patterns to be excluded from the check
32 WAIVER_LIST = ['integration']
34 SPECIFIC_LOGS_CONTAINERS = {
35 'sdc-be': ['/var/log/onap/sdc/sdc-be/error.log'],
36 'sdc-onboarding-be': ['/var/log/onap/sdc/sdc-onboarding-be/error.log'],
38 '/opt/app/osaaf/logs/cm/cm-service.log',
39 '/opt/app/osaaf/logs/cm/cm-init.log'
42 '/opt/app/osaaf/logs/fs/fs-service.log',
43 '/opt/app/osaaf/logs/fs/fs-init.log'
46 '/opt/app/osaaf/logs/locate/locate-service.log',
47 '/opt/app/osaaf/logs/locate/locate-init.log'
50 '/opt/app/osaaf/logs/service/authz-service.log',
51 '/opt/app/osaaf/logs/service/authz-init.log'
54 '/var/log/onap/sdc/sdc-be/debug.log',
55 '/var/log/onap/sdc/sdc-be/error.log'
58 '/var/log/onap/sdc/sdc-fe/debug.log',
59 '/var/log/onap/sdc/sdc-fe/error.log'
62 '/var/log/onap/vid/audit.log',
63 '/var/log/onap/vid/application.log',
64 '/var/log/onap/vid/debug.log',
65 '/var/log/onap/vid/error.log'
69 DOCKER_REPOSITORIES = [
70 'nexus3.onap.org:10001', 'docker.elastic.co', 'docker.io', 'library',
71 'registry.gitlab.com', 'registry.hub.docker.com', 'k8s.gcr.io', 'gcr.io'
73 DOCKER_REPOSITORIES_NICKNAMES = {
74 'nexus3.onap.org:10001': 'onap',
75 'docker.elastic.co': 'elastic',
76 'docker.io': 'dockerHub (docker.io)',
77 'registry.hub.docker.com': 'dockerHub (registry)',
78 'registry.gitlab.com': 'gitlab',
79 'library': 'dockerHub (library)',
80 'default': 'dockerHub',
81 'k8s.gcr.io': 'google (k8s.gcr)',
82 'gcr.io': 'google (gcr)'
86 'postgreSQL': ['crunchydata/crunchy-postgres', 'postgres'],
87 'mariadb': ['adfinissygroup/k8s-mariadb-galera-centos', 'mariadb'],
89 'bitnami/elasticsearch', 'elasticsearch/elasticsearch',
90 'onap/clamp-dashboard-elasticsearch'
92 'nginx': ['bitnami/nginx', 'nginx'],
94 'cassandra', 'onap/music/cassandra_3_11', 'onap/music/cassandra_music',
97 'zookeeper': ['google_samples/k8szk', 'onap/dmaap/zookeeper', 'zookeeper'],
100 'onap/org.onap.dcaegen2.deployments.redis-cluster-container'
102 'consul': ['consul', 'oomk8s/consul'],
103 'rabbitmq': ['ansible/awx_rabbitmq', 'rabbitmq']
106 MAX_LOG_BYTES = 512000
109 class CheckNamespaceStatusStep(BaseStep):
110 """Check status of all k8s resources in the selected namespace."""
112 __logger = logging.getLogger(__name__)
114 def __init__(self, cleanup: bool = False,**kwargs):
115 """Init CheckNamespaceStatusStep."""
116 super().__init__(cleanup=cleanup)
118 if settings.STATUS_RESULTS_DIRECTORY:
119 self.res_dir = f"{settings.STATUS_RESULTS_DIRECTORY}"
121 self.res_dir = f"{testcase.TestCase.dir_results}/kubernetes-status"
123 if settings.IN_CLUSTER:
124 config.load_incluster_config()
126 config.load_kube_config(config_file=settings.K8S_CONFIG)
128 self.core = client.CoreV1Api()
129 self.batch = client.BatchV1Api()
130 self.app = client.AppsV1Api()
131 self.networking = client.NetworkingV1Api()
133 self.__logger.debug("namespace status init started")
137 self.deployments = []
139 self.statefulsets = []
148 def description(self) -> str:
149 """Step description."""
150 return "Check status of all k8s resources in the selected namespace."
153 def component(self) -> str:
154 """Component name."""
157 @BaseStep.store_state
159 """Check status of all k8s resources in the selected namespace.
162 - K8S_ONAP_NAMESPACE.
166 if settings.STORE_ARTIFACTS:
167 os.makedirs(self.res_dir, exist_ok=True)
168 self.__logger.debug("start test")
170 self.k8s_pods = self.core.list_namespaced_pod(NAMESPACE).items
171 self.__logger.info("%4s Pods in the namespace", len(self.k8s_pods))
173 self.k8s_jobs = self.batch.list_namespaced_job(NAMESPACE).items
174 self.__logger.info("%4s Jobs in the namespace", len(self.k8s_jobs))
176 self.k8s_deployments = self.app.list_namespaced_deployment(
178 self.__logger.info("%4s Deployments in the namespace",
179 len(self.k8s_deployments))
181 self.k8s_replicasets = self.app.list_namespaced_replica_set(
183 self.__logger.info("%4s Replicasets in the namespace",
184 len(self.k8s_replicasets))
186 self.k8s_statefulsets = self.app.list_namespaced_stateful_set(
188 self.__logger.info("%4s StatefulSets in the namespace",
189 len(self.k8s_statefulsets))
191 self.k8s_daemonsets = self.app.list_namespaced_daemon_set(
193 self.__logger.info("%4s DaemonSets in the namespace",
194 len(self.k8s_daemonsets))
196 self.k8s_services = self.core.list_namespaced_service(
198 self.__logger.info("%4s Services in the namespace",
199 len(self.k8s_services))
201 self.k8s_pvcs = self.core.list_namespaced_persistent_volume_claim(
203 self.__logger.info("%4s PVCs in the namespace", len(self.pvcs))
205 self.k8s_configmaps = self.core.list_namespaced_config_map(
207 self.__logger.info("%4s ConfigMaps in the namespace",
208 len(self.configmaps))
210 self.k8s_secrets = self.core.list_namespaced_secret(
212 self.__logger.info("%4s Secrets in the namespace",
215 self.k8s_ingresses = self.networking.list_namespaced_ingress(
217 self.__logger.info("%4s Ingresses in the namespace",
219 except (ConnectionRefusedError, MaxRetryError, NewConnectionError):
220 self.__logger.error("namespace status test failed.")
221 self.__logger.error("cannot connect to Kubernetes.")
222 return testcase.TestCase.EX_TESTCASE_FAILED
224 self.failing_statefulsets = []
225 self.failing_jobs = []
226 self.failing_deployments = []
227 self.failing_replicasets = []
228 self.failing_daemonsets = []
229 self.failing_pvcs = []
232 self.jinja_env = Environment(autoescape=select_autoescape(['html']),
233 loader=PackageLoader('onaptests.templates','status'))
234 self.parse_services()
235 jobs_pods = self.parse_jobs()
236 self.parse_pods(excluded_pods=jobs_pods)
237 self.parse_deployments()
238 self.parse_replicasets()
239 self.parse_statefulsets()
240 self.parse_daemonsets()
242 self.parse_configmaps()
244 self.parse_ingresses()
245 self.parse_versions()
246 if settings.STORE_ARTIFACTS:
247 self.jinja_env.get_template('index.html.j2').stream(
249 delta=delta).dump('{}/index.html'.format(self.res_dir))
250 self.jinja_env.get_template('raw_output.txt.j2').stream(
251 ns=self, namespace=NAMESPACE).dump('{}/onap-k8s.log'.format(
254 if len(self.jobs) > 0:
255 self.details['jobs'] = {
256 'number': len(self.jobs),
257 'number_failing': len(self.failing_jobs),
258 'failing': self.map_by_name(self.failing_jobs)
260 if len(self.deployments) > 0:
261 self.details['deployments'] = {
262 'number': len(self.deployments),
263 'number_failing': len(self.failing_deployments),
264 'failing': self.map_by_name(self.failing_deployments)
266 if len(self.replicasets) > 0:
267 self.details['replicasets'] = {
268 'number': len(self.replicasets),
269 'number_failing': len(self.failing_replicasets),
270 'failing': self.map_by_name(self.failing_replicasets)
272 if len(self.statefulsets) > 0:
273 self.details['statefulsets'] = {
274 'number': len(self.statefulsets),
275 'number_failing': len(self.failing_statefulsets),
276 'failing': self.map_by_name(self.failing_statefulsets)
278 if len(self.daemonsets) > 0:
279 self.details['daemonsets'] = {
280 'number': len(self.daemonsets),
281 'number_failing': len(self.failing_daemonsets),
282 'failing': self.map_by_name(self.failing_daemonsets)
284 if len(self.pvcs) > 0:
285 self.details['pvcs'] = {
286 'number': len(self.pvcs),
287 'number_failing': len(self.failing_pvcs),
288 'failing': self.map_by_name(self.failing_pvcs)
291 self.__logger.error("namespace status test failed.")
292 self.__logger.error("number of errored Jobs: %s",
293 len(self.failing_jobs))
294 self.__logger.error("number of errored Deployments: %s",
295 len(self.failing_deployments))
296 self.__logger.error("number of errored Replicasets: %s",
297 len(self.failing_replicasets))
298 self.__logger.error("number of errored StatefulSets: %s",
299 len(self.failing_statefulsets))
300 self.__logger.error("number of errored DaemonSets: %s",
301 len(self.failing_daemonsets))
302 self.__logger.error("number of errored PVCs: %s",
303 len(self.failing_pvcs))
304 raise StatusCheckException
306 def parse_pods(self, excluded_pods=None):
307 """Parse the pods status."""
308 self.__logger.info("%4s pods to parse", len(self.k8s_pods))
309 for k8s in self.k8s_pods:
312 if excluded_pods and pod in excluded_pods:
315 if k8s.status.init_container_statuses:
316 for k8s_container in k8s.status.init_container_statuses:
317 pod.runned_init_containers += self.parse_container(
318 pod, k8s_container, init=True)
319 if k8s.status.container_statuses:
320 for k8s_container in k8s.status.container_statuses:
321 pod.running_containers += self.parse_container(
323 pod.events = self.core.list_namespaced_event(
325 field_selector="involvedObject.name={}".format(pod.name)).items
326 if settings.STORE_ARTIFACTS:
327 self.jinja_env.get_template('pod.html.j2').stream(pod=pod).dump(
328 '{}/pod-{}.html'.format(self.res_dir, pod.name))
329 if any(waiver_elt in pod.name for waiver_elt in WAIVER_LIST):
330 self.__logger.warn("Waiver pattern found in pod, exclude %s", pod.name)
332 self.pods.append(pod)
334 def parse_container(self, pod, k8s_container, init=False):
335 """Get the logs of a container."""
339 containers_list = pod.containers
340 container = Container(name=k8s_container.name)
341 container.restart_count = k8s_container.restart_count
342 container.set_status(k8s_container.state)
343 container.ready = k8s_container.ready
344 container.image = k8s_container.image
347 containers_list = pod.init_containers
348 if container.restart_count > pod.init_restart_count:
349 pod.init_restart_count = container.restart_count
350 if not container.ready:
351 pod.init_done = False
353 if container.restart_count > pod.restart_count:
354 pod.restart_count = container.restart_count
360 logs = self.core.read_namespaced_pod_log(
363 container=container.name,
364 limit_bytes=MAX_LOG_BYTES,
366 except UnicodeDecodeError:
367 logs= "{0} has an unicode decode error...".format(pod.name)
369 "{0} has an unicode decode error in the logs...", pod.name,
371 if settings.STORE_ARTIFACTS:
373 "{}/pod-{}-{}.log".format(self.res_dir,
374 pod.name, container.name),
376 log_result.write(logs)
377 if (not container.ready) and container.restart_count > 0:
378 old_logs = self.core.read_namespaced_pod_log(
381 container=container.name,
383 if settings.STORE_ARTIFACTS:
385 "{}/pod-{}-{}.old.log".format(self.res_dir,
389 log_result.write(old_logs)
390 if (container.name in FULL_LOGS_CONTAINERS):
391 logs = self.core.read_namespaced_pod_log(
392 pod.name, NAMESPACE, container=container.name)
393 if settings.STORE_ARTIFACTS:
395 "{}/pod-{}-{}.log".format(self.res_dir,
396 pod.name, container.name),
398 log_result.write(logs)
399 if (container.name in SPECIFIC_LOGS_CONTAINERS):
400 for log_file in SPECIFIC_LOGS_CONTAINERS[container.name]:
401 exec_command = ['/bin/sh', '-c', "cat {}".format(log_file)]
402 log_files[log_file] = stream(
403 self.core.connect_get_namespaced_pod_exec,
406 container=container.name,
407 command=exec_command,
412 log_file_slug = log_file.split('.')[0].split('/')[-1]
414 "{}/pod-{}-{}-{}.log".format(
415 self.res_dir, pod.name,
416 container.name, log_file_slug),
418 log_result.write(log_files[log_file])
419 except client.rest.ApiException as exc:
420 self.__logger.warning("%scontainer %s of pod %s has an exception: %s",
421 prefix, container.name, pod.name, exc.reason)
422 if settings.STORE_ARTIFACTS:
423 self.jinja_env.get_template('container_log.html.j2').stream(
428 log_files=log_files).dump('{}/pod-{}-{}-logs.html'.format(
429 self.res_dir, pod.name, container.name))
430 if any(waiver_elt in container.name for waiver_elt in WAIVER_LIST):
432 "Waiver pattern found in container, exclude %s", container.name)
434 containers_list.append(container)
435 if k8s_container.ready:
439 def parse_services(self):
440 """Parse the services."""
441 self.__logger.info("%4s services to parse", len(self.k8s_services))
442 for k8s in self.k8s_services:
443 service = Service(k8s=k8s)
446 service.failed_pods) = self._find_child_pods(k8s.spec.selector)
448 if settings.STORE_ARTIFACTS:
449 self.jinja_env.get_template('service.html.j2').stream(
450 service=service).dump('{}/service-{}.html'.format(
451 self.res_dir, service.name))
452 self.services.append(service)
454 def parse_jobs(self):
456 Return a list of Pods that were created to perform jobs.
458 self.__logger.info("%4s jobs to parse", len(self.k8s_jobs))
460 for i in range(len(self.k8s_jobs)):
461 k8s = self.k8s_jobs[i]
465 if k8s.spec.selector and k8s.spec.selector.match_labels:
466 (job.pods, job.failed_pods) = self._find_child_pods(
467 k8s.spec.selector.match_labels)
469 field_selector = "involvedObject.name={}".format(job.name)
470 field_selector += ",involvedObject.kind=Job"
471 job.events = self.core.list_namespaced_event(
473 field_selector=field_selector).items
475 if settings.STORE_ARTIFACTS:
476 self.jinja_env.get_template('job.html.j2').stream(job=job).dump(
477 '{}/job-{}.html'.format(self.res_dir, job.name))
480 if not k8s.status.completion_time:
481 self.__logger.warning("a Job is in error: {}".format(job.name))
483 waiver_elt not in job.name for waiver_elt in WAIVER_LIST):
484 self.failing_jobs.append(job)
487 if any(waiver_elt not in job.name for waiver_elt in WAIVER_LIST):
488 self.jobs.append(job)
489 jobs_pods += job_pods
492 def parse_deployments(self):
493 """Parse the deployments."""
494 self.__logger.info("%4s deployments to parse",
495 len(self.k8s_deployments))
496 for i in range(len(self.k8s_deployments)):
497 k8s = self.k8s_deployments[i]
498 deployment = Deployment(k8s=k8s)
500 if k8s.spec.selector and k8s.spec.selector.match_labels:
502 deployment.failed_pods) = self._find_child_pods(
503 k8s.spec.selector.match_labels)
504 field_selector = "involvedObject.name={}".format(deployment.name)
505 field_selector += ",involvedObject.kind=Deployment"
506 deployment.events = self.core.list_namespaced_event(
508 field_selector=field_selector).items
510 if settings.STORE_ARTIFACTS:
511 self.jinja_env.get_template('deployment.html.j2').stream(
512 deployment=deployment).dump('{}/deployment-{}.html'.format(
513 self.res_dir, deployment.name))
515 if k8s.status.unavailable_replicas:
516 self.__logger.warning("a Deployment is in error: {}".format(deployment.name))
517 self.failing_deployments.append(deployment)
520 self.deployments.append(deployment)
522 def parse_replicasets(self):
523 """Parse the replicasets."""
524 self.__logger.info("%4s replicasets to parse",
525 len(self.k8s_replicasets))
526 for i in range(len(self.k8s_replicasets)):
527 k8s = self.k8s_replicasets[i]
528 replicaset = ReplicaSet(k8s=k8s)
530 if k8s.spec.selector and k8s.spec.selector.match_labels:
532 replicaset.failed_pods) = self._find_child_pods(
533 k8s.spec.selector.match_labels)
534 field_selector = "involvedObject.name={}".format(replicaset.name)
535 field_selector += ",involvedObject.kind=ReplicaSet"
536 replicaset.events = self.core.list_namespaced_event(
538 field_selector=field_selector).items
540 if settings.STORE_ARTIFACTS:
541 self.jinja_env.get_template('replicaset.html.j2').stream(
542 replicaset=replicaset).dump('{}/replicaset-{}.html'.format(
543 self.res_dir, replicaset.name))
545 if (not k8s.status.ready_replicas
546 or (k8s.status.ready_replicas < k8s.status.replicas)):
547 self.__logger.warning("a ReplicaSet is in error: {}".format(replicaset.name))
548 self.failing_replicasets.append(replicaset)
551 self.replicasets.append(replicaset)
553 def parse_statefulsets(self):
554 """Parse the statefulsets."""
555 self.__logger.info("%4s statefulsets to parse",
556 len(self.k8s_statefulsets))
557 for i in range(len(self.k8s_statefulsets)):
558 k8s = self.k8s_statefulsets[i]
559 statefulset = StatefulSet(k8s=k8s)
561 if k8s.spec.selector and k8s.spec.selector.match_labels:
563 statefulset.failed_pods) = self._find_child_pods(
564 k8s.spec.selector.match_labels)
565 field_selector = "involvedObject.name={}".format(statefulset.name)
566 field_selector += ",involvedObject.kind=StatefulSet"
567 statefulset.events = self.core.list_namespaced_event(
569 field_selector=field_selector).items
571 if settings.STORE_ARTIFACTS:
572 self.jinja_env.get_template('statefulset.html.j2').stream(
573 statefulset=statefulset).dump('{}/statefulset-{}.html'.format(
574 self.res_dir, statefulset.name))
576 if ((not k8s.status.ready_replicas)
577 or (k8s.status.ready_replicas < k8s.status.replicas)):
578 self.__logger.warning("a StatefulSet is in error: {}".format(statefulset.name))
579 self.failing_statefulsets.append(statefulset)
582 self.statefulsets.append(statefulset)
584 def parse_daemonsets(self):
585 """Parse the daemonsets."""
586 self.__logger.info("%4s daemonsets to parse", len(self.k8s_daemonsets))
587 for i in range(len(self.k8s_daemonsets)):
588 k8s = self.k8s_daemonsets[i]
589 daemonset = DaemonSet(k8s=k8s)
591 if k8s.spec.selector and k8s.spec.selector.match_labels:
593 daemonset.failed_pods) = self._find_child_pods(
594 k8s.spec.selector.match_labels)
595 field_selector = "involvedObject.name={}".format(daemonset.name)
596 field_selector += ",involvedObject.kind=DaemonSet"
597 daemonset.events = self.core.list_namespaced_event(
599 field_selector=field_selector).items
601 if settings.STORE_ARTIFACTS:
602 self.jinja_env.get_template('daemonset.html.j2').stream(
603 daemonset=daemonset).dump('{}/daemonset-{}.html'.format(
604 self.res_dir, daemonset.name))
606 if (k8s.status.number_ready < k8s.status.desired_number_scheduled):
607 self.__logger.warning("a DaemonSet is in error: {}".format(daemonset.name))
608 self.failing_daemonsets.append(daemonset)
611 self.daemonsets.append(daemonset)
613 def parse_pvcs(self):
614 """Parse the persistent volume claims."""
615 self.__logger.info("%4s pvcs to parse", len(self.k8s_pvcs))
616 for k8s in self.k8s_pvcs:
618 field_selector = f"involvedObject.name={pvc.name},involvedObject.kind=PersistentVolumeClaim"
619 pvc.events = self.core.list_namespaced_event(
621 field_selector=field_selector).items
623 if k8s.status.phase != "Bound":
624 self.__logger.warning("a PVC is in error: {}".format(pvc.name))
625 self.failing_pvcs.append(pvc)
628 self.pvcs.append(pvc)
630 def parse_configmaps(self):
631 """Parse the config maps."""
632 self.__logger.info("%4s config maps to parse",
633 len(self.k8s_configmaps))
634 for k8s in self.k8s_configmaps:
635 configmap = ConfigMap(k8s=k8s)
636 self.configmaps.append(configmap)
638 def parse_secrets(self):
639 """Parse the secrets."""
640 self.__logger.info("%4s secrets to parse", len(self.k8s_secrets))
641 for k8s in self.k8s_secrets:
642 secret = Secret(k8s=k8s)
643 self.secrets.append(secret)
645 def parse_ingresses(self):
646 """Parse the ingresses."""
647 self.__logger.info("%4s ingresses to parse", len(self.k8s_ingresses))
648 for k8s in self.k8s_secrets:
649 ingress = Ingress(k8s=k8s)
650 self.ingresses.append(ingress)
652 def parse_versions(self):
653 """Parse the versions of the pods."""
654 self.__logger.info("%4s pods to parse", len(self.k8s_pods))
657 for pod in self.k8s_pods:
658 pod_component = pod.metadata.name
659 if 'app' in pod.metadata.labels:
660 pod_component = pod.metadata.labels['app']
662 if 'app.kubernetes.io/name' in pod.metadata.labels:
663 pod_component = pod.metadata.labels[
664 'app.kubernetes.io/name']
666 self.__logger.error("pod %s has no 'app' or 'app.kubernetes.io/name' in metadata: %s", pod_component, pod.metadata.labels)
668 # looks for docker version
669 for container in pod.spec.containers:
671 pod_container_version = container.image.rsplit(":", 1)
672 pod_container_image = pod_container_version[0]
673 pod_container_tag = "latest"
674 if len(pod_container_version) > 1:
675 pod_container_tag = pod_container_version[1]
678 'container': container.name,
679 'component': pod_component,
680 'image': pod_container_image,
681 'version': pod_container_tag
683 pod_versions.append(pod_version)
685 search_rule = "^(?P<source>[^/]*)/*(?P<container>[^:]*):*(?P<version>.*)$"
686 search = re.search(search_rule, container.image)
687 name = "{}/{}".format(search.group('source'),
688 search.group('container'))
689 version = search.group('version')
693 if search.group('source') in DOCKER_REPOSITORIES:
694 source = search.group('source')
695 name = search.group('container')
696 container_search_rule = "^library/(?P<real_container>[^:]*)$"
697 container_search = re.search(container_search_rule, name)
699 name = container_search.group('real_container')
700 for common_component in GENERIC_NAMES.keys():
701 if name in GENERIC_NAMES[common_component]:
702 version = "{}:{}".format(name, version)
703 name = common_component
706 repository = DOCKER_REPOSITORIES_NICKNAMES[source]
707 if name in containers:
708 if version in containers[name]['versions']:
709 if not (pod_component in containers[name]['versions']
710 [version]['components']):
711 containers[name]['versions'][version][
712 'components'].append(pod_component)
713 containers[name]['number_components'] += 1
714 if not (repository in containers[name]['versions']
715 [version]['repositories']):
716 containers[name]['versions'][version][
717 'repositories'].append(repository)
719 containers[name]['versions'][version] = {
720 'repositories': [repository],
721 'components': [pod_component]
723 containers[name]['number_components'] += 1
728 'repositories': [repository],
729 'components': [pod_component]
732 'number_components': 1
735 if settings.STORE_ARTIFACTS:
736 self.jinja_env.get_template('version.html.j2').stream(
737 pod_versions=pod_versions).dump('{}/versions.html'.format(
739 self.jinja_env.get_template('container_versions.html.j2').stream(
740 containers=containers).dump('{}/container_versions.html'.format(
742 # create a json file for version tracking
743 with open(self.res_dir + "/onap_versions.json", "w") as write_file:
744 json.dump(pod_versions, write_file)
746 def _find_child_pods(self, selector):
751 for key, value in selector.items():
752 raw_selector += key + '=' + value + ','
753 raw_selector = raw_selector[:-1]
754 pods = self.core.list_namespaced_pod(
755 NAMESPACE, label_selector=raw_selector).items
757 for known_pod in self.pods:
758 if known_pod.name == pod.metadata.name:
759 pods_list.append(known_pod)
760 if not known_pod.ready():
762 return (pods_list, failed_pods)
764 def map_by_name(self, resources):
765 return list(map(lambda resource: resource.name, resources))