From c691f0251c562c189abebeea456fb744e301bcba Mon Sep 17 00:00:00 2001 From: AndyWalshe Date: Mon, 10 Jun 2019 23:20:41 +0000 Subject: ONAP audit script detailing pods, docker images, tags and digests Change-Id: Ia8d369c978f3d1da0e98af91415cc50cd36b03a2 Issue-ID: INT-1101 Signed-off-by: AndyWalshe --- test/ete/scripts/probe-onap.py | 236 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 236 insertions(+) create mode 100644 test/ete/scripts/probe-onap.py diff --git a/test/ete/scripts/probe-onap.py b/test/ete/scripts/probe-onap.py new file mode 100644 index 000000000..a1dc235ea --- /dev/null +++ b/test/ete/scripts/probe-onap.py @@ -0,0 +1,236 @@ +#!/usr/bin/env python +""" +This script attempts to probe an ONAP deployment in a Kubernetes cluster on an OpenStack stack +and extract all pods and corresponding docker image versions along with some of the tool versions utilised. +""" + +import os +import sys +import subprocess +import argparse +import logging + +OPENSTACK_GET_SERVER_IPS = 'openstack server list --name "^(%s-).*" -c Name -c Networks -f value --sort-column Name' + +SSH_CMD_TEMPLATE = 'ssh -o StrictHostKeychecking=no -i %s ubuntu@%s "sudo su -c \\"%s\\""' + +KUBECTL_GET_ALL_POD_IMAGES_AND_SHAS = 'kubectl get pods --all-namespaces -o=jsonpath=\'{range .items[*]}' \ + '{\\\\\\"\\n\\\\\\"}{.metadata.name}{\\\\\\":::\\\\\\"}' \ + '{range .status.containerStatuses[*]}{.image}{\\\\\\"___\\\\\\"}' \ + '{.imageID}{\\\\\\" \\\\\\"}{end}{end}{\\\\\\"\\n\\\\\\"}\'' + +DOCKER_INSPECT = 'docker inspect --format=\'{{index .RepoTags 0}}{{\\\\\\" \\\\\\"}}{{index .RepoDigests 0}}\' ' \ + '\$(docker images -q | uniq| tr \'\n\' \' \')' + +KUBECTL_VERSION = 'kubectl version' +DOCKER_VERSION = 'docker --version' + +logging.basicConfig(level=logging.DEBUG, format='%(message)s') +file_log = logging.FileHandler(filename='onap-probe-report.txt', mode='w') +file_log.setLevel(logging.INFO) +formatter = logging.Formatter('%(message)s') +file_log.setFormatter(formatter) +logging.getLogger('').addHandler(file_log) + + +class CommandResult(object): + def __init__(self, exit_code, stdout, stderr): + self.exit_code = exit_code + self.stdout = stdout + self.stderr = stderr + + +def run_command_or_exit(command, message=""): + if message: + logging.debug(message) + logging.debug('cmd> ' + command) + + child = subprocess.Popen(command, stdout=subprocess.PIPE, + stderr=subprocess.PIPE, shell=True) + result = child.communicate() + + cmd_result = CommandResult(child.returncode, + result[0].strip(), result[1].strip()) + + if cmd_result.exit_code: + logging.error("exit_code: '%d', stdout: '%s', stderr: '%s'" % + (cmd_result.exit_code, cmd_result.stdout, cmd_result.stderr)) + sys.exit(1) + + return cmd_result + + +class OpenStackK8sCluster(object): + def __init__(self, stack_name, identity_file): + self.stack_name = stack_name + self.identity_file = identity_file + self.servers = {} + self.vm_docker_images = set() + self.kubectl_version = 'unknown' + self.docker_version = 'unknown' + + response = run_command_or_exit(OPENSTACK_GET_SERVER_IPS % stack_name, + "Get all stack servers and ip addressed using stack name").stdout + for line in response.split('\n'): + parts = line.split() + self.servers[parts[0].replace(stack_name+'-', "")] = parts[2] + + def __str__(self): + desc = "Stack name: " + self.stack_name + '\n' + for key, value in sorted(self.servers.items()): + desc += " " + key + " : " + value + "\n" + return desc.strip() + + def get_stack_name(self): + return self.stack_name + + def get_identity_file(self): + return self.identity_file + + def get_rancher_ip_address(self): + return self.servers['rancher'] + + def get_worker_nodes(self): + return [value for key, value in self.servers.items() if 'k8s-' in key.lower()] + + def get_orchestrators(self): + return [value for key, value in self.servers.items() if 'orch-' in key.lower()] + + def determine_docker_images_on_vms(self): + for node_ip in self.get_worker_nodes() + self.get_orchestrators(): + command = SSH_CMD_TEMPLATE % (self.get_identity_file(), node_ip, DOCKER_INSPECT) + vm_inspect_results = run_command_or_exit(command, "Examine server and list docker images").stdout + + for inspect_line in vm_inspect_results.split('\n'): + name_tag, name_digest = inspect_line.split(' ') + name, tag = name_tag.rsplit(':', 1) + digest = name_digest.split('sha256:')[1] + self.vm_docker_images.add((name, tag, digest)) + + def get_docker_images_on_vms(self): + return self.vm_docker_images + + def get_number_of_vm_docker_images(self): + return len(self.vm_docker_images) + + def determine_kubectl_version(self): + command = SSH_CMD_TEMPLATE % (self.get_identity_file(), self.get_rancher_ip_address(), KUBECTL_VERSION) + self.kubectl_version = run_command_or_exit(command, "Examine rancher vm to determine kubectl version").stdout + + def get_kubectl_version(self): + return self.kubectl_version + + def determine_docker_version(self): + command = SSH_CMD_TEMPLATE % (self.get_identity_file(), self.get_worker_nodes()[0], DOCKER_VERSION) + self.docker_version = run_command_or_exit(command, "Examine worker node to determine docker version").stdout + + def get_docker_version(self): + return self.docker_version + + +class OnapDeployment(object): + def __init__(self, openstack_stack): + self.stack = openstack_stack + self.raw = "" + self.pods = [] + self.unique_images = set() + + def dig(self): + command = SSH_CMD_TEMPLATE % (self.stack.get_identity_file(), self.stack.get_rancher_ip_address(), + KUBECTL_GET_ALL_POD_IMAGES_AND_SHAS) + self.raw = run_command_or_exit(command, "Use kubectl to retrieve all pods and pod images in K8S cluster").stdout + + for row in self.raw.strip().split("\n"): + self.pods.append(Pod(row)) + + for pod in self.pods: + for image in pod.get_images(): + self.unique_images.add(image) + + def __str__(self): + desc = "Pods and docker images:\n" + for pod in sorted(self.pods): + desc += str(pod) + return desc.strip() + + def get_number_of_pods(self): + return len(self.pods) + + def get_docker_images(self): + return sorted(self.unique_images) + + def get_number_of_unique_docker_images(self): + return len(self.unique_images) + + def get_number_of_docker_images(self): + images = [] + for pod in self.pods: + for image in pod.get_images(): + images.append(image) + return len(images) + + +class Pod(object): + def __init__(self, data): + self.name, images = data.strip().split(":::") + self.shas_images = {} + for item in images.split(" "): + image_raw, sha_raw = item.split("___") + if "sha256:" in images: + self.shas_images[sha_raw.split("sha256:")[1]] = image_raw + + def get_images(self): + return self.shas_images.values() + + def __cmp__(self, other): + return cmp(self.name, other.name) + + def __str__(self): + desc = self.name + "\n" + for key, value in sorted(self.shas_images.items(), key=lambda x: x[1]): + desc += " " + value + ", " + key + "\n" + return desc + + +def main(): + # Disable output buffering to receive the output instantly + sys.stdout = os.fdopen(sys.stdout.fileno(), "w", 0) + sys.stderr = os.fdopen(sys.stderr.fileno(), "w", 0) + + help_desc = "Script to probe an ONAP k8s cluster on an OpenStack stack and report back information on the " \ + "stack vms, all pods and corresponding docker image versions/digests and some of the tool versions " \ + "utilised." + parser = argparse.ArgumentParser(description=help_desc) + parser.add_argument("-s", "--stack-name", dest="stack_name", + help="OpenStack stack name used by this ONAP deployment", + metavar="STACKNAME", required=True) + parser.add_argument("-i", "--identity_file", dest="identity_file", + help="OpenStack identity file to be used by ssh to access servers", + metavar="IDENTITY-FILE", required=True) + args = parser.parse_args() + + openstack_k8s = OpenStackK8sCluster(args.stack_name, args.identity_file) + openstack_k8s.determine_kubectl_version() + openstack_k8s.determine_docker_version() + openstack_k8s.determine_docker_images_on_vms() + + onap_dep = OnapDeployment(openstack_k8s) + onap_dep.dig() + + logging.info('\n%s\n' % openstack_k8s) + logging.info("number of pods: %d" % onap_dep.get_number_of_pods()) + logging.info("number of docker images in pods: %d" % onap_dep.get_number_of_docker_images()) + logging.info("number of unique docker images in pods: %d" % onap_dep.get_number_of_unique_docker_images()) + logging.info("number of unique docker images on vms: %d" % openstack_k8s.get_number_of_vm_docker_images()) + logging.info("docker version:\n%s" % openstack_k8s.get_docker_version()) + logging.info("kubectl version:\n%s" % openstack_k8s.get_kubectl_version()) + + logging.info("\n%s\n" % onap_dep) + + logging.info(",,") + for entry in sorted(openstack_k8s.get_docker_images_on_vms()): + logging.info('%s,%s,%s' % (entry[0], entry[1], entry[2])) + + +if __name__ == "__main__": + main() -- cgit 1.2.3-korg