From 828a702e99a18ecb8175ada64bca4e43c83bc8cf Mon Sep 17 00:00:00 2001 From: micafer Date: Thu, 4 Oct 2018 16:00:06 +0200 Subject: [PATCH 01/18] Update README and setup --- README.rst | 7 +++++-- setup.py | 38 +++++++++++++++++++------------------- 2 files changed, 24 insertions(+), 21 deletions(-) diff --git a/README.rst b/README.rst index 531e336..3727023 100644 --- a/README.rst +++ b/README.rst @@ -20,12 +20,14 @@ This introduces a cost-efficient approach for Cluster-based computing. Installation ------------ -The program `ec3` requires Python 2.6+, `PLY`_, `PyYAML`_ and an `IM`_ server, which is used to +The program `ec3` requires Python 2.6+, `PLY`_, `PyYAML`_, `Requests`_ and an `IM`_ server, which is used to launch virtual machines. By default `ec3` uses our public `IM`_ server in `servproject.i3m.upv.es`. *Optionally* you can deploy a local `IM`_ server. See `IM documentation `_ for more information. -`PyYAML`_ and `PLY`_ are usually available in distribution repositories (``python-yaml``, ``python-ply`` in Debian; ``PyYAML``, ``python-ply`` in Red Hat; and ``PyYAML``, ``PLY`` in pip). +`PyYAML`_, `PLY`_ and `Requests`_ are usually available in distribution repositories (``python-yaml``, +``python-ply``, ``python-requests`` in Debian; ``PyYAML``, ``python-ply``, ``python-requests`` in Red Hat; +and ``PyYAML``, ``PLY``, ``requests`` in pip). `ec3` can be download from `this `_ git repository:: @@ -143,6 +145,7 @@ Additional information .. _`IM`: https://github.com/grycap/im .. _`PyYAML`: http://pyyaml.org/wiki/PyYAML .. _`PLY`: http://www.dabeaz.com/ply/ +.. _`Requests`: http://docs.python-requests.org/ .. _`EC3 Command-line Interface`: http://ec3.readthedocs.org/en/devel/ec3.html .. _`Command templates`: http://ec3.readthedocs.org/en/devel/ec3.html#command-templates .. _`Authorization file`: http://ec3.readthedocs.org/en/devel/ec3.html#authorization-file diff --git a/setup.py b/setup.py index 1a7d172..9b22714 100644 --- a/setup.py +++ b/setup.py @@ -6,28 +6,28 @@ # Add contextualization dir files install_path = '/etc/ec3/' datafiles = [(os.path.join(install_path, root), [os.path.join(root, f) for f in files]) - for root, dirs, files in os.walk("templates")] - -# Utility function to read the README file. -# Used for the long_description. It's nice, because now 1) we have a top level -# README file and 2) it's easier to type in the README file than to put a raw -# string in below ... -def read(fname): - return open(os.path.join(os.path.dirname(__file__), fname)).read() + for root, dirs, files in os.walk("templates")] setup( - name = "ec3", - version = "0.0.1", - author = "Amanda Calatrava, Eloy Romero, Miguel Caballer", - author_email = "", - description = ("Tool to deploy virtual elastic clusters on the cloud."), - license = "Apache 2.0", - keywords = "cloud cluster elasticity", - url = "http://www.grycap.upv.es/ec3/", - data_files=datafiles, + name="ec3", + version="0.0.1", + author="Amanda Calatrava, Eloy Romero, Miguel Caballer", + author_email="", + description=("Tool to deploy virtual elastic clusters on the cloud."), + license="Apache 2.0", + keywords="cloud cluster elasticity", + url="http://www.grycap.upv.es/ec3/", + data_files=datafiles, packages=['IM2', 'IM2.radl'], package_data={'IM2.radl': ['radl_schema.json']}, scripts=["ec3"], - install_requires=["ply","PyYAML","jsonschema"], - long_description=read('README.rst'), + install_requires=["ply", "PyYAML", "jsonschema", "requests"], + long_description=("Elastic Cloud Computing Cluster (EC3) is a tool to create elastic virtual clusters on top" + "of Infrastructure as a Service (IaaS) providers, either public (such as Amazon Web Services," + "Google Cloud or Microsoft Azure)" + "or on-premises (such as OpenNebula and OpenStack). We offer recipes to deploy TORQUE" + "(optionally with MAUI), SLURM, SGE, HTCondor, Mesos, Nomad and Kubernetes clusters that can be self-managed with CLUES:" + "it starts with a single-node cluster and working nodes will be dynamically deployed and provisioned" + "to fit increasing load (number of jobs at the LRMS). Working nodes will be undeployed when they are idle." + "This introduces a cost-efficient approach for Cluster-based computing."), ) From f95c23bd9128bd204fc23f70752f99b434ff82ad Mon Sep 17 00:00:00 2001 From: micafer Date: Fri, 5 Oct 2018 08:16:29 +0200 Subject: [PATCH 02/18] Fix py3 issues: #55 --- IM2/radl/radl_parse.py | 2 +- ec3 | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/IM2/radl/radl_parse.py b/IM2/radl/radl_parse.py index 792ef2d..bcf07a9 100644 --- a/IM2/radl/radl_parse.py +++ b/IM2/radl/radl_parse.py @@ -204,7 +204,7 @@ def p_configure_sentence(self, t): recipe = "".join(t[5]) if yaml: try: - recipe = yaml.safe_load(recipe) + yaml.safe_load(recipe) except Exception as e: raise RADLParseException("Error parsing YAML: %s" % str(e), line=t.lineno(5)) t[0] = configure(t[2], recipe, line=t.lineno(1)) diff --git a/ec3 b/ec3 index 63f4369..0a9df66 100755 --- a/ec3 +++ b/ec3 @@ -593,6 +593,7 @@ class CLI: parser.add_argument("-ll", "--log-level", dest="log_level", nargs=1, type=int, default=[5], help="log level. 1: debug; 2: info; 3: warning; 4: error") parser.add_argument("-q", "--quiet", action="store_true", dest="quiet", default=False, help="only print messages from front-end") subparsers = parser.add_subparsers(title="subcommands", description="valid subcommands", help="additional help") + parser.set_defaults(func=None) for cmd in commands: cmd.parse(subparsers) CLI.options = parser.parse_args() @@ -603,7 +604,8 @@ class CLI: CLI.logger = logging.getLogger('ec3') # Run command - CLI.options.func(CLI.options) + if CLI.options.func: + CLI.options.func(CLI.options) @staticmethod def display(msg, alt=None, level=logging.INFO, exception=False): From 376d87bdca355682565a215d4b077c1e539d880c Mon Sep 17 00:00:00 2001 From: micafer Date: Wed, 10 Oct 2018 09:05:19 +0200 Subject: [PATCH 03/18] Set ver num --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 9b22714..64445f0 100644 --- a/setup.py +++ b/setup.py @@ -10,7 +10,7 @@ setup( name="ec3", - version="0.0.1", + version="2.0.0", author="Amanda Calatrava, Eloy Romero, Miguel Caballer", author_email="", description=("Tool to deploy virtual elastic clusters on the cloud."), From 0b1449a71f243767f6454a333c0b5153b3544a81 Mon Sep 17 00:00:00 2001 From: micafer Date: Wed, 10 Oct 2018 09:22:59 +0200 Subject: [PATCH 04/18] Remove raw_input --- ec3 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ec3 b/ec3 index 0a9df66..bc91e30 100755 --- a/ec3 +++ b/ec3 @@ -665,7 +665,7 @@ class CmdLaunch: if not options.restapi: raise Exception("option is not set") if not options.dry_run and options.restapi[0].startswith("http:") and not options.restapi[0].startswith("http://localhost"): CLI.display("WARNING: you are not using a secure connection and this can compromise the secrecy of the passwords and private keys available in the authorization file.") - if not options.yes and raw_input("Continue [y/N]? ")[0:1].lower() != "y": + if not options.yes and input("Continue [y/N]? ")[0:1].lower() != "y": sys.exit(1) except Exception as e: CLI.display("Error in -u/--restapi-url: %s" % str(e), level=logging.ERROR) @@ -1068,7 +1068,7 @@ class CmdDestroy: try: r = ClusterStore.load(options.clustername) CLI.display("WARNING: you are going to delete the infrastructure (including frontend and nodes).") - if not options.yes and raw_input("Continue [y/N]? ")[0:1].lower() != "y": + if not options.yes and input("Continue [y/N]? ")[0:1].lower() != "y": sys.exit(1) new_im_server_url, infrId, _, auth = ClusterStore.get_im_server_infrId_and_vmId_and_auth(r) if options.auth_file: @@ -1641,7 +1641,7 @@ class CmdStop: try: r = ClusterStore.load(options.clustername) CLI.display("WARNING: you are going to stop the infrastructure (including frontend and nodes).") - if not options.yes and raw_input("Continue [y/N]? ")[0:1].lower() != "y": + if not options.yes and input("Continue [y/N]? ")[0:1].lower() != "y": sys.exit(1) cluster_im_server_url, infrId, _, auth = ClusterStore.get_im_server_infrId_and_vmId_and_auth(r) From 472d22773324bab93ffe23083106927f39d2734b Mon Sep 17 00:00:00 2001 From: micafer Date: Mon, 15 Oct 2018 14:52:40 +0200 Subject: [PATCH 05/18] Fix issue: #55 --- IM2/radl/radl_parse.py | 2 +- ec3 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/IM2/radl/radl_parse.py b/IM2/radl/radl_parse.py index bcf07a9..792ef2d 100644 --- a/IM2/radl/radl_parse.py +++ b/IM2/radl/radl_parse.py @@ -204,7 +204,7 @@ def p_configure_sentence(self, t): recipe = "".join(t[5]) if yaml: try: - yaml.safe_load(recipe) + recipe = yaml.safe_load(recipe) except Exception as e: raise RADLParseException("Error parsing YAML: %s" % str(e), line=t.lineno(5)) t[0] = configure(t[2], recipe, line=t.lineno(1)) diff --git a/ec3 b/ec3 index bc91e30..31c5843 100755 --- a/ec3 +++ b/ec3 @@ -195,7 +195,7 @@ def apply_ec3_expressions(r): def rm_ec3_prio(t): t[1].pop("ec3_prio", None); return t[1] for conf in recipes_prio: - conf.recipe = map(rm_ec3_prio, sorted([ (d.get("ec3_prio", i), d) for i, d in enumerate(conf.recipe) ])) + conf.recipe = list(map(rm_ec3_prio, sorted([ (d.get("ec3_prio", i), d) for i, d in enumerate(conf.recipe) ]))) def sanitize(cad): return cad.replace("{", "\\x7b").replace("}", "\\x7d") #return cad.replace("\\", "\\\\").replace("{", "\\x7b").replace("}", "\\x7d") From 0d285017f5073ea5e066ce2f8e2682d7b00aa0ac Mon Sep 17 00:00:00 2001 From: micafer Date: Mon, 15 Oct 2018 15:13:24 +0200 Subject: [PATCH 06/18] Fix radl issue --- IM2/radl/radl_parse.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/IM2/radl/radl_parse.py b/IM2/radl/radl_parse.py index 792ef2d..cdde5e9 100644 --- a/IM2/radl/radl_parse.py +++ b/IM2/radl/radl_parse.py @@ -416,8 +416,8 @@ def d_contextualize_sentence(a, enter, margin, indent): def d_contextualize_item(a, enter, margin, indent): assert isinstance(a, contextualize_item) - return "{margin}system {sys} configure {conf} step {num}".format( - margin=margin, sys=a.system, conf=a.configure, num=" %d" % a.num if a.num else "") + return "{margin}system {sys} configure {conf}{num}".format( + margin=margin, sys=a.system, conf=a.configure, num=" step %d" % a.num if a.num else "") def d_cfeatures_sentence(a, enter, margin, indent): assert isinstance(a, Features) From 522e5f0de3a6bf3bb65eeac17ed1b29682e6b2e7 Mon Sep 17 00:00:00 2001 From: micafer Date: Mon, 15 Oct 2018 16:28:01 +0200 Subject: [PATCH 07/18] Add tests --- ec3.py | 1 + test/test_ec3.py | 244 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 245 insertions(+) create mode 120000 ec3.py create mode 100644 test/test_ec3.py diff --git a/ec3.py b/ec3.py new file mode 120000 index 0000000..105f374 --- /dev/null +++ b/ec3.py @@ -0,0 +1 @@ +ec3 \ No newline at end of file diff --git a/test/test_ec3.py b/test/test_ec3.py new file mode 100644 index 0000000..8ec2270 --- /dev/null +++ b/test/test_ec3.py @@ -0,0 +1,244 @@ +# test_radl - Test for module ``radl``. +# Copyright (C) 2014 - GRyCAP - Universitat Politecnica de Valencia +# +# This program 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. +# +# This program 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 this program. If not, see . + +import sys +import os +import unittest +import logging +from mock import patch, MagicMock +from collections import namedtuple +from StringIO import StringIO + +sys.path.append("..") +sys.path.append(".") + +from ec3 import CmdLaunch, CLI + + +class TestEC3(unittest.TestCase): + + def __init__(self, *args): + unittest.TestCase.__init__(self, *args) + + @patch('ec3.ClusterStore') + @patch('ec3.CLI.display') + def test_launch(self, display, cluster_store): + Options = namedtuple('Options', ['quiet']) + cli_options = Options(quiet=False) + CLI.logger = logging.getLogger('ec3') + CLI.options = cli_options + cluster_store.list.return_value = ["name"] + Options = namedtuple('Options', ['not_store', 'clustername', 'auth_file', 'restapi', 'dry_run', 'templates', + 'add', 'golden_image', 'print_radl', 'json']) + auth_file = [MagicMock()] + auth_file[0].readlines.return_value = ["type = InfrastructureManager; username = user; password = pass"] + options = Options(not_store=False, clustername="name", auth_file=auth_file, restapi='http://server.com:8800', + dry_run=True, templates=['ubuntu-ec2','kubernetes'], add=False, golden_image=False, + print_radl=True, json=False) + with self.assertRaises(SystemExit) as ex1: + CmdLaunch.run(options) + self.assertEquals("1" ,str(ex1.exception)) + + cluster_store.list.return_value = [] + with self.assertRaises(SystemExit) as ex2: + CmdLaunch.run(options) + self.assertEquals("0" ,str(ex2.exception)) + + radl = """system front ( + net_interface.1.dns_name = 'kubeserverpublic' and + disk.0.os.credentials.username = 'ubuntu' and + disk.0.applications contains ( + name = 'ansible.modules.grycap.kubernetes' + ) and + disk.0.applications contains ( + name = 'ansible.modules.grycap.clues' + ) and + disk.0.applications contains ( + name = 'ansible.modules.grycap.im' + ) and + cpu.count >= 2 and + net_interface.1.connection = 'public' and + queue_system = 'kubernetes' and + net_interface.0.dns_name = 'kubeserver' and + instance_type = 't1.micro' and + ec3_templates = 'im,clues2,kubernetes' and + disk.0.image.url = 'aws://us-east-1/ami-30519058' and + auth = 'username = user ; password = pass ; type = InfrastructureManager +' and + net_interface.0.connection = 'private' and + memory.size >= 2048m and + disk.0.os.name = 'linux' and + ec3_templates_cmd = 'ubuntu-ec2 kubernetes' +) + +system wn ( + disk.0.image.url = 'aws://us-east-1/ami-30519058' and + instance_type = 't1.micro' and + ec3_max_instances = 10 and + memory.size >= 2048m and + net_interface.0.connection = 'private' and + disk.0.os.name = 'linux' and + disk.0.os.credentials.username = 'ubuntu' +) + +network public ( + outbound = 'yes' and + outports = '6443/tcp,8800/tcp' +) + +network private ( + +) + +configure front ( +@begin +- tasks: + - iptables: + action: insert + chain: INPUT + destination_port: '{{item|dirname}}' + jump: ACCEPT + protocol: '{{item|basename}}' + when: ansible_os_family == "RedHat" + with_items: '{{OUTPORTS.split('','')}}' + - firewalld: + immediate: true + permanent: true + port: '{{item}}' + state: enabled + ignore_errors: true + when: ansible_os_family == "RedHat" + with_items: '{{OUTPORTS.split('','')}}' + vars: + OUTPORTS: 6443/tcp,8800/tcp +- roles: + - kube_api_server: '{{ IM_NODE_PRIVATE_IP }}' + kube_apiserver_options: + - option: --insecure-port + value: '8080' + kube_server: kubeserver + role: grycap.kubernetes +- roles: + - role: grycap.im +- roles: + - auth: '{{AUTH}}' + clues_queue_system: '{{QUEUE_SYSTEM}}' + max_number_of_nodes: '{{ NNODES }}' + role: grycap.clues + vnode_prefix: wn + vars: + AUTH: 'username = user ; password = pass ; type = InfrastructureManager + + ' + NNODES: '{{ SYSTEMS | selectattr("ec3_max_instances_max", "defined") | sum(attribute="ec3_max_instances_max") + }}' + QUEUE_SYSTEM: kubernetes + SYSTEMS: + - auth: 'username = user ; password = pass ; type = InfrastructureManager + + ' + class: system + cpu.count_max: inf + cpu.count_min: 2 + disk.0.applications: + - name: ansible.modules.grycap.kubernetes + - name: ansible.modules.grycap.clues + - name: ansible.modules.grycap.im + disk.0.image.url: aws://us-east-1/ami-30519058 + disk.0.os.credentials.username: ubuntu + disk.0.os.name: linux + ec3_templates: + - im + - clues2 + - kubernetes + ec3_templates_cmd: ubuntu-ec2 kubernetes + id: front + instance_type: t1.micro + memory.size_max: inf + memory.size_min: 2048 + net_interface.0.connection: + class: network + id: private + reference: true + net_interface.0.dns_name: kubeserver + net_interface.1.connection: + class: network + id: public + reference: true + net_interface.1.dns_name: kubeserverpublic + queue_system: kubernetes + - class: network + id: public + outbound: 'yes' + outports: + - 6443/tcp + - 8800/tcp + - class: network + id: private + - class: system + disk.0.image.url: aws://us-east-1/ami-30519058 + disk.0.os.credentials.username: ubuntu + disk.0.os.name: linux + ec3_max_instances_max: 10 + ec3_max_instances_min: 10 + id: wn + instance_type: t1.micro + memory.size_max: inf + memory.size_min: 2048 + net_interface.0.connection: + class: network + id: private + reference: true + +@end +) + +configure wn ( +@begin +- tasks: + - iptables: + action: insert + chain: INPUT + destination_port: '{{item|dirname}}' + jump: ACCEPT + protocol: '{{item|basename}}' + when: ansible_os_family == "RedHat" + with_items: '{{OUTPORTS.split('','')}}' + - firewalld: + immediate: true + permanent: true + port: '{{item}}' + state: enabled + ignore_errors: true + when: ansible_os_family == "RedHat" + with_items: '{{OUTPORTS.split('','')}}' + vars: + OUTPORTS: 6443/tcp,8800/tcp +- roles: + - kube_server: kubeserver + kube_type_of_node: wn + role: grycap.kubernetes + +@end +) + +deploy front 1 +""" + self.assertEquals(display.call_args_list[1][0][0], radl) + + +if __name__ == "__main__": + unittest.main() From 25221bb0d60a0b2e45d12d21efb6f3d0cfe9df7e Mon Sep 17 00:00:00 2001 From: micafer Date: Mon, 15 Oct 2018 16:43:32 +0200 Subject: [PATCH 08/18] Fix py3 issues: #55 --- ec3 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ec3 b/ec3 index 31c5843..60e35ed 100755 --- a/ec3 +++ b/ec3 @@ -337,8 +337,8 @@ class Display: '\033[1;32m _____ \033[m'] # Make worm fatter if sys.stdout.encoding == "UTF-8": - ANIMATION = map(lambda s: s.replace("_", "▄").replace("/", "▟").replace("\\", "▙") - .replace('"', '¨'), ANIMATION) + ANIMATION = list(map(lambda s: s.replace("_", "▄").replace("/", "▟").replace("\\", "▙") + .replace('"', '¨'), ANIMATION)) @staticmethod def _display_waiting_tty(msg, delay=0.): From b70188b12b6993c51ada25cb6773bdfb29cbcb22 Mon Sep 17 00:00:00 2001 From: micafer Date: Tue, 16 Oct 2018 09:01:08 +0200 Subject: [PATCH 09/18] Improve tests --- test/test_ec3.py | 62 ++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 58 insertions(+), 4 deletions(-) diff --git a/test/test_ec3.py b/test/test_ec3.py index 8ec2270..7633f72 100644 --- a/test/test_ec3.py +++ b/test/test_ec3.py @@ -21,6 +21,7 @@ from mock import patch, MagicMock from collections import namedtuple from StringIO import StringIO +from urlparse import urlparse sys.path.append("..") sys.path.append(".") @@ -33,21 +34,60 @@ class TestEC3(unittest.TestCase): def __init__(self, *args): unittest.TestCase.__init__(self, *args) + def get_response(self, method, url, verify, headers, data=None): + resp = MagicMock() + resp.status_code = 400 + parts = urlparse(url) + url = parts[2] + params = parts[4] + + if method == "GET": + if url == "/infrastructures/infid" or url == "/infrastructures/newinfid": + resp.status_code = 200 + resp.json.return_value = {"uri-list": [{ "uri": "http://server.com/infid/vms/0"}, + { "uri": "http://server.com/infid/vms/1"}]} + elif url == "/infrastructures/infid/state": + resp.status_code = 200 + resp.json.return_value = {"state": {"state": "configured", + "vm_states": {"0": "configured", + "1": "configured"}}} + elif url == "/infrastructures/infid/vms/0": + resp.status_code = 200 + resp.text = "network public (outbound='yes')\n" + resp.text += "system front (net_interface.0.connection = 'public' and net_interface.0.ip = '8.8.8.8')" + elif url == "/infrastructures/infid/data": + resp.status_code = 200 + resp.json.return_value = {"data": "data"} + elif method == "POST": + if url == "/infrastructures": + resp.status_code = 200 + resp.text = 'http://server.com/infid' + elif method == "PUT": + if url == "/infrastructures": + resp.status_code = 200 + resp.text = 'http://server.com/newinfid' + elif method == "DELETE": + if url == "/infrastructures/infid": + resp.status_code = 200 + + return resp + @patch('ec3.ClusterStore') @patch('ec3.CLI.display') - def test_launch(self, display, cluster_store): + @patch('requests.request') + def test_launch(self, requests, display, cluster_store): Options = namedtuple('Options', ['quiet']) cli_options = Options(quiet=False) CLI.logger = logging.getLogger('ec3') CLI.options = cli_options cluster_store.list.return_value = ["name"] Options = namedtuple('Options', ['not_store', 'clustername', 'auth_file', 'restapi', 'dry_run', 'templates', - 'add', 'golden_image', 'print_radl', 'json']) + 'add', 'golden_image', 'print_radl', 'json', 'yes', 'destroy']) auth_file = [MagicMock()] auth_file[0].readlines.return_value = ["type = InfrastructureManager; username = user; password = pass"] - options = Options(not_store=False, clustername="name", auth_file=auth_file, restapi='http://server.com:8800', + options = Options(not_store=False, clustername="name", auth_file=auth_file, restapi=['http://server.com:8800'], dry_run=True, templates=['ubuntu-ec2','kubernetes'], add=False, golden_image=False, - print_radl=True, json=False) + print_radl=True, json=False, yes=True, destroy=False) with self.assertRaises(SystemExit) as ex1: CmdLaunch.run(options) self.assertEquals("1" ,str(ex1.exception)) @@ -239,6 +279,20 @@ def test_launch(self, display, cluster_store): """ self.assertEquals(display.call_args_list[1][0][0], radl) + requests.side_effect = self.get_response + options = Options(not_store=False, clustername="name", auth_file=auth_file, restapi=['http://server.com:8800'], + dry_run=False, templates=['ubuntu-ec2','kubernetes'], add=False, golden_image=False, + print_radl=False, json=False, yes=True, destroy=False) + + with self.assertRaises(SystemExit) as ex2: + CmdLaunch.run(options) + self.assertEquals("0" ,str(ex2.exception)) + + self.assertEquals(display.call_args_list[4][0][0], "Infrastructure successfully created with ID: infid") + self.assertEquals(display.call_args_list[5][0][0], "Front-end configured with IP 8.8.8.8") + self.assertEquals(display.call_args_list[6][0][0], "Transferring infrastructure") + self.assertEquals(display.call_args_list[7][0][0], "Front-end ready!") + if __name__ == "__main__": unittest.main() From cdda6c880c48b53759562e2dfb2bc3019fb8fe77 Mon Sep 17 00:00:00 2001 From: micafer Date: Tue, 16 Oct 2018 09:11:59 +0200 Subject: [PATCH 10/18] Improve tests --- test/test_ec3.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/test/test_ec3.py b/test/test_ec3.py index 7633f72..bab1aed 100644 --- a/test/test_ec3.py +++ b/test/test_ec3.py @@ -20,13 +20,16 @@ import logging from mock import patch, MagicMock from collections import namedtuple -from StringIO import StringIO +try: + from StringIO import StringIO +except ImportError: + from io import StringIO from urlparse import urlparse sys.path.append("..") sys.path.append(".") -from ec3 import CmdLaunch, CLI +from ec3 import CLI, CmdLaunch, CmdList class TestEC3(unittest.TestCase): @@ -72,6 +75,16 @@ def get_response(self, method, url, verify, headers, data=None): return resp + def test_list(self): + Options = namedtuple('Options', ['json']) + options = Options(json=False) + old_stdout = sys.stdout + sys.stdout = StringIO() + CmdList.run(options) + res = sys.stdout.getvalue() + sys.stdout = old_stdout + self.assertEquals(res, " name state IP nodes \n------------------------\n") + @patch('ec3.ClusterStore') @patch('ec3.CLI.display') @patch('requests.request') From 1e7a65beff8a799b57c742049098fb4a15012190 Mon Sep 17 00:00:00 2001 From: micafer Date: Tue, 16 Oct 2018 09:28:05 +0200 Subject: [PATCH 11/18] Improve tests --- test/test_ec3.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/test/test_ec3.py b/test/test_ec3.py index bab1aed..5946685 100644 --- a/test/test_ec3.py +++ b/test/test_ec3.py @@ -15,7 +15,6 @@ # along with this program. If not, see . import sys -import os import unittest import logging from mock import patch, MagicMock @@ -24,7 +23,10 @@ from StringIO import StringIO except ImportError: from io import StringIO -from urlparse import urlparse +try: + from urlparse import urlparse +except ImportError: + from urllib.parse import urlparse sys.path.append("..") sys.path.append(".") @@ -109,7 +111,7 @@ def test_launch(self, requests, display, cluster_store): with self.assertRaises(SystemExit) as ex2: CmdLaunch.run(options) self.assertEquals("0" ,str(ex2.exception)) - + radl = """system front ( net_interface.1.dns_name = 'kubeserverpublic' and disk.0.os.credentials.username = 'ubuntu' and @@ -290,7 +292,9 @@ def test_launch(self, requests, display, cluster_store): deploy front 1 """ - self.assertEquals(display.call_args_list[1][0][0], radl) + + if sys.version_info < (3, 0): + self.assertEquals(display.call_args_list[1][0][0], radl) requests.side_effect = self.get_response options = Options(not_store=False, clustername="name", auth_file=auth_file, restapi=['http://server.com:8800'], From c9f56048fba6201dcb3e91750267d5bf62c5ef95 Mon Sep 17 00:00:00 2001 From: micafer Date: Tue, 16 Oct 2018 09:59:39 +0200 Subject: [PATCH 12/18] Improve tests --- test/test_ec3.py | 36 +++++++++++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/test/test_ec3.py b/test/test_ec3.py index 5946685..dd18eb5 100644 --- a/test/test_ec3.py +++ b/test/test_ec3.py @@ -31,7 +31,8 @@ sys.path.append("..") sys.path.append(".") -from ec3 import CLI, CmdLaunch, CmdList +from IM2.radl.radl import RADL, system, network +from ec3 import CLI, CmdLaunch, CmdList, CmdTemplates class TestEC3(unittest.TestCase): @@ -77,15 +78,28 @@ def get_response(self, method, url, verify, headers, data=None): return resp - def test_list(self): - Options = namedtuple('Options', ['json']) - options = Options(json=False) + @patch('ec3.ClusterStore') + def test_list(self, cluster_store): + cluster_store.list.return_value = ["name"] + radl = RADL() + n = network("public") + n.setValue("outbound", "yes") + s = system("front") + s.setValue("ec3aas.username", "user") + s.setValue("state", "configured") + s.setValue("nodes", "1") + s.setValue("net_interface.0.connection", n) + s.setValue("net_interface.0.ip", "8.8.8.8") + radl.add(s) + cluster_store.load.return_value = radl + Options = namedtuple('Options', ['json', 'refresh', 'username']) + options = Options(json=False, refresh=False, username=['user']) old_stdout = sys.stdout sys.stdout = StringIO() CmdList.run(options) res = sys.stdout.getvalue() sys.stdout = old_stdout - self.assertEquals(res, " name state IP nodes \n------------------------\n") + self.assertEquals(res, " name state IP nodes \n----------------------------------\n name configured 8.8.8.8 1 \n") @patch('ec3.ClusterStore') @patch('ec3.CLI.display') @@ -310,6 +324,18 @@ def test_launch(self, requests, display, cluster_store): self.assertEquals(display.call_args_list[6][0][0], "Transferring infrastructure") self.assertEquals(display.call_args_list[7][0][0], "Front-end ready!") + def test_templates(self): + Options = namedtuple('Options', ['search', 'name', 'json', 'full']) + options = Options(search=[None], name=[None], json=False, full=False) + old_stdout = sys.stdout + sys.stdout = StringIO() + CmdTemplates.run(options) + res = sys.stdout.getvalue() + sys.stdout = old_stdout + self.assertIn(" name kind summary \n", res) + self.assertIn("----------------------------------------------------------------------------------------------------------------------\n", res) + self.assertIn(" galaxy component Galaxy is an open, web-based platform for data intensive biomedical research. \n", res) + if __name__ == "__main__": unittest.main() From e38ead45799a325e63790da6398b340b6c39553d Mon Sep 17 00:00:00 2001 From: micafer Date: Tue, 16 Oct 2018 11:03:05 +0200 Subject: [PATCH 13/18] Improve tests --- test/test_ec3.py | 68 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 66 insertions(+), 2 deletions(-) diff --git a/test/test_ec3.py b/test/test_ec3.py index dd18eb5..f86c42e 100644 --- a/test/test_ec3.py +++ b/test/test_ec3.py @@ -17,7 +17,7 @@ import sys import unittest import logging -from mock import patch, MagicMock +from mock import patch, MagicMock, mock_open from collections import namedtuple try: from StringIO import StringIO @@ -32,8 +32,20 @@ sys.path.append(".") from IM2.radl.radl import RADL, system, network -from ec3 import CLI, CmdLaunch, CmdList, CmdTemplates +from ec3 import ClusterStore, CLI, CmdLaunch, CmdList, CmdTemplates, CmdDestroy +cluster_data = """system front ( + state = 'configured' and + __im_server = 'http://server.com:8800' and + __infrastructure_id = 'infid' and + __vm_id = '0' and + auth = '[{"type": "InfrastructureManager", "username": "user", "password": "pass"}]' + )""" + +if sys.version_info > (3, 0): + open_name = 'builtins.open' +else: + open_name = '__builtin__.open' class TestEC3(unittest.TestCase): @@ -64,6 +76,9 @@ def get_response(self, method, url, verify, headers, data=None): elif url == "/infrastructures/infid/data": resp.status_code = 200 resp.json.return_value = {"data": "data"} + elif url == "/infrastructures/infid/contmsg": + resp.status_code = 200 + resp.text = "contmsg" elif method == "POST": if url == "/infrastructures": resp.status_code = 200 @@ -336,6 +351,55 @@ def test_templates(self): self.assertIn("----------------------------------------------------------------------------------------------------------------------\n", res) self.assertIn(" galaxy component Galaxy is an open, web-based platform for data intensive biomedical research. \n", res) + @patch('requests.request') + @patch('ec3.ClusterStore') + @patch('ec3.CLI.display') + def test_destroy(self, display, cluster_store, requests): + cluster_store.list.return_value = [] + Options = namedtuple('Options', ['restapi', 'json', 'clustername', 'force', 'yes', 'auth_file']) + options = Options(restapi=['http://server.com:8800'], json=False, clustername='name', force=True, yes=True, + auth_file=[]) + with self.assertRaises(SystemExit) as ex: + CmdDestroy.run(options) + self.assertEquals("1" ,str(ex.exception)) + + cluster_store.list.return_value = ["name"] + radl = RADL() + n = network("public") + n.setValue("outbound", "yes") + s = system("front") + s.setValue("ec3aas.username", "user") + s.setValue("state", "configured") + s.setValue("nodes", "1") + s.setValue("net_interface.0.connection", n) + s.setValue("net_interface.0.ip", "8.8.8.8") + radl.add(s) + cluster_store.load.return_value = radl + auth = [{"type": "InfrastructureManager", "username": "user", "password": "pass"}] + cluster_store.get_im_server_infrId_and_vmId_and_auth.return_value = "http://server.com", "infid", "", auth + requests.side_effect = self.get_response + + with self.assertRaises(SystemExit) as ex: + CmdDestroy.run(options) + self.assertEquals("0" ,str(ex.exception)) + + @patch('requests.request') + @patch('os.listdir') + @patch('os.makedirs') + @patch(open_name, new_callable=mock_open, read_data=cluster_data) + def test_cluster_store(self, mo, makedirs, listdirs, requests): + listdirs.return_value = ["cluster1"] + res = ClusterStore.list() + self.assertEqual(["cluster1"], res) + + requests.side_effect = self.get_response + res = ClusterStore.load("cluster1", True) + s = res.get(system("front")) + self.assertEqual(s.getValue("__infrastructure_id"), "infid") + self.assertIn(".ec3/clusters/cluster1", mo.call_args_list[-1][0][0]) + if sys.version_info < (3, 0): + expected_res = """network public (\n outbound = \'yes\'\n)\n\nsystem front (\n net_interface.0.ip = \'8.8.8.8\' and\n __infrastructure_id = \'infid\' and\n auth = \'[{"type": "InfrastructureManager", "username": "user", "password": "pass"}]\' and\n __im_server = \'http://server.com:8800\' and\n net_interface.0.connection = \'public\' and\n nodes = 1 and\n contextualization_output = \'contmsg\'\n)""" + self.assertEqual(mo.mock_calls[-2][1][0], expected_res) if __name__ == "__main__": unittest.main() From dbc3e1d7239751bc375afa4ef08cf069f7ed33e2 Mon Sep 17 00:00:00 2001 From: micafer Date: Tue, 16 Oct 2018 12:22:20 +0200 Subject: [PATCH 14/18] Improve tests --- test/test_ec3.py | 48 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/test/test_ec3.py b/test/test_ec3.py index f86c42e..708f023 100644 --- a/test/test_ec3.py +++ b/test/test_ec3.py @@ -32,7 +32,7 @@ sys.path.append(".") from IM2.radl.radl import RADL, system, network -from ec3 import ClusterStore, CLI, CmdLaunch, CmdList, CmdTemplates, CmdDestroy +from ec3 import ClusterStore, CLI, CmdLaunch, CmdList, CmdTemplates, CmdDestroy, CmdReconfigure, CmdClone cluster_data = """system front ( state = 'configured' and @@ -87,6 +87,9 @@ def get_response(self, method, url, verify, headers, data=None): if url == "/infrastructures": resp.status_code = 200 resp.text = 'http://server.com/newinfid' + elif url == "/infrastructures/infid/reconfigure": + resp.status_code = 200 + resp.text = '' elif method == "DELETE": if url == "/infrastructures/infid": resp.status_code = 200 @@ -401,5 +404,48 @@ def test_cluster_store(self, mo, makedirs, listdirs, requests): expected_res = """network public (\n outbound = \'yes\'\n)\n\nsystem front (\n net_interface.0.ip = \'8.8.8.8\' and\n __infrastructure_id = \'infid\' and\n auth = \'[{"type": "InfrastructureManager", "username": "user", "password": "pass"}]\' and\n __im_server = \'http://server.com:8800\' and\n net_interface.0.connection = \'public\' and\n nodes = 1 and\n contextualization_output = \'contmsg\'\n)""" self.assertEqual(mo.mock_calls[-2][1][0], expected_res) + def test_cli(self): + testargs = ["ec3", "list"] + with patch.object(sys, 'argv', testargs): + old_stdout = sys.stdout + sys.stdout = StringIO() + res = CLI.run([CmdList]) + res = sys.stdout.getvalue() + sys.stdout = old_stdout + self.assertIn(" name state IP nodes \n", res) + + @patch('requests.request') + @patch('ec3.ClusterStore') + @patch('ec3.CLI.display') + def test_reconf(self, display, cluster_store, requests): + cluster_store.list.return_value = [] + Options = namedtuple('Options', ['restapi', 'json', 'clustername', 'reload', 'yes', + 'auth_file', 'add', 'new_template', 'force']) + options = Options(restapi=['http://server.com:8800'], json=False, clustername='name', reload=False, yes=True, + auth_file=[], add=[], new_template=None, force=False) + with self.assertRaises(SystemExit) as ex: + CmdDestroy.run(options) + self.assertEquals("1" ,str(ex.exception)) + + cluster_store.list.return_value = ["name"] + radl = RADL() + n = network("public") + n.setValue("outbound", "yes") + s = system("front") + s.setValue("ec3aas.username", "user") + s.setValue("state", "configured") + s.setValue("nodes", "1") + s.setValue("net_interface.0.connection", n) + s.setValue("net_interface.0.ip", "8.8.8.8") + radl.add(s) + cluster_store.load.return_value = radl + auth = [{"type": "InfrastructureManager", "username": "user", "password": "pass"}] + cluster_store.get_im_server_infrId_and_vmId_and_auth.return_value = "http://server.com", "infid", "0", auth + requests.side_effect = self.get_response + + with self.assertRaises(SystemExit) as ex: + CmdReconfigure.run(options) + self.assertEquals("0" ,str(ex.exception)) + if __name__ == "__main__": unittest.main() From 0a252c7f2ba440f932dac66563721a7dd4b87bb5 Mon Sep 17 00:00:00 2001 From: micafer Date: Tue, 16 Oct 2018 12:29:12 +0200 Subject: [PATCH 15/18] Improve tests --- test/test_ec3.py | 38 ++++++++++++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/test/test_ec3.py b/test/test_ec3.py index 708f023..87a72ca 100644 --- a/test/test_ec3.py +++ b/test/test_ec3.py @@ -79,6 +79,10 @@ def get_response(self, method, url, verify, headers, data=None): elif url == "/infrastructures/infid/contmsg": resp.status_code = 200 resp.text = "contmsg" + elif url == "/infrastructures/infid/radl": + resp.status_code = 200 + resp.text = "network public (outbound='yes')\n" + resp.text += "system front (net_interface.0.connection = 'public' and net_interface.0.ip = '8.8.8.8')" elif method == "POST": if url == "/infrastructures": resp.status_code = 200 @@ -418,14 +422,40 @@ def test_cli(self): @patch('ec3.ClusterStore') @patch('ec3.CLI.display') def test_reconf(self, display, cluster_store, requests): - cluster_store.list.return_value = [] Options = namedtuple('Options', ['restapi', 'json', 'clustername', 'reload', 'yes', 'auth_file', 'add', 'new_template', 'force']) options = Options(restapi=['http://server.com:8800'], json=False, clustername='name', reload=False, yes=True, auth_file=[], add=[], new_template=None, force=False) + + cluster_store.list.return_value = ["name"] + radl = RADL() + n = network("public") + n.setValue("outbound", "yes") + s = system("front") + s.setValue("ec3aas.username", "user") + s.setValue("state", "configured") + s.setValue("nodes", "1") + s.setValue("net_interface.0.connection", n) + s.setValue("net_interface.0.ip", "8.8.8.8") + radl.add(s) + cluster_store.load.return_value = radl + auth = [{"type": "InfrastructureManager", "username": "user", "password": "pass"}] + cluster_store.get_im_server_infrId_and_vmId_and_auth.return_value = "http://server.com", "infid", "0", auth + requests.side_effect = self.get_response + with self.assertRaises(SystemExit) as ex: - CmdDestroy.run(options) - self.assertEquals("1" ,str(ex.exception)) + CmdReconfigure.run(options) + self.assertEquals("0" ,str(ex.exception)) + + @patch('requests.request') + @patch('ec3.ClusterStore') + @patch('ec3.CLI.display') + def test_clone(self, display, cluster_store, requests): + auth_file = [MagicMock()] + auth_file[0].readlines.return_value = ["type = InfrastructureManager; username = user; password = pass"] + Options = namedtuple('Options', ['restapi', 'json', 'clustername', 'destination', 'auth_file', 'eliminate']) + options = Options(restapi=['http://server.com:8800'], json=False, clustername='name', + destination=["http://server2.com:8800"], auth_file=auth_file, eliminate=True) cluster_store.list.return_value = ["name"] radl = RADL() @@ -444,7 +474,7 @@ def test_reconf(self, display, cluster_store, requests): requests.side_effect = self.get_response with self.assertRaises(SystemExit) as ex: - CmdReconfigure.run(options) + CmdClone.run(options) self.assertEquals("0" ,str(ex.exception)) if __name__ == "__main__": From 0e76ce4d0cd7ab1861f1463790ee0508ade28a5b Mon Sep 17 00:00:00 2001 From: micafer Date: Tue, 16 Oct 2018 12:31:36 +0200 Subject: [PATCH 16/18] Improve tests --- test/test_ec3.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test/test_ec3.py b/test/test_ec3.py index 87a72ca..4940481 100644 --- a/test/test_ec3.py +++ b/test/test_ec3.py @@ -416,7 +416,10 @@ def test_cli(self): res = CLI.run([CmdList]) res = sys.stdout.getvalue() sys.stdout = old_stdout - self.assertIn(" name state IP nodes \n", res) + self.assertIn(" name ", res) + self.assertIn(" state ", res) + self.assertIn(" IP ", res) + self.assertIn(" nodes \n", res) @patch('requests.request') @patch('ec3.ClusterStore') From 47fac477fc60fc2b7beec0eef08cd14f8f2ff0a9 Mon Sep 17 00:00:00 2001 From: micafer Date: Tue, 16 Oct 2018 12:46:37 +0200 Subject: [PATCH 17/18] Improve tests --- test/test_ec3.py | 67 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 66 insertions(+), 1 deletion(-) diff --git a/test/test_ec3.py b/test/test_ec3.py index 4940481..8b4202e 100644 --- a/test/test_ec3.py +++ b/test/test_ec3.py @@ -32,7 +32,7 @@ sys.path.append(".") from IM2.radl.radl import RADL, system, network -from ec3 import ClusterStore, CLI, CmdLaunch, CmdList, CmdTemplates, CmdDestroy, CmdReconfigure, CmdClone +from ec3 import ClusterStore, CLI, CmdLaunch, CmdList, CmdTemplates, CmdDestroy, CmdReconfigure, CmdClone, CmdStop, CmdRestart cluster_data = """system front ( state = 'configured' and @@ -94,6 +94,12 @@ def get_response(self, method, url, verify, headers, data=None): elif url == "/infrastructures/infid/reconfigure": resp.status_code = 200 resp.text = '' + elif url == "/infrastructures/newinfid/stop": + resp.status_code = 200 + resp.text = '' + elif url == "/infrastructures/infid/start": + resp.status_code = 200 + resp.text = '' elif method == "DELETE": if url == "/infrastructures/infid": resp.status_code = 200 @@ -480,5 +486,64 @@ def test_clone(self, display, cluster_store, requests): CmdClone.run(options) self.assertEquals("0" ,str(ex.exception)) + @patch('requests.request') + @patch('ec3.ClusterStore') + @patch('ec3.CLI.display') + def test_stop(self, display, cluster_store, requests): + auth_file = [MagicMock()] + auth_file[0].readlines.return_value = ["type = InfrastructureManager; username = user; password = pass"] + Options = namedtuple('Options', ['restapi', 'json', 'clustername', 'auth_file', 'yes']) + options = Options(restapi=['http://server.com:8800'], json=False, clustername='name', auth_file=auth_file, yes=True) + + cluster_store.list.return_value = ["name"] + radl = RADL() + n = network("public") + n.setValue("outbound", "yes") + s = system("front") + s.setValue("ec3aas.username", "user") + s.setValue("state", "configured") + s.setValue("nodes", "1") + s.setValue("net_interface.0.connection", n) + s.setValue("net_interface.0.ip", "8.8.8.8") + radl.add(s) + cluster_store.load.return_value = radl + auth = [{"type": "InfrastructureManager", "username": "user", "password": "pass"}] + cluster_store.get_im_server_infrId_and_vmId_and_auth.return_value = "http://server.com", "infid", "0", auth + requests.side_effect = self.get_response + + with self.assertRaises(SystemExit) as ex: + CmdStop.run(options) + self.assertEquals("0" ,str(ex.exception)) + + @patch('requests.request') + @patch('ec3.ClusterStore') + @patch('ec3.CLI.display') + def test_restart(self, display, cluster_store, requests): + auth_file = [MagicMock()] + auth_file[0].readlines.return_value = ["type = InfrastructureManager; username = user; password = pass"] + Options = namedtuple('Options', ['restapi', 'json', 'clustername', 'auth_file', 'yes']) + options = Options(restapi=['http://server.com:8800'], json=False, clustername='name', auth_file=auth_file, yes=True) + + cluster_store.list.return_value = ["name"] + radl = RADL() + n = network("public") + n.setValue("outbound", "yes") + s = system("front") + s.setValue("ec3aas.username", "user") + s.setValue("state", "configured") + s.setValue("nodes", "1") + s.setValue("net_interface.0.connection", n) + s.setValue("net_interface.0.ip", "8.8.8.8") + radl.add(s) + cluster_store.load.return_value = radl + auth = [{"type": "InfrastructureManager", "username": "user", "password": "pass"}] + cluster_store.get_im_server_infrId_and_vmId_and_auth.return_value = "http://server.com", "infid", "0", auth + requests.side_effect = self.get_response + + with self.assertRaises(SystemExit) as ex: + CmdRestart.run(options) + self.assertEquals("0" ,str(ex.exception)) + + if __name__ == "__main__": unittest.main() From 53e177a105142eed3ed925788c810461a026495c Mon Sep 17 00:00:00 2001 From: micafer Date: Tue, 16 Oct 2018 13:12:03 +0200 Subject: [PATCH 18/18] Improve tests --- test/test_ec3.py | 117 +++++++++++++++++++++++------------------------ 1 file changed, 56 insertions(+), 61 deletions(-) diff --git a/test/test_ec3.py b/test/test_ec3.py index 8b4202e..222d4e9 100644 --- a/test/test_ec3.py +++ b/test/test_ec3.py @@ -32,7 +32,7 @@ sys.path.append(".") from IM2.radl.radl import RADL, system, network -from ec3 import ClusterStore, CLI, CmdLaunch, CmdList, CmdTemplates, CmdDestroy, CmdReconfigure, CmdClone, CmdStop, CmdRestart +from ec3 import ClusterStore, CLI, CmdLaunch, CmdList, CmdTemplates, CmdDestroy, CmdReconfigure, CmdClone, CmdStop, CmdRestart, CmdSsh cluster_data = """system front ( state = 'configured' and @@ -52,6 +52,21 @@ class TestEC3(unittest.TestCase): def __init__(self, *args): unittest.TestCase.__init__(self, *args) + def gen_radl(self): + radl = RADL() + n = network("public") + n.setValue("outbound", "yes") + s = system("front") + s.setValue("ec3aas.username", "user") + s.setValue("state", "configured") + s.setValue("nodes", "1") + s.setValue("net_interface.0.connection", n) + s.setValue("net_interface.0.ip", "8.8.8.8") + s.setValue("disk.0.os.credentials.password", "pass") + s.setValue("disk.0.os.credentials.username", "user") + radl.add(s) + return radl, s + def get_response(self, method, url, verify, headers, data=None): resp = MagicMock() resp.status_code = 400 @@ -109,16 +124,7 @@ def get_response(self, method, url, verify, headers, data=None): @patch('ec3.ClusterStore') def test_list(self, cluster_store): cluster_store.list.return_value = ["name"] - radl = RADL() - n = network("public") - n.setValue("outbound", "yes") - s = system("front") - s.setValue("ec3aas.username", "user") - s.setValue("state", "configured") - s.setValue("nodes", "1") - s.setValue("net_interface.0.connection", n) - s.setValue("net_interface.0.ip", "8.8.8.8") - radl.add(s) + radl, _ = self.gen_radl() cluster_store.load.return_value = radl Options = namedtuple('Options', ['json', 'refresh', 'username']) options = Options(json=False, refresh=False, username=['user']) @@ -377,16 +383,7 @@ def test_destroy(self, display, cluster_store, requests): self.assertEquals("1" ,str(ex.exception)) cluster_store.list.return_value = ["name"] - radl = RADL() - n = network("public") - n.setValue("outbound", "yes") - s = system("front") - s.setValue("ec3aas.username", "user") - s.setValue("state", "configured") - s.setValue("nodes", "1") - s.setValue("net_interface.0.connection", n) - s.setValue("net_interface.0.ip", "8.8.8.8") - radl.add(s) + radl, _ = self.gen_radl() cluster_store.load.return_value = radl auth = [{"type": "InfrastructureManager", "username": "user", "password": "pass"}] cluster_store.get_im_server_infrId_and_vmId_and_auth.return_value = "http://server.com", "infid", "", auth @@ -437,16 +434,7 @@ def test_reconf(self, display, cluster_store, requests): auth_file=[], add=[], new_template=None, force=False) cluster_store.list.return_value = ["name"] - radl = RADL() - n = network("public") - n.setValue("outbound", "yes") - s = system("front") - s.setValue("ec3aas.username", "user") - s.setValue("state", "configured") - s.setValue("nodes", "1") - s.setValue("net_interface.0.connection", n) - s.setValue("net_interface.0.ip", "8.8.8.8") - radl.add(s) + radl, _ = self.gen_radl() cluster_store.load.return_value = radl auth = [{"type": "InfrastructureManager", "username": "user", "password": "pass"}] cluster_store.get_im_server_infrId_and_vmId_and_auth.return_value = "http://server.com", "infid", "0", auth @@ -467,16 +455,7 @@ def test_clone(self, display, cluster_store, requests): destination=["http://server2.com:8800"], auth_file=auth_file, eliminate=True) cluster_store.list.return_value = ["name"] - radl = RADL() - n = network("public") - n.setValue("outbound", "yes") - s = system("front") - s.setValue("ec3aas.username", "user") - s.setValue("state", "configured") - s.setValue("nodes", "1") - s.setValue("net_interface.0.connection", n) - s.setValue("net_interface.0.ip", "8.8.8.8") - radl.add(s) + radl, _ = self.gen_radl() cluster_store.load.return_value = radl auth = [{"type": "InfrastructureManager", "username": "user", "password": "pass"}] cluster_store.get_im_server_infrId_and_vmId_and_auth.return_value = "http://server.com", "infid", "0", auth @@ -496,16 +475,7 @@ def test_stop(self, display, cluster_store, requests): options = Options(restapi=['http://server.com:8800'], json=False, clustername='name', auth_file=auth_file, yes=True) cluster_store.list.return_value = ["name"] - radl = RADL() - n = network("public") - n.setValue("outbound", "yes") - s = system("front") - s.setValue("ec3aas.username", "user") - s.setValue("state", "configured") - s.setValue("nodes", "1") - s.setValue("net_interface.0.connection", n) - s.setValue("net_interface.0.ip", "8.8.8.8") - radl.add(s) + radl, _ = self.gen_radl() cluster_store.load.return_value = radl auth = [{"type": "InfrastructureManager", "username": "user", "password": "pass"}] cluster_store.get_im_server_infrId_and_vmId_and_auth.return_value = "http://server.com", "infid", "0", auth @@ -525,16 +495,7 @@ def test_restart(self, display, cluster_store, requests): options = Options(restapi=['http://server.com:8800'], json=False, clustername='name', auth_file=auth_file, yes=True) cluster_store.list.return_value = ["name"] - radl = RADL() - n = network("public") - n.setValue("outbound", "yes") - s = system("front") - s.setValue("ec3aas.username", "user") - s.setValue("state", "configured") - s.setValue("nodes", "1") - s.setValue("net_interface.0.connection", n) - s.setValue("net_interface.0.ip", "8.8.8.8") - radl.add(s) + radl, _ = self.gen_radl() cluster_store.load.return_value = radl auth = [{"type": "InfrastructureManager", "username": "user", "password": "pass"}] cluster_store.get_im_server_infrId_and_vmId_and_auth.return_value = "http://server.com", "infid", "0", auth @@ -544,6 +505,40 @@ def test_restart(self, display, cluster_store, requests): CmdRestart.run(options) self.assertEquals("0" ,str(ex.exception)) + @patch('ec3.ClusterStore') + @patch('ec3.CLI.display') + def test_ssh(self, display, cluster_store): + Options = namedtuple('Options', ['json', 'clustername', 'show_only']) + options = Options(json=False, clustername='name', show_only=True) + + cluster_store.list.return_value = ["name"] + radl, s = self.gen_radl() + cluster_store.load.return_value = radl + auth = [{"type": "InfrastructureManager", "username": "user", "password": "pass"}] + cluster_store.get_im_server_infrId_and_vmId_and_auth.return_value = "http://server.com", "infid", "0", auth + + with self.assertRaises(SystemExit) as ex: + CmdSsh.run(options) + self.assertEquals("0" ,str(ex.exception)) + + self.assertEquals(display.call_args_list[0][0][0], "sshpass -ppass ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no user@8.8.8.8 -p 22") + + s.setValue("disk.0.os.credentials.private_key", "priv_key") + + with self.assertRaises(SystemExit) as ex: + CmdSsh.run(options) + self.assertEquals("0" ,str(ex.exception)) + + self.assertIn("ssh -i /tmp/tmp", display.call_args_list[1][0][0]) + self.assertIn(" -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no user@8.8.8.8 -p 22", display.call_args_list[1][0][0]) + + if sys.version_info > (3, 0): + priv_key_file = display.call_args_list[1][0][0][7:23] + else: + priv_key_file = display.call_args_list[1][0][0][7:21] + with open(priv_key_file, "r") as f: + self.assertEquals(f.read(), "priv_key") + if __name__ == "__main__": unittest.main()