diff --git a/IM/connectors/Azure.py b/IM/connectors/Azure.py index 4e69f2dd7..bbd138308 100644 --- a/IM/connectors/Azure.py +++ b/IM/connectors/Azure.py @@ -193,6 +193,12 @@ def gen_input_endpoints(self, radl): # SSH port must be allways available res = """ + + 3389 + RDP + 3389 + TCP + 5986 WinRM @@ -215,7 +221,7 @@ def gen_input_endpoints(self, radl): outports = public_net.getOutPorts() if outports: for remote_port, remote_protocol, local_port, local_protocol in outports: - if local_port != 22 and local_port != 5986: + if local_port != 22 and local_port != 5986 and local_port != 3389: protocol = remote_protocol if remote_protocol != local_protocol: self.logger.warn( @@ -334,8 +340,9 @@ def get_azure_vm_create_xml(self, vm, storage_account, radl, num, auth_data): hostname = "AzureNode" + str(num) SourceImageName = url[1] - MediaLink = "https://%s.blob.core.windows.net/vhds/%s.vhd" % ( - storage_account, SourceImageName) + MediaLink = "https://%s.blob.core.windows.net/vhds/%s" % (storage_account, SourceImageName) + if not MediaLink.endswith('.vhd'): + MediaLink = MediaLink + '.vhd' instance_type = self.get_instance_type(system, auth_data) DataVirtualHardDisks = self.gen_data_disks(system, storage_account) @@ -531,9 +538,11 @@ def wait_operation_status(self, request_id, auth_data, delay=2, timeout=90): self.logger.exception("Error waiting the operation") return False - def get_storage_name(self, subscription_id): - res = subscription_id.replace("-", "")[:24] - return res + def get_storage_name(self, subscription_id, region=None): + if not region: + region = self.DEFAULT_LOCATION + res = region.replace(" ", "").lower() + subscription_id.replace("-", "") + return res[:24] def create_storage_account(self, storage_account, auth_data, region, timeout=120): """ @@ -656,16 +665,14 @@ def launch(self, inf, radl, requested_radl, num_vm, auth_data): i = 0 while i < num_vm: try: - conn, subscription_id = self.get_connection_and_subscription_id( - auth_data) + conn, subscription_id = self.get_connection_and_subscription_id(auth_data) # Create storage account - storage_account_name = self.get_storage_name(subscription_id) - storage_account = self.get_storage_account( - storage_account_name, auth_data) + storage_account_name = self.get_storage_name(subscription_id, region) + storage_account = self.get_storage_account(storage_account_name, auth_data) if not storage_account: - storage_account_name, error_msg = self.create_storage_account( - self.get_storage_name(subscription_id), auth_data, region) + storage_account_name, error_msg = self.create_storage_account(storage_account_name, + auth_data, region) if not storage_account_name: res.append((False, error_msg)) break @@ -683,8 +690,7 @@ def launch(self, inf, radl, requested_radl, num_vm, auth_data): region = storage_account.GeoPrimaryRegion # and the service - service_name, error_msg = self.create_service( - auth_data, region) + service_name, error_msg = self.create_service(auth_data, region) if service_name is None: res.append((False, error_msg)) break @@ -692,8 +698,7 @@ def launch(self, inf, radl, requested_radl, num_vm, auth_data): self.logger.debug("Creating the VM with id: " + service_name) # Create the VM to get the nodename - vm = VirtualMachine(inf, service_name, - self.cloud, radl, requested_radl, self) + vm = VirtualMachine(inf, service_name, self.cloud, radl, requested_radl, self) vm.info.systems[0].setValue('instance_id', str(vm.id)) # Generate the XML to create the VM diff --git a/changelog b/changelog index 7ee4a8a6e..d06b5f4a0 100644 --- a/changelog +++ b/changelog @@ -218,3 +218,4 @@ IM 1.4.6 * Add ANSIBLE_INSTALL_TIMEOUT var * Create user with cloudinit in OpenStack connector * Improve error msg in ssh wait + * Bugfixes in Azure connector diff --git a/doc/source/manual.rst b/doc/source/manual.rst index 22a243a83..d453205db 100644 --- a/doc/source/manual.rst +++ b/doc/source/manual.rst @@ -25,19 +25,8 @@ IM needs at least Python 2.6 to run, as well as the next libraries: Also, IM uses `Ansible `_ (1.4.2 or later) to configure the infrastructure nodes. The current recommended version is 1.9.4 untill the 2.X versions become stable. -These components are usually available from the distribution repositories. To -install them in Debian and Ubuntu based distributions, do:: - - $ apt-get install python-ply python-paramiko python-yaml python-soappy python-netaddr python-boto python-libcloud ansible - -In Red Hat based distributions (RHEL, CentOS, Amazon Linux, Oracle Linux, -Fedora, etc.), do:: - - $ yum install python-ply python-paramiko python-netaddr PyYAML SOAPpy python-boto python-libcloud ansible +These components are usually available from the distribution repositories. -**WARNING: In some GNU/Linux distributions (RHEL 6 or equivalents) you must NOT install -the packages 'python-paramiko' and 'python-crypto' with yum. You MUST use pip to install them** - Finally, check the next values in the Ansible configuration file :file:`ansible.cfg`, (usually found in :file:`/etc/ansible`):: @@ -78,12 +67,48 @@ Optional Packages with SSL certificates (see :confval:`REST_SSL`). The Debian package for CherryPy is named ``python-cherrypy3``. pyOpenSSL can be installed using pip. +* `MySQL `_ is needed if the IM data is going to be stored in DB. + (see DATA_DB configuration variable. Installation ------------ -From RPM packages (RH6 and RH7) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +From Pip (Recommended option) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +**WARNING: In some linux old distributions (REL 6 or equivalents) you must unistall +the package python-crypto and python-paramiko before installing the IM with pip.**:: + + $ rpm -e python-crypto python-paramiko --nodeps + +First you need to install pip tool and some packages needed to compile some of the IM requirements. +To install them in Debian and Ubuntu based distributions, do:: + + $ apt update + $ apt install gcc python-dev libffi-dev libssl-dev python-pip sshpass + +In Red Hat based distributions (RHEL, CentOS, Amazon Linux, Oracle Linux, +Fedora, etc.), do:: + + $ yum install epel-release + $ yum install which gcc python-devel libffi-devel openssl-devel python-pip sshpass + +For some problems with the dependencies of the apache-libcloud package in some systems (as ubuntu 14.04 or CentOS 6) +this package has to be installed manually:: + + $ pip install backports-ssl_match_hostname + +Then you only have to call the install command of the pip tool with the IM package:: + + $ pip install IM + +Pip will also install the, non installed, pre-requisites needed. So Ansible 1.4.2 or later will +be installed in the system. + +You must also remember to modify the ansible.cfg file setting as specified in the +REQUISITES section. + +From RPM packages (RH7) +^^^^^^^^^^^^^^^^^^^^^^^ Download the RPM package from `GitHub `_. Also remember to download the RPM of the RADL package also from `GitHub `_. You must have the epel repository enabled:: @@ -117,25 +142,6 @@ Put all the .deb files in the same directory and do:: $ sudo dpkg -i *.deb $ sudo apt install -f -y -From Pip -^^^^^^^^ - -**WARNING: In some linux distributions (REL 6 or equivalents) you must unistall -the packages python-paramiko and python-crypto before installing the IM with pip.** - -You only have to call the install command of the pip tool with the IM package:: - - $ pip install IM - -Pip will install all the pre-requisites needed. So Ansible 1.4.2 or later will -be installed in the system. Yo will also need to install the sshpass command -('sshpass' package in main distributions). In some cases it will need to have installed -the GCC compiler and the python developer libraries ('python-dev' or 'python-devel' -packages in main distributions). - -You must also remember to modify the ansible.cfg file setting as specified in the -REQUISITES section. - From Source ^^^^^^^^^^^ diff --git a/packages/generate_rpm.sh b/packages/generate_rpm.sh index 679056a12..80048fa52 100755 --- a/packages/generate_rpm.sh +++ b/packages/generate_rpm.sh @@ -2,6 +2,6 @@ yum -y install rpm-build python-setuptools echo "%_unpackaged_files_terminate_build 0" > ~/.rpmmacros -python setup.py bdist_rpm --release="$1" --requires="tosca-parser, RADL, ansible, python-paramiko, PyYAML, SOAPpy, python-boto >= 2.29, python-libcloud, python-bottle, python-netaddr, python-scp" +python setup.py bdist_rpm --release="$1" --requires="which, tosca-parser, RADL, ansible, python-paramiko, PyYAML, SOAPpy, python-boto >= 2.29, python-libcloud, python-bottle, python-netaddr, python-scp" mkdir dist_pkg -cp dist/*.noarch.rpm dist_pkg \ No newline at end of file +cp dist/*.noarch.rpm dist_pkg diff --git a/test/files/inventory b/test/files/inventory new file mode 100644 index 000000000..2fbb50c4a --- /dev/null +++ b/test/files/inventory @@ -0,0 +1 @@ +localhost diff --git a/test/files/play.yaml b/test/files/play.yaml new file mode 100644 index 000000000..fe6665c75 --- /dev/null +++ b/test/files/play.yaml @@ -0,0 +1,27 @@ +- hosts: localhost + connection: local + tasks: + - command: /bin/true + - command: /bin/true {{item}} + with_items: + - OK1 + - OK2 + - debug: msg=OK + - debug: msg=OK + when: 0 == 1 + - debug: msg={{item}} + with_items: + - OK1 + - OK2 + - debug: msg={{item}} + with_items: + - OK1 + - OK2 + when: 0 == 1 + - fail: msg={{item}} + with_items: + - Error1 + - Error2 + ignore_errors: yes + - fail: msg=Error + ignore_errors: yes \ No newline at end of file diff --git a/test/integration/TestIM.py b/test/integration/TestIM.py index 15481b119..95430f9b3 100755 --- a/test/integration/TestIM.py +++ b/test/integration/TestIM.py @@ -561,7 +561,7 @@ def test_75_destroy(self): def test_80_create_ansible_host(self): """ - Test the CreateInfrastructure IM function + Test the CreateInfrastructure IM function using an external ansible host """ ansible_radl = """ network publicnet (outbound = 'yes') diff --git a/test/unit/test_ansible.py b/test/unit/test_ansible.py new file mode 100755 index 000000000..853761456 --- /dev/null +++ b/test/unit/test_ansible.py @@ -0,0 +1,50 @@ +#! /usr/bin/env python +# +# IM - Infrastructure Manager +# Copyright (C) 2011 - 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 unittest +import os +from multiprocessing import Queue +from StringIO import StringIO +import time + +from IM.ansible.ansible_launcher import AnsibleThread +from mock import patch, MagicMock + + +class TestAnsible(unittest.TestCase): + """ + Class to test the Ansible related classes + """ + + def test_ansible_thread(self): + result = Queue() + tests_path = os.path.dirname(os.path.abspath(__file__)) + play_file_path = os.path.join(tests_path, "../files/play.yaml") + inventory = os.path.join(tests_path, "../files/inventory") + ansible_process = AnsibleThread(result, StringIO(), play_file_path, None, 1, None, + "password", 1, inventory, "username") + ansible_process.run() + + _, (return_code, _), output = result.get() + self.assertEqual(return_code, 0) + self.assertIn("failed=0", output.getvalue()) + self.assertIn("changed=2", output.getvalue()) + print output.getvalue() + +if __name__ == '__main__': + unittest.main()