From 2a5bcdcbc680fb5533da26b6a8b3bb7a6e9724ce Mon Sep 17 00:00:00 2001 From: Matt Clay Date: Mon, 16 Apr 2018 10:10:54 -0700 Subject: [PATCH] Initial import of code from ansible/ansible. --- Dockerfile | 39 ++++++ flask_control.py | 335 +++++++++++++++++++++++++++++++++++++++++++++++ make.sh | 3 + 3 files changed, 377 insertions(+) create mode 100644 Dockerfile create mode 100755 flask_control.py create mode 100755 make.sh diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..9692668 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,39 @@ +FROM fedora:26 + +RUN (cd /lib/systemd/system/sysinit.target.wants/; for i in *; do [ $i == systemd-tmpfiles-setup.service ] || rm -f $i; done); \ +rm -f /lib/systemd/system/multi-user.target.wants/*; \ +rm -f /etc/systemd/system/*.wants/*; \ +rm -f /lib/systemd/system/local-fs.target.wants/*; \ +rm -f /lib/systemd/system/sockets.target.wants/*udev*; \ +rm -f /lib/systemd/system/sockets.target.wants/*initctl*; \ +rm -f /lib/systemd/system/basic.target.wants/*; \ +rm -f /lib/systemd/system/anaconda.target.wants/*; + +RUN dnf clean all && \ + dnf -y --setopt=install_weak_deps=false install \ + git \ + glibc \ + gcc \ + golang-bin \ + python-devel \ + python-pip \ + python-setuptools \ + redhat-rpm-config \ + yum \ + && \ + dnf clean all + +VOLUME /sys/fs/cgroup /run /tmp +ENV container=docker +RUN mkdir -p /opt/gocode +RUN chmod -R 777 /opt/gocode +ENV GOPATH=/opt/gocode +RUN go get -u github.com/vmware/govmomi/vcsim +RUN go get github.com/vmware/govmomi/govc +RUN pip install psutil +RUN pip install flask +ADD flask_control.py /root/flask_control.py + + +EXPOSE 5000 8989 443 80 8080 +CMD ["/root/flask_control.py"] diff --git a/flask_control.py b/flask_control.py new file mode 100755 index 0000000..ff6b716 --- /dev/null +++ b/flask_control.py @@ -0,0 +1,335 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# (c) 2017 James Tanner (@jctanner) +# Abhijeet Kasurde (@akasurde) +# +# Written by James Tanner +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see . + +import os +import psutil +import socket +import subprocess + +from flask import Flask +from flask import jsonify +from flask import request + + +app = Flask(__name__) +GOPATH = os.path.expanduser('/opt/gocode') +VCSIMPATH = os.path.join(GOPATH, 'bin', 'vcsim') +GOVCPATH = os.path.join(GOPATH, 'bin', 'govc') +GOVCURL = None + + +@app.route('/') +def m_index(): + return 'vcsim controller' + + +@app.route('/log') +def get_log(): + """Read and return the vcsim log""" + fdata = '' + if os.path.isfile('vcsim.log'): + with open('vcsim.log', 'rb') as f: + fdata = f.read() + return fdata + + +@app.route('/kill/') +def kill_one(number): + """Kill any arbitrary process id""" + + success = False + e = None + + try: + p = psutil.Process(number) + p.terminate() + success = True + except Exception as e: + pass + + return jsonify({'success': success, 'e': str(e)}) + + +@app.route('/killall') +def kill_all(): + """Kill ALL of the running vcsim pids""" + + results = [] + + for x in psutil.pids(): + p = psutil.Process(x) + if VCSIMPATH in p.cmdline(): + success = False + e = None + try: + p.terminate() + success = True + except Exception as e: + pass + results.append( + {'pid': x, 'cmdline': p.cmdline(), + 'success': success, 'e': str(e)} + ) + + return jsonify(results) + + +@app.route('/spawn') +def spawn_vcsim(): + """Launch vcsim in a background process and return the pid+govcm_url""" + + global GOVCURL + + username = request.args.get('username') or 'user' + password = request.args.get('password') or 'pass' + hostname = request.args.get('hostname') or \ + socket.gethostbyname(socket.gethostname()) + port = request.args.get('port') or '443' + port = int(port) + + # FIXME - enable tracing + if request.args.get('trace'): + trace = True + else: + trace = False + + # vcsim cli options and their default values + cli_opts = [ + ['app', 0], + ['cluster', 0], + ['dc', 1], + ['ds', 1], + ['folder', 1], + ['host', 3], + ['pg', 1], + ['pod', 1], + ['pool', 1], + ['vm', 2] + ] + + # useful for client govc commands + govc_url = 'https://%s:%s@%s:%s' % (username, password, hostname, port) + GOVCURL = govc_url + + # need these to run the service + env = { + 'GOPATH': GOPATH, + 'GOVC_URL': govc_url, + 'GOVC_INSECURE': '1' + } + + # build the command + cmd = [ + VCSIMPATH, + '-httptest.serve', + '%s:%s' % (hostname, port), + ] + + # trace soap requests+responses + if trace: + cmd.append('-trace') + + # esx only allows certain arguments + if request.args.get('esx'): + cmd.append('-esx') + for x in [('vm', 1), ('ds', 1)]: + name = x[0] + default = x[1] + if request.args.get(name): + default = request.args.get(name) + cmd.append('-%s=%s' % (name, default)) + else: + # use all other options as requested for vcenter + for x in cli_opts: + name = x[0] + default = x[1] + if request.args.get(name): + default = request.args.get(name) + cmd.append('-%s=%s' % (name, default)) + cmd = ' '.join(cmd) + cmd += ' > vcsim.log 2>&1' + + # run it with environment settings + p = subprocess.Popen( + cmd, + env=env, + shell=True + ) + + # return the relevant data + pid = p.pid + rdata = { + 'cmd': cmd, + 'pid': pid, + 'host': hostname, + 'port': port, + 'username': username, + 'password': password, + 'GOVC_URL': govc_url + } + + return jsonify(rdata) + + +@app.route('/govc_find') +def govc_find(): + """Run govc find and optionally filter results""" + ofilter = request.args.get('filter') or None + stdout_lines = _get_all_objs(ofilter=ofilter) + return jsonify(stdout_lines) + + +@app.route('/govc_vm_info') +def get_govc_vm_info(): + """Run govc vm info """ + vm_name = request.args.get('vm_name') or None + vm_output = {} + if vm_name: + all_vms = [vm_name] + else: + # Get all VMs + all_vms = _get_all_objs(ofilter='VM') + + for vm_name in all_vms: + vm_info = _get_vm_info(vm_name=vm_name) + name = vm_info.get('Name', vm_name) + vm_output[name] = vm_info + + return jsonify(vm_output) + + +@app.route('/govc_host_info') +def get_govc_host_info(): + """ Run govc host.info """ + host_name = request.args.get("host_name") or None + host_output = {} + if host_name: + all_hosts = [host_name] + else: + all_hosts = _get_all_objs(ofilter='H') + for host_system in all_hosts: + host_info = _get_host_info(host_name=host_system) + name = host_info.get('Name', host_system) + host_output[name] = host_info + + return jsonify(host_output) + + +def _get_host_info(host_name=None): + """ + Get all information of host from vcsim + :param vm_name: Name of host + :return: Dictionary containing information about VM, + where KEY represent attributes and VALUE represent attribute's value + """ + cmd = '%s host.info -host=%s 2>&1' % (GOVCPATH, host_name) + + host_info = {} + if host_name is None: + return host_info + host_info = parse_govc_info(cmd) + + return host_info + + +def _get_vm_info(vm_name=None): + """ + Get all information of VM from vcsim + :param vm_name: Name of VM + :return: Dictionary containing information about VM, + where KEY represent attributes and VALUE represent attribute's value + """ + cmd = '%s vm.info %s 2>&1' % (GOVCPATH, vm_name) + + vm_info = {} + if vm_name is None: + return vm_info + vm_info = parse_govc_info(cmd) + + return vm_info + + +def parse_govc_info(cmd): + """ + Helper function to parse output of govc info commands + :param cmd: command variable to run and parse output for + :return: Dictionary containing information about object + """ + so, se = run_cmd(cmd) + stdout_lines = so.splitlines() + info = {} + for line in stdout_lines: + if ":" in line: + key, value = line.split(":", 1) + key = key.lstrip() + info[key] = value.strip() + + return info + + +def _get_all_objs(ofilter=None): + """ + Get all VM Objects from vcsim + :param ofilter: Specify which object to get + :return: list of Object specified by ofilter + """ + cmd = '%s find ' % GOVCPATH + filter_mapping = dict(VA='a', CCR='c', DC='d', F='f', DVP='g', H='h', + VM='m', N='n', ON='o', RP='p', CR='r', D='s', DVS='w') + if ofilter: + type_filter = filter_mapping.get(ofilter, '') + if type_filter != '': + cmd += '-type %s ' % type_filter + + cmd += "2>&1" + so, se = run_cmd(cmd) + stdout_lines = so.splitlines() + return stdout_lines + + +def run_cmd(cmd): + """ + Helper Function to run commands + :param cmd: Command string to execute + :return: StdOut and StdErr in string format + """ + global GOVCURL + + env = { + 'GOPATH': GOPATH, + 'GOVC_URL': GOVCURL, + 'GOVC_INSECURE': '1' + } + + p = subprocess.Popen( + cmd, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + env=env, + shell=True + ) + + (so, se) = p.communicate() + return so, se + + +if __name__ == "__main__": + app.run(debug=False, host='0.0.0.0') diff --git a/make.sh b/make.sh new file mode 100755 index 0000000..589d949 --- /dev/null +++ b/make.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +docker build --no-cache -t ansible:vcenter-simulator .