Added support for using '~' for SSH key location. 27/37027/1
authorspisarski <s.pisarski@cablelabs.com>
Thu, 6 Jul 2017 17:58:52 +0000 (11:58 -0600)
committerspisarski <s.pisarski@cablelabs.com>
Thu, 6 Jul 2017 17:58:52 +0000 (11:58 -0600)
While testing changes, fixed problems found with querying for
floating IPs which also required adding network data to the
VMInst domain object.

JIRA: SNAPS-85

Change-Id: I0ecf3a6885ce84fe14c4a6db09269c56dc0ad9fc
Signed-off-by: spisarski <s.pisarski@cablelabs.com>
examples/simple/deploy-simple.yaml
snaps/domain/test/vm_inst_tests.py
snaps/domain/vm_inst.py
snaps/openstack/create_instance.py
snaps/openstack/utils/nova_utils.py
snaps/provisioning/ansible_utils.py

index c58f135..3124210 100644 (file)
@@ -44,15 +44,15 @@ openstack:
           - simple-subnet
   keypairs:
     - keypair:
-        name: simple
-        public_filepath: /tmp/simple.pub
-        private_filepath: /tmp/simple
+        name: simple-kp
+        public_filepath: /tmp/simple-kp.pub
+        private_filepath: /tmp/simple-kp
   instances:
     - instance:
         name: simple-1
         flavor: m1.small
         imageName: Ubuntu14
-        keypair_name: simple
+        keypair_name: simple-kp
         userdata: "#cloud-config\npassword: cable123\nchpasswd: { expire: False }\nsshr_pwauth: True"
         ports:
           - port:
index e722a06..c3de8ba 100644 (file)
@@ -23,14 +23,16 @@ class VmInstDomainObjectTests(unittest.TestCase):
     """
 
     def test_construction_positional(self):
-        vm_inst = VmInst('name', 'id')
+        vm_inst = VmInst('name', 'id', dict())
         self.assertEqual('name', vm_inst.name)
         self.assertEqual('id', vm_inst.id)
+        self.assertEqual(dict(), vm_inst.networks)
 
     def test_construction_named(self):
-        vm_inst = VmInst(inst_id='id', name='name')
+        vm_inst = VmInst(networks=dict(), inst_id='id', name='name')
         self.assertEqual('name', vm_inst.name)
         self.assertEqual('id', vm_inst.id)
+        self.assertEqual(dict(), vm_inst.networks)
 
 
 class FloatingIpDomainObjectTests(unittest.TestCase):
index 0e12d14..ae01cf0 100644 (file)
@@ -19,14 +19,17 @@ class VmInst:
     SNAPS domain object for Images. Should contain attributes that
     are shared amongst cloud providers
     """
-    def __init__(self, name, inst_id):
+    def __init__(self, name, inst_id, networks):
         """
         Constructor
         :param name: the image's name
         :param inst_id: the instance's id
+        :param networks: dict of networks where the key is the subnet name and
+                         value is a list of associated IPs
         """
         self.name = name
         self.id = inst_id
+        self.networks = networks
 
 
 class FloatingIp:
index 68c421d..c8e668f 100644 (file)
@@ -102,12 +102,13 @@ class OpenStackVmInstance:
                 logger.info(
                     'Found existing machine with name - %s',
                     self.instance_settings.name)
-                fips = neutron_utils.get_floating_ips(self.__nova)
+                fips = neutron_utils.get_floating_ips(self.__neutron)
                 for fip in fips:
-                    if fip.instance_id == server.id:
-                        self.__floating_ips.append(fip)
-                        # TODO - Determine a means to associate to the FIP
-                        # configuration and add to FIP map
+                    for subnet_name, ips in server.networks.items():
+                        if fip.ip in ips:
+                            self.__floating_ips.append(fip)
+                            # TODO - Determine a means to associate to the FIP
+                            # configuration and add to FIP map
 
     def __create_vm(self, block=False):
         """
index 90eec13..bab0533 100644 (file)
@@ -93,7 +93,8 @@ def create_server(nova, neutron, glance, instance_settings, image_settings,
                 'availability_zone':
                     instance_settings.availability_zone}
         server = nova.servers.create(**args)
-        return VmInst(name=server.name, inst_id=server.id)
+        return VmInst(name=server.name, inst_id=server.id,
+                      networks=server.networks)
     else:
         raise Exception(
             'Cannot create instance, image cannot be located with name %s',
@@ -110,7 +111,8 @@ def get_servers_by_name(nova, name):
     out = list()
     servers = nova.servers.list(search_opts={'name': name})
     for server in servers:
-        out.append(VmInst(name=server.name, inst_id=server.id))
+        out.append(VmInst(name=server.name, inst_id=server.id,
+                          networks=server.networks))
     return out
 
 
@@ -132,7 +134,8 @@ def get_latest_server_object(nova, server):
     :return: the list of servers or None if not found
     """
     server = get_latest_server_os_object(nova, server)
-    return VmInst(name=server.name, inst_id=server.id)
+    return VmInst(name=server.name, inst_id=server.id,
+                  networks=server.networks)
 
 
 def create_keys(key_size=2048):
@@ -166,30 +169,35 @@ def save_keys_to_files(keys=None, pub_file_path=None, priv_file_path=None):
     """
     if keys:
         if pub_file_path:
-            pub_dir = os.path.dirname(pub_file_path)
+            # To support '~'
+            pub_expand_file = os.path.expanduser(pub_file_path)
+            pub_dir = os.path.dirname(pub_expand_file)
+
             if not os.path.isdir(pub_dir):
                 os.mkdir(pub_dir)
-            public_handle = open(pub_file_path, 'wb')
+            public_handle = open(pub_expand_file, 'wb')
             public_bytes = keys.public_key().public_bytes(
                 serialization.Encoding.OpenSSH,
                 serialization.PublicFormat.OpenSSH)
             public_handle.write(public_bytes)
             public_handle.close()
-            os.chmod(pub_file_path, 0o400)
-            logger.info("Saved public key to - " + pub_file_path)
+            os.chmod(pub_expand_file, 0o400)
+            logger.info("Saved public key to - " + pub_expand_file)
         if priv_file_path:
-            priv_dir = os.path.dirname(priv_file_path)
+            # To support '~'
+            priv_expand_file = os.path.expanduser(priv_file_path)
+            priv_dir = os.path.dirname(priv_expand_file)
             if not os.path.isdir(priv_dir):
                 os.mkdir(priv_dir)
-            private_handle = open(priv_file_path, 'wb')
+            private_handle = open(priv_expand_file, 'wb')
             private_handle.write(
                 keys.private_bytes(
                     encoding=serialization.Encoding.PEM,
                     format=serialization.PrivateFormat.TraditionalOpenSSL,
                     encryption_algorithm=serialization.NoEncryption()))
             private_handle.close()
-            os.chmod(priv_file_path, 0o400)
-            logger.info("Saved private key to - " + priv_file_path)
+            os.chmod(priv_expand_file, 0o400)
+            logger.info("Saved private key to - " + priv_expand_file)
 
 
 def upload_keypair_file(nova, name, file_path):
index 31750ee..b776ba0 100644 (file)
@@ -32,21 +32,28 @@ __author__ = 'spisarski'
 logger = logging.getLogger('ansible_utils')
 
 
-def apply_playbook(playbook_path, hosts_inv, host_user, ssh_priv_key_file_path, variables=None, proxy_setting=None):
+def apply_playbook(playbook_path, hosts_inv, host_user, ssh_priv_key_file_path,
+                   variables=None, proxy_setting=None):
     """
     Executes an Ansible playbook to the given host
     :param playbook_path: the (relative) path to the Ansible playbook
-    :param hosts_inv: a list of hostnames/ip addresses to which to apply the Ansible playbook
-    :param host_user: A user for the host instances (must be a password-less sudo user if playbook has "sudo: yes"
+    :param hosts_inv: a list of hostnames/ip addresses to which to apply the
+                      Ansible playbook
+    :param host_user: A user for the host instances (must be a password-less
+                      sudo user if playbook has "sudo: yes"
     :param ssh_priv_key_file_path: the file location of the ssh key
-    :param variables: a dictionary containing any substitution variables needed by the Jinga 2 templates
+    :param variables: a dictionary containing any substitution variables needed
+                      by the Jinga 2 templates
     :param proxy_setting: instance of os_credentials.ProxySettings class
     :return: the results
     """
     if not os.path.isfile(playbook_path):
         raise Exception('Requested playbook not found - ' + playbook_path)
-    if not os.path.isfile(ssh_priv_key_file_path):
-        raise Exception('Requested private SSH key not found - ' + ssh_priv_key_file_path)
+
+    pk_file_path = os.path.expanduser(ssh_priv_key_file_path)
+    if not os.path.isfile(pk_file_path):
+        raise Exception('Requested private SSH key not found - ' +
+                        pk_file_path)
 
     import ansible.constants
     ansible.constants.HOST_KEY_CHECKING = False
@@ -56,23 +63,30 @@ def apply_playbook(playbook_path, hosts_inv, host_user, ssh_priv_key_file_path,
         variable_manager.extra_vars = variables
 
     loader = DataLoader()
-    inventory = Inventory(loader=loader, variable_manager=variable_manager, host_list=hosts_inv)
+    inventory = Inventory(loader=loader, variable_manager=variable_manager,
+                          host_list=hosts_inv)
     variable_manager.set_inventory(inventory)
 
     ssh_extra_args = None
     if proxy_setting and proxy_setting.ssh_proxy_cmd:
-        ssh_extra_args = '-o ProxyCommand=\'' + proxy_setting.ssh_proxy_cmd + '\''
-
-    options = namedtuple('Options', ['listtags', 'listtasks', 'listhosts', 'syntax', 'connection', 'module_path',
-                                     'forks', 'remote_user', 'private_key_file', 'ssh_common_args', 'ssh_extra_args',
-                                     'become', 'become_method', 'become_user', 'verbosity', 'check'])
-
-    ansible_opts = options(listtags=False, listtasks=False, listhosts=False, syntax=False, connection='ssh',
-                           module_path=None, forks=100, remote_user=host_user, private_key_file=ssh_priv_key_file_path,
-                           ssh_common_args=None, ssh_extra_args=ssh_extra_args, become=None, become_method=None,
-                           become_user=None, verbosity=11111, check=False)
-
-    logger.debug('Setting up Ansible Playbook Executor for playbook - ' + playbook_path)
+        ssh_extra_args = '-o ProxyCommand=\'%s\'' % proxy_setting.ssh_proxy_cmd
+
+    options = namedtuple(
+        'Options', ['listtags', 'listtasks', 'listhosts', 'syntax',
+                    'connection', 'module_path', 'forks', 'remote_user',
+                    'private_key_file', 'ssh_common_args', 'ssh_extra_args',
+                    'become', 'become_method', 'become_user', 'verbosity',
+                    'check'])
+
+    ansible_opts = options(
+        listtags=False, listtasks=False, listhosts=False, syntax=False,
+        connection='ssh', module_path=None, forks=100, remote_user=host_user,
+        private_key_file=pk_file_path, ssh_common_args=None,
+        ssh_extra_args=ssh_extra_args, become=None, become_method=None,
+        become_user=None, verbosity=11111, check=False)
+
+    logger.debug('Setting up Ansible Playbook Executor for playbook - ' +
+                 playbook_path)
     executor = PlaybookExecutor(
         playbooks=[playbook_path],
         inventory=inventory,
@@ -91,7 +105,8 @@ def ssh_client(ip, user, private_key_filepath, proxy_settings=None):
     :param ip: the IP of the host to connect
     :param user: the user with which to connect
     :param private_key_filepath: the path to the private key file
-    :param proxy_settings: instance of os_credentials.ProxySettings class (optional)
+    :param proxy_settings: instance of os_credentials.ProxySettings class
+                           (optional)
     :return: the SSH client if can connect else false
     """
     logger.debug('Retrieving SSH client')
@@ -105,7 +120,9 @@ def ssh_client(ip, user, private_key_filepath, proxy_settings=None):
             proxy_cmd_str = proxy_cmd_str.replace("%p", '22')
             proxy_cmd = paramiko.ProxyCommand(proxy_cmd_str)
 
-        ssh.connect(ip, username=user, key_filename=private_key_filepath, sock=proxy_cmd)
+        pk_abs_path = os.path.expanduser(private_key_filepath)
+        ssh.connect(ip, username=user, key_filename=pk_abs_path,
+                    sock=proxy_cmd)
         return ssh
     except Exception as e:
         logger.warning('Unable to connect via SSH with message - ' + str(e))