blob: a1dc235eacf81b9fa2ce150d25b55075ba0d516e [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
28logging.basicConfig(level=logging.DEBUG, format='%(message)s')
29file_log = logging.FileHandler(filename='onap-probe-report.txt', mode='w')
30file_log.setLevel(logging.INFO)
31formatter = logging.Formatter('%(message)s')
32file_log.setFormatter(formatter)
33logging.getLogger('').addHandler(file_log)
34
35
36class CommandResult(object):
37 def __init__(self, exit_code, stdout, stderr):
38 self.exit_code = exit_code
39 self.stdout = stdout
40 self.stderr = stderr
41
42
43def run_command_or_exit(command, message=""):
44 if message:
45 logging.debug(message)
46 logging.debug('cmd> ' + command)
47
48 child = subprocess.Popen(command, stdout=subprocess.PIPE,
49 stderr=subprocess.PIPE, shell=True)
50 result = child.communicate()
51
52 cmd_result = CommandResult(child.returncode,
53 result[0].strip(), result[1].strip())
54
55 if cmd_result.exit_code:
56 logging.error("exit_code: '%d', stdout: '%s', stderr: '%s'" %
57 (cmd_result.exit_code, cmd_result.stdout, cmd_result.stderr))
58 sys.exit(1)
59
60 return cmd_result
61
62
63class OpenStackK8sCluster(object):
64 def __init__(self, stack_name, identity_file):
65 self.stack_name = stack_name
66 self.identity_file = identity_file
67 self.servers = {}
68 self.vm_docker_images = set()
69 self.kubectl_version = 'unknown'
70 self.docker_version = 'unknown'
71
72 response = run_command_or_exit(OPENSTACK_GET_SERVER_IPS % stack_name,
73 "Get all stack servers and ip addressed using stack name").stdout
74 for line in response.split('\n'):
75 parts = line.split()
76 self.servers[parts[0].replace(stack_name+'-', "")] = parts[2]
77
78 def __str__(self):
79 desc = "Stack name: " + self.stack_name + '\n'
80 for key, value in sorted(self.servers.items()):
81 desc += " " + key + " : " + value + "\n"
82 return desc.strip()
83
84 def get_stack_name(self):
85 return self.stack_name
86
87 def get_identity_file(self):
88 return self.identity_file
89
90 def get_rancher_ip_address(self):
91 return self.servers['rancher']
92
93 def get_worker_nodes(self):
94 return [value for key, value in self.servers.items() if 'k8s-' in key.lower()]
95
96 def get_orchestrators(self):
97 return [value for key, value in self.servers.items() if 'orch-' in key.lower()]
98
99 def determine_docker_images_on_vms(self):
100 for node_ip in self.get_worker_nodes() + self.get_orchestrators():
101 command = SSH_CMD_TEMPLATE % (self.get_identity_file(), node_ip, DOCKER_INSPECT)
102 vm_inspect_results = run_command_or_exit(command, "Examine server and list docker images").stdout
103
104 for inspect_line in vm_inspect_results.split('\n'):
105 name_tag, name_digest = inspect_line.split(' ')
106 name, tag = name_tag.rsplit(':', 1)
107 digest = name_digest.split('sha256:')[1]
108 self.vm_docker_images.add((name, tag, digest))
109
110 def get_docker_images_on_vms(self):
111 return self.vm_docker_images
112
113 def get_number_of_vm_docker_images(self):
114 return len(self.vm_docker_images)
115
116 def determine_kubectl_version(self):
117 command = SSH_CMD_TEMPLATE % (self.get_identity_file(), self.get_rancher_ip_address(), KUBECTL_VERSION)
118 self.kubectl_version = run_command_or_exit(command, "Examine rancher vm to determine kubectl version").stdout
119
120 def get_kubectl_version(self):
121 return self.kubectl_version
122
123 def determine_docker_version(self):
124 command = SSH_CMD_TEMPLATE % (self.get_identity_file(), self.get_worker_nodes()[0], DOCKER_VERSION)
125 self.docker_version = run_command_or_exit(command, "Examine worker node to determine docker version").stdout
126
127 def get_docker_version(self):
128 return self.docker_version
129
130
131class OnapDeployment(object):
132 def __init__(self, openstack_stack):
133 self.stack = openstack_stack
134 self.raw = ""
135 self.pods = []
136 self.unique_images = set()
137
138 def dig(self):
139 command = SSH_CMD_TEMPLATE % (self.stack.get_identity_file(), self.stack.get_rancher_ip_address(),
140 KUBECTL_GET_ALL_POD_IMAGES_AND_SHAS)
141 self.raw = run_command_or_exit(command, "Use kubectl to retrieve all pods and pod images in K8S cluster").stdout
142
143 for row in self.raw.strip().split("\n"):
144 self.pods.append(Pod(row))
145
146 for pod in self.pods:
147 for image in pod.get_images():
148 self.unique_images.add(image)
149
150 def __str__(self):
151 desc = "Pods and docker images:\n"
152 for pod in sorted(self.pods):
153 desc += str(pod)
154 return desc.strip()
155
156 def get_number_of_pods(self):
157 return len(self.pods)
158
159 def get_docker_images(self):
160 return sorted(self.unique_images)
161
162 def get_number_of_unique_docker_images(self):
163 return len(self.unique_images)
164
165 def get_number_of_docker_images(self):
166 images = []
167 for pod in self.pods:
168 for image in pod.get_images():
169 images.append(image)
170 return len(images)
171
172
173class Pod(object):
174 def __init__(self, data):
175 self.name, images = data.strip().split(":::")
176 self.shas_images = {}
177 for item in images.split(" "):
178 image_raw, sha_raw = item.split("___")
179 if "sha256:" in images:
180 self.shas_images[sha_raw.split("sha256:")[1]] = image_raw
181
182 def get_images(self):
183 return self.shas_images.values()
184
185 def __cmp__(self, other):
186 return cmp(self.name, other.name)
187
188 def __str__(self):
189 desc = self.name + "\n"
190 for key, value in sorted(self.shas_images.items(), key=lambda x: x[1]):
191 desc += " " + value + ", " + key + "\n"
192 return desc
193
194
195def main():
196 # Disable output buffering to receive the output instantly
197 sys.stdout = os.fdopen(sys.stdout.fileno(), "w", 0)
198 sys.stderr = os.fdopen(sys.stderr.fileno(), "w", 0)
199
200 help_desc = "Script to probe an ONAP k8s cluster on an OpenStack stack and report back information on the " \
201 "stack vms, all pods and corresponding docker image versions/digests and some of the tool versions " \
202 "utilised."
203 parser = argparse.ArgumentParser(description=help_desc)
204 parser.add_argument("-s", "--stack-name", dest="stack_name",
205 help="OpenStack stack name used by this ONAP deployment",
206 metavar="STACKNAME", required=True)
207 parser.add_argument("-i", "--identity_file", dest="identity_file",
208 help="OpenStack identity file to be used by ssh to access servers",
209 metavar="IDENTITY-FILE", required=True)
210 args = parser.parse_args()
211
212 openstack_k8s = OpenStackK8sCluster(args.stack_name, args.identity_file)
213 openstack_k8s.determine_kubectl_version()
214 openstack_k8s.determine_docker_version()
215 openstack_k8s.determine_docker_images_on_vms()
216
217 onap_dep = OnapDeployment(openstack_k8s)
218 onap_dep.dig()
219
220 logging.info('\n%s\n' % openstack_k8s)
221 logging.info("number of pods: %d" % onap_dep.get_number_of_pods())
222 logging.info("number of docker images in pods: %d" % onap_dep.get_number_of_docker_images())
223 logging.info("number of unique docker images in pods: %d" % onap_dep.get_number_of_unique_docker_images())
224 logging.info("number of unique docker images on vms: %d" % openstack_k8s.get_number_of_vm_docker_images())
225 logging.info("docker version:\n%s" % openstack_k8s.get_docker_version())
226 logging.info("kubectl version:\n%s" % openstack_k8s.get_kubectl_version())
227
228 logging.info("\n%s\n" % onap_dep)
229
230 logging.info("<image-name>,<image-version>,<image-digest>")
231 for entry in sorted(openstack_k8s.get_docker_images_on_vms()):
232 logging.info('%s,%s,%s' % (entry[0], entry[1], entry[2]))
233
234
235if __name__ == "__main__":
236 main()