blob: 2ab849387be2e7f43582354c01f86da10cebda92 [file] [log] [blame]
AndyWalshec691f022019-06-10 23:20:41 +00001#!/usr/bin/env python
2"""
3This script attempts to probe an ONAP deployment in a Kubernetes cluster on an OpenStack stack
4and extract all pods and corresponding docker image versions along with some of the tool versions utilised.
5"""
6
7import os
8import sys
9import subprocess
10import argparse
11import logging
12
13OPENSTACK_GET_SERVER_IPS = 'openstack server list --name "^(%s-).*" -c Name -c Networks -f value --sort-column Name'
14
15SSH_CMD_TEMPLATE = 'ssh -o StrictHostKeychecking=no -i %s ubuntu@%s "sudo su -c \\"%s\\""'
16
17KUBECTL_GET_ALL_POD_IMAGES_AND_SHAS = 'kubectl get pods --all-namespaces -o=jsonpath=\'{range .items[*]}' \
18 '{\\\\\\"\\n\\\\\\"}{.metadata.name}{\\\\\\":::\\\\\\"}' \
19 '{range .status.containerStatuses[*]}{.image}{\\\\\\"___\\\\\\"}' \
20 '{.imageID}{\\\\\\" \\\\\\"}{end}{end}{\\\\\\"\\n\\\\\\"}\''
21
22DOCKER_INSPECT = 'docker inspect --format=\'{{index .RepoTags 0}}{{\\\\\\" \\\\\\"}}{{index .RepoDigests 0}}\' ' \
23 '\$(docker images -q | uniq| tr \'\n\' \' \')'
24
25KUBECTL_VERSION = 'kubectl version'
26DOCKER_VERSION = 'docker --version'
27
AndyWalshe3e92cbf2019-10-08 16:33:14 +010028local_registry = ""
29
AndyWalshec691f022019-06-10 23:20:41 +000030logging.basicConfig(level=logging.DEBUG, format='%(message)s')
31file_log = logging.FileHandler(filename='onap-probe-report.txt', mode='w')
32file_log.setLevel(logging.INFO)
33formatter = logging.Formatter('%(message)s')
34file_log.setFormatter(formatter)
35logging.getLogger('').addHandler(file_log)
36
37
38class CommandResult(object):
39 def __init__(self, exit_code, stdout, stderr):
40 self.exit_code = exit_code
41 self.stdout = stdout
42 self.stderr = stderr
43
44
45def run_command_or_exit(command, message=""):
46 if message:
47 logging.debug(message)
48 logging.debug('cmd> ' + command)
49
50 child = subprocess.Popen(command, stdout=subprocess.PIPE,
51 stderr=subprocess.PIPE, shell=True)
52 result = child.communicate()
53
54 cmd_result = CommandResult(child.returncode,
55 result[0].strip(), result[1].strip())
56
57 if cmd_result.exit_code:
58 logging.error("exit_code: '%d', stdout: '%s', stderr: '%s'" %
59 (cmd_result.exit_code, cmd_result.stdout, cmd_result.stderr))
60 sys.exit(1)
61
62 return cmd_result
63
64
65class OpenStackK8sCluster(object):
66 def __init__(self, stack_name, identity_file):
67 self.stack_name = stack_name
68 self.identity_file = identity_file
69 self.servers = {}
70 self.vm_docker_images = set()
71 self.kubectl_version = 'unknown'
72 self.docker_version = 'unknown'
73
74 response = run_command_or_exit(OPENSTACK_GET_SERVER_IPS % stack_name,
75 "Get all stack servers and ip addressed using stack name").stdout
76 for line in response.split('\n'):
77 parts = line.split()
78 self.servers[parts[0].replace(stack_name+'-', "")] = parts[2]
79
80 def __str__(self):
81 desc = "Stack name: " + self.stack_name + '\n'
82 for key, value in sorted(self.servers.items()):
83 desc += " " + key + " : " + value + "\n"
84 return desc.strip()
85
86 def get_stack_name(self):
87 return self.stack_name
88
89 def get_identity_file(self):
90 return self.identity_file
91
AndyWalshe3e92cbf2019-10-08 16:33:14 +010092 def get_nfs_ip_address(self):
93 return self.servers['nfs']
AndyWalshec691f022019-06-10 23:20:41 +000094
95 def get_worker_nodes(self):
96 return [value for key, value in self.servers.items() if 'k8s-' in key.lower()]
97
98 def get_orchestrators(self):
99 return [value for key, value in self.servers.items() if 'orch-' in key.lower()]
100
101 def determine_docker_images_on_vms(self):
102 for node_ip in self.get_worker_nodes() + self.get_orchestrators():
103 command = SSH_CMD_TEMPLATE % (self.get_identity_file(), node_ip, DOCKER_INSPECT)
104 vm_inspect_results = run_command_or_exit(command, "Examine server and list docker images").stdout
105
106 for inspect_line in vm_inspect_results.split('\n'):
107 name_tag, name_digest = inspect_line.split(' ')
108 name, tag = name_tag.rsplit(':', 1)
109 digest = name_digest.split('sha256:')[1]
AndyWalshe3e92cbf2019-10-08 16:33:14 +0100110 if local_registry:
111 name = name.replace(local_registry + "/", "")
AndyWalshec691f022019-06-10 23:20:41 +0000112 self.vm_docker_images.add((name, tag, digest))
113
114 def get_docker_images_on_vms(self):
115 return self.vm_docker_images
116
117 def get_number_of_vm_docker_images(self):
118 return len(self.vm_docker_images)
119
120 def determine_kubectl_version(self):
AndyWalshe3e92cbf2019-10-08 16:33:14 +0100121 command = SSH_CMD_TEMPLATE % (self.get_identity_file(), self.get_nfs_ip_address(), KUBECTL_VERSION)
122 self.kubectl_version = run_command_or_exit(command, "Examine nfs vm to determine kubectl version").stdout
AndyWalshec691f022019-06-10 23:20:41 +0000123
124 def get_kubectl_version(self):
125 return self.kubectl_version
126
127 def determine_docker_version(self):
128 command = SSH_CMD_TEMPLATE % (self.get_identity_file(), self.get_worker_nodes()[0], DOCKER_VERSION)
129 self.docker_version = run_command_or_exit(command, "Examine worker node to determine docker version").stdout
130
131 def get_docker_version(self):
132 return self.docker_version
133
134
135class OnapDeployment(object):
136 def __init__(self, openstack_stack):
137 self.stack = openstack_stack
138 self.raw = ""
139 self.pods = []
140 self.unique_images = set()
141
142 def dig(self):
AndyWalshe3e92cbf2019-10-08 16:33:14 +0100143 command = SSH_CMD_TEMPLATE % (self.stack.get_identity_file(), self.stack.get_nfs_ip_address(),
AndyWalshec691f022019-06-10 23:20:41 +0000144 KUBECTL_GET_ALL_POD_IMAGES_AND_SHAS)
145 self.raw = run_command_or_exit(command, "Use kubectl to retrieve all pods and pod images in K8S cluster").stdout
146
147 for row in self.raw.strip().split("\n"):
148 self.pods.append(Pod(row))
149
150 for pod in self.pods:
151 for image in pod.get_images():
152 self.unique_images.add(image)
153
154 def __str__(self):
155 desc = "Pods and docker images:\n"
156 for pod in sorted(self.pods):
157 desc += str(pod)
158 return desc.strip()
159
160 def get_number_of_pods(self):
161 return len(self.pods)
162
163 def get_docker_images(self):
164 return sorted(self.unique_images)
165
166 def get_number_of_unique_docker_images(self):
167 return len(self.unique_images)
168
169 def get_number_of_docker_images(self):
170 images = []
171 for pod in self.pods:
172 for image in pod.get_images():
173 images.append(image)
174 return len(images)
175
176
177class Pod(object):
178 def __init__(self, data):
179 self.name, images = data.strip().split(":::")
180 self.shas_images = {}
181 for item in images.split(" "):
182 image_raw, sha_raw = item.split("___")
AndyWalshe3e92cbf2019-10-08 16:33:14 +0100183 if local_registry:
184 image_raw = image_raw.replace(local_registry + "/", "")
AndyWalshec691f022019-06-10 23:20:41 +0000185 if "sha256:" in images:
186 self.shas_images[sha_raw.split("sha256:")[1]] = image_raw
187
188 def get_images(self):
189 return self.shas_images.values()
190
191 def __cmp__(self, other):
Bartek Grzybowski4be94a62020-03-05 11:41:08 +0100192 return cmp(self.name, other.name) # pylint: disable=E0602
AndyWalshec691f022019-06-10 23:20:41 +0000193
194 def __str__(self):
195 desc = self.name + "\n"
196 for key, value in sorted(self.shas_images.items(), key=lambda x: x[1]):
197 desc += " " + value + ", " + key + "\n"
198 return desc
199
200
201def main():
202 # Disable output buffering to receive the output instantly
203 sys.stdout = os.fdopen(sys.stdout.fileno(), "w", 0)
204 sys.stderr = os.fdopen(sys.stderr.fileno(), "w", 0)
205
206 help_desc = "Script to probe an ONAP k8s cluster on an OpenStack stack and report back information on the " \
207 "stack vms, all pods and corresponding docker image versions/digests and some of the tool versions " \
208 "utilised."
209 parser = argparse.ArgumentParser(description=help_desc)
210 parser.add_argument("-s", "--stack-name", dest="stack_name",
211 help="OpenStack stack name used by this ONAP deployment",
212 metavar="STACKNAME", required=True)
213 parser.add_argument("-i", "--identity_file", dest="identity_file",
214 help="OpenStack identity file to be used by ssh to access servers",
215 metavar="IDENTITY-FILE", required=True)
AndyWalshe3e92cbf2019-10-08 16:33:14 +0100216 parser.add_argument("-r", "--registry", dest="registry",
217 help="Local registry used to serve docker images which should be " +
218 " stripped from any image names in the script output",
219 metavar="REGISTRY", required=False)
AndyWalshec691f022019-06-10 23:20:41 +0000220 args = parser.parse_args()
AndyWalshe3e92cbf2019-10-08 16:33:14 +0100221 if args.registry:
222 global local_registry
223 local_registry = args.registry
AndyWalshec691f022019-06-10 23:20:41 +0000224
225 openstack_k8s = OpenStackK8sCluster(args.stack_name, args.identity_file)
226 openstack_k8s.determine_kubectl_version()
227 openstack_k8s.determine_docker_version()
228 openstack_k8s.determine_docker_images_on_vms()
229
230 onap_dep = OnapDeployment(openstack_k8s)
231 onap_dep.dig()
232
233 logging.info('\n%s\n' % openstack_k8s)
234 logging.info("number of pods: %d" % onap_dep.get_number_of_pods())
235 logging.info("number of docker images in pods: %d" % onap_dep.get_number_of_docker_images())
236 logging.info("number of unique docker images in pods: %d" % onap_dep.get_number_of_unique_docker_images())
237 logging.info("number of unique docker images on vms: %d" % openstack_k8s.get_number_of_vm_docker_images())
238 logging.info("docker version:\n%s" % openstack_k8s.get_docker_version())
239 logging.info("kubectl version:\n%s" % openstack_k8s.get_kubectl_version())
240
241 logging.info("\n%s\n" % onap_dep)
242
243 logging.info("<image-name>,<image-version>,<image-digest>")
244 for entry in sorted(openstack_k8s.get_docker_images_on_vms()):
245 logging.info('%s,%s,%s' % (entry[0], entry[1], entry[2]))
246
247
248if __name__ == "__main__":
249 main()