def _login(self, api_url, api_key): cmd = ['ssh', '-i', os.path.expanduser('~/.ssh/id_maas'), '-o', 'UserKnownHostsFile=/dev/null', '-o', 'StrictHostKeyChecking=no', '-o', 'LogLevel=quiet', '{}@{}'.format(self.ssh_user, self.maas_ip), 'maas', 'login', 'maas', api_url, api_key] execc(cmd, stdin=self.cmd_stdin)
def configure_maas(self, client, maas_config): """Configures the MAAS instance.""" nodegroup = self.get_nodegroup(client, maas_config) self.update_nodegroup(client, nodegroup, maas_config) self.create_nodegroup_interfaces(client, nodegroup, maas_config) nodes = maas_config.get('nodes', []) self._create_maas_nodes(client, nodes) self._render_environments_yaml() log.debug("Uploading Juju environments.yaml to MAAS vm") target = '.juju/' script = """ sudo -u juju mkdir -p /home/juju/%s """ % (target) util.exec_script_remote(maas_config['user'], self.ip_addr, script) cmd = self.get_scp_cmd(maas_config['user'], self.ip_addr, JUJU_ENV_YAML) util.execc(cmd) script = """ chown juju: %s; sudo mv %s /home/juju/%s """ % (JUJU_ENV_YAML, JUJU_ENV_YAML, target) util.exec_script_remote(maas_config['user'], self.ip_addr, script) if os.path.exists(util.USER_PRESEED_DIR) and \ os.path.isdir(util.USER_PRESEED_DIR): log.debug('Copying over custom preseed files.') cmd = self.get_scp_cmd(maas_config['user'], self.ip_addr, util.USER_PRESEED_DIR, scp_opts=['-r']) util.execc(cmd) # Move them to the maas dir script = """ chown maas:maas preseeds/* sudo mv preseeds/* /etc/maas/preseeds/ rmdir preseeds """ util.exec_script_remote(maas_config['user'], self.ip_addr, script) # Start juju domain juju_node = self._get_juju_nodename(nodes) if juju_node is not None: try: util.virsh(['start', juju_node]) except CalledProcessError as exc: # Ignore already started error msg = 'Domain is already active' if msg not in exc.output: raise log.debug(msg) self._wait_for_nodes_to_commission(client) self._claim_sticky_ip_address(client, maas_config) log.debug("Done")
def _login(self, api_url, api_key): cmd = [ 'ssh', '-i', os.path.expanduser('~/.ssh/id_maas'), '-o', 'UserKnownHostsFile=/dev/null', '-o', 'StrictHostKeyChecking=no', '-o', 'LogLevel=quiet', '{}@{}'.format(self.ssh_user, self.maas_ip), 'maas', 'login', 'maas', api_url, api_key ] execc(cmd, stdin=self.cmd_stdin)
def wait_for_vm_ready(self, user, host): cmd = self.get_ssh_cmd(user, host, remote_cmd=['true']) while True: try: util.execc(cmd, suppress_stderr=True) log.debug("MAAS vm started.") break except CalledProcessError: log.debug("Waiting for MAAS vm to start.") time.sleep(1) continue
def configure_maas_virsh_control(self, maas_config): """Configure the virsh control SSH keys""" virsh_info = maas_config.get('virsh') if not virsh_info: log.debug('No virsh settings specified in maas_config.') return KEY_TO_FILE_MAP = { 'rsa_priv_key': 'id_rsa', 'rsa_pub_key': 'id_rsa.pub', 'dsa_priv_key': 'id_dsa', 'dsa_pub_key': 'id_dsa.pub', } # First, make the remote directory. remote_cmd = ['mkdir', '-p', 'virsh-keys'] cmd = self.get_ssh_cmd(maas_config['user'], self.ip_addr, remote_cmd=remote_cmd) util.execc(cmd) for key, value in virsh_info.iteritems(): # not a key of interest if not key.endswith('_key'): continue src = os.path.expanduser(value) if not os.path.isfile(src): raise MAASDeployerValueError("Virsh SSH key '%s' not found" % (src)) dst = 'virsh-keys/%s' % KEY_TO_FILE_MAP[key] cmd = self.get_scp_cmd(maas_config['user'], self.ip_addr, src, dst) util.execc(cmd) # Now move them over to the maas user. script = """ maas_home=$(echo ~maas) sudo mkdir -p $maas_home/.ssh sudo mv ~/virsh-keys/* $maas_home/.ssh sudo chown -R maas:maas $maas_home/.ssh sudo chmod 700 $maas_home/.ssh sudo find $maas_home/.ssh -name id* | xargs sudo chmod 600 rmdir ~/virsh-keys """ util.exec_script_remote(maas_config['user'], self.ip_addr, script)
def configure_maas_virsh_control(self, maas_config): """Configure the virsh control SSH keys""" virsh_info = maas_config.get('virsh') if not virsh_info: log.debug('No virsh settings specified in maas_config.') return KEY_TO_FILE_MAP = { 'rsa_priv_key': 'id_rsa', 'rsa_pub_key': 'id_rsa.pub', 'dsa_priv_key': 'id_dsa', 'dsa_pub_key': 'id_dsa.pub', } # First, make the remote directory. remote_cmd = ['mkdir', '-p', 'virsh-keys'] cmd = self.get_ssh_cmd(maas_config['user'], self.ip_addr, remote_cmd=remote_cmd) util.execc(cmd) for key in virsh_info: # not a key of interest if not key.endswith('_key'): continue value = virsh_info[key] src = os.path.expanduser(value) if not os.path.isfile(src): raise MAASDeployerValueError("Virsh SSH key '%s' not found" % (src)) dst = 'virsh-keys/%s' % KEY_TO_FILE_MAP[key] cmd = self.get_scp_cmd(maas_config['user'], self.ip_addr, src, dst) util.execc(cmd) # Now move them over to the maas user. script = """ maas_home=$(echo ~maas) sudo mkdir -p $maas_home/.ssh sudo mv ~/virsh-keys/* $maas_home/.ssh sudo chown -R maas:maas $maas_home/.ssh sudo chmod 700 $maas_home/.ssh sudo find $maas_home/.ssh -name id* | xargs sudo chmod 600 rmdir ~/virsh-keys """ util.exec_script_remote(maas_config['user'], self.ip_addr, script)
def wait_for_cloudinit_finished(self, maas_config, maas_ip): log.debug("Logging into maas host '%s'", (maas_ip)) # Now get the api key msg = "MAAS controller is now configured" cloudinitlog = '/var/log/cloud-init-output.log' rcmd = ['grep "%s" %s' % (msg, cloudinitlog)] cmd = self.get_ssh_cmd(maas_config['user'], maas_ip, remote_cmd=rcmd) out, err = util.execc(cmd=cmd, fatal=False) if out and not err: self._get_api_key_from_cloudinit(maas_config['user'], maas_ip) return log.info("Waiting for cloud-init to complete - this usually takes " "several minutes") rcmd = ['grep -m 1 "%s" <(sudo tail -n 1 -F %s)' % (msg, cloudinitlog)] cmd = self.get_ssh_cmd(maas_config['user'], maas_ip, remote_cmd=rcmd) util.execc(cmd=cmd) log.info("done.") self._get_api_key_from_cloudinit(maas_config['user'], maas_ip)
def _get_api_key_from_cloudinit(self, user, addr): # Now get the api key rcmd = [r'grep "+ apikey=" %s| tail -n 1| sed -r "s/.+=(.+)/\1/"' % ('/var/log/cloud-init-output.log')] cmd = self.get_ssh_cmd(user, addr, remote_cmd=rcmd) stdout, _ = util.execc(cmd=cmd) if stdout: stdout = stdout.strip() self.api_key = stdout
def _get_api_key_from_cloudinit(self, user, addr): # Now get the api key rcmd = [ r'grep "+ apikey=" %s| tail -n 1| sed -r "s/.+=(.+)/\1/"' % ('/var/log/cloud-init-output.log') ] cmd = self.get_ssh_cmd(user, addr, remote_cmd=rcmd) stdout, _ = util.execc(cmd=cmd) if stdout: stdout = stdout.strip() self.api_key = stdout
def _get_api_key(self, maas_config): """Retrieves the API key""" if not self.api_key: log.debug("Fetching MAAS api key") user = maas_config['user'] remote_cmd = ['sudo', 'maas-region-admin', 'apikey', '--username', user] cmd = self.get_ssh_cmd(maas_config['user'], self.ip_addr, remote_cmd=remote_cmd) self.api_key, _ = util.execc(cmd) if self.api_key: self.api_key = self.api_key.strip() return self.api_key
def _get_api_key(self, maas_config): """Retrieves the API key""" if not self.api_key: log.debug("Fetching MAAS api key") user = maas_config['user'] remote_cmd = [ 'sudo', 'maas-region-admin', 'apikey', '--username', user ] cmd = self.get_ssh_cmd(maas_config['user'], self.ip_addr, remote_cmd=remote_cmd) self.api_key, _ = util.execc(cmd) if self.api_key: self.api_key = self.api_key.strip() return self.api_key
def test_execc_piped_stderr(self): tmpdir = tempfile.mkdtemp() try: # expect error cmd1 = ['ls', os.path.join(tmpdir, '1')] self.assertRaises(subprocess.CalledProcessError, util.execc, cmd1) open(os.path.join(tmpdir, '2'), 'w') # expect NO error cmd1 = ['ls', os.path.join(tmpdir, '2')] util.execc(cmd1) # expect error cmd1 = ['ls', os.path.join(tmpdir, '1')] cmd2 = ['ls', os.path.join(tmpdir, '2')] open(os.path.join(tmpdir, '2'), 'w') self.assertRaises(subprocess.CalledProcessError, util.execc, cmd1, pipedcmds=[cmd2]) # expect NO error cmd1 = ['ls', os.path.join(tmpdir, '2')] cmd2 = ['ls', os.path.join(tmpdir, '2')] open(os.path.join(tmpdir, '2'), 'w') util.execc(cmd1, pipedcmds=[cmd2]) # test exception params cmd1 = ['ls', os.path.join(tmpdir, '1')] cmd2 = ['ls', os.path.join(tmpdir, '2')] open(os.path.join(tmpdir, '2'), 'w') try: util.execc(cmd1, pipedcmds=[cmd2]) except subprocess.CalledProcessError as exc: test_passed = False for msg in [("ls: cannot access %s: No such file or " "directory\n"), ("ls: cannot access '%s': No such file or " "directory\n")]: try: self.assertEqual(exc.output, msg % (os.path.join(tmpdir, '1'))) test_passed = True break except AssertionError: pass self.assertTrue(test_passed, exc.output) self.assertEqual(exc.returncode, 2) else: raise UnitTestException("Exception not raised") finally: shutil.rmtree(tmpdir)
def _maas_execute(self, cmd, *args, **kwargs): """ Executes the specified subcommand. The keyword maas and the profile name will be prepended to the name """ try: cmdarr = self._get_base_command() cmdarr.append(str(cmd)) if args: for a in args: cmdarr.append(str(a)) if kwargs: for key in kwargs.keys(): value = kwargs[key] if isinstance(value, list): for v in value: cmdarr.append("%s='%s'" % (key, str(v))) else: cmdarr.append("%s='%s'" % (key, str(value))) stdout = execc(cmdarr, stdin=self.cmd_stdin)[0] display_stdout = stdout if display_stdout and len(display_stdout) > 100: display_stdout = "%s..." % display_stdout[:100] log.debug("Command executed successfully: stdout='%s'", display_stdout) try: output = json.loads(stdout) except ValueError: output = stdout return Response(True, output) except CalledProcessError as cpe: log.error("Command '%s' failed: rc='%s' output='%s'", ' '.join(cmdarr), cpe.returncode, cpe.output) return Response(False, cpe.output) except OSError as ose: log.error("Command '%s' failed: error='%s'", ' '.join(cmdarr), str(ose)) return Response(False, None)
def _create_new_boot_source(self, client, maas_config, url, keyring_data, keyring_filename): log.debug("Creating new boot source url='%s'", (url)) # If we want to supply new keyring data we have to write it to # a file, upload it and reference that from the cli. if keyring_data: ssh_user = maas_config['user'] remote_host = self.ip_addr with tempfile.NamedTemporaryFile() as ftmp: with open(ftmp.name, 'w') as fd: fd.write(base64.b64decode(keyring_data)) filepath = "/tmp/maas-deployer-archive-keyring.gpg" util.execc( self.get_scp_cmd(ssh_user, remote_host, ftmp.name, filepath)) target = (keyring_filename or "/usr/share/keyrings/%s" % (os.path.basename(filepath))) log.debug("Writing boot source key '%s'", (target)) cmd = ['sudo', 'mv', filepath, target] util.execc( self.get_ssh_cmd(ssh_user, remote_host, remote_cmd=cmd)) cmd = ['sudo', 'chmod', '0644', target] util.execc( self.get_ssh_cmd(ssh_user, remote_host, remote_cmd=cmd)) cmd = ['sudo', 'chown', 'root:', target] util.execc( self.get_ssh_cmd(ssh_user, remote_host, remote_cmd=cmd)) keyring_filename = target ret = client.create_boot_source(url, keyring_filename=keyring_filename) if not ret: msg = "Failed to create boot resource url='%s'" % (url) log.error(msg) raise MAASDeployerClientError(msg)
def _create_new_boot_source(self, client, maas_config, url, keyring_data, keyring_filename): log.debug("Creating new boot source url='%s'", (url)) # If we want to supply new keyring data we have to write it to # a file, upload it and reference that from the cli. if keyring_data: ssh_user = maas_config['user'] remote_host = self.ip_addr with tempfile.NamedTemporaryFile() as ftmp: with open(ftmp.name, 'w') as fd: fd.write(base64.b64decode(keyring_data)) filepath = "/tmp/maas-deployer-archive-keyring.gpg" util.execc(self.get_scp_cmd(ssh_user, remote_host, ftmp.name, filepath)) target = (keyring_filename or "/usr/share/keyrings/%s" % (os.path.basename(filepath))) log.debug("Writing boot source key '%s'", (target)) cmd = ['sudo', 'mv', filepath, target] util.execc(self.get_ssh_cmd(ssh_user, remote_host, remote_cmd=cmd)) cmd = ['sudo', 'chmod', '0644', target] util.execc(self.get_ssh_cmd(ssh_user, remote_host, remote_cmd=cmd)) cmd = ['sudo', 'chown', 'root:', target] util.execc(self.get_ssh_cmd(ssh_user, remote_host, remote_cmd=cmd)) keyring_filename = target ret = client.create_boot_source(url, keyring_filename=keyring_filename) if not ret: msg = "Failed to create boot resource url='%s'" % (url) log.error(msg) raise MAASDeployerClientError(msg)
def test_execc_piped_stderr(self): tmpdir = tempfile.mkdtemp() try: # expect error cmd1 = ['ls', os.path.join(tmpdir, '1')] self.assertRaises(subprocess.CalledProcessError, util.execc, cmd1) open(os.path.join(tmpdir, '2'), 'w') # expect NO error cmd1 = ['ls', os.path.join(tmpdir, '2')] util.execc(cmd1) # expect error cmd1 = ['ls', os.path.join(tmpdir, '1')] cmd2 = ['ls', os.path.join(tmpdir, '2')] open(os.path.join(tmpdir, '2'), 'w') self.assertRaises(subprocess.CalledProcessError, util.execc, cmd1, pipedcmds=[cmd2]) # expect NO error cmd1 = ['ls', os.path.join(tmpdir, '2')] cmd2 = ['ls', os.path.join(tmpdir, '2')] open(os.path.join(tmpdir, '2'), 'w') util.execc(cmd1, pipedcmds=[cmd2]) # test exception params cmd1 = ['ls', os.path.join(tmpdir, '1')] cmd2 = ['ls', os.path.join(tmpdir, '2')] open(os.path.join(tmpdir, '2'), 'w') try: util.execc(cmd1, pipedcmds=[cmd2]) except subprocess.CalledProcessError as exc: self.assertEqual( exc.output, "ls: cannot access %s: No such file or " "directory\n" % (os.path.join(tmpdir, '1'))) self.assertEqual(exc.returncode, 2) else: raise UnitTestException("Exception not raised") finally: shutil.rmtree(tmpdir)
def configure_maas(self, client, maas_config): """Configures the MAAS instance.""" nodegroup = self.get_nodegroup(client, maas_config) self.update_nodegroup(client, nodegroup, maas_config) self.create_nodegroup_interfaces(client, nodegroup, maas_config) nodes = maas_config.get('nodes', []) self._create_maas_nodes(client, nodes) self._render_environments_yaml() log.debug("Uploading Juju environments.yaml to MAAS vm") target = '.juju/' script = """ sudo -u juju mkdir -p /home/juju/%s """ % (target) util.exec_script_remote(maas_config['user'], self.ip_addr, script) cmd = self.get_scp_cmd(maas_config['user'], self.ip_addr, JUJU_ENV_YAML) util.execc(cmd) script = """ chown juju: %s; sudo mv %s /home/juju/%s """ % (JUJU_ENV_YAML, JUJU_ENV_YAML, target) util.exec_script_remote(maas_config['user'], self.ip_addr, script) if os.path.exists(util.USER_PRESEED_DIR) and \ os.path.isdir(util.USER_PRESEED_DIR): log.debug('Copying over custom preseed files.') cmd = self.get_scp_cmd(maas_config['user'], self.ip_addr, util.USER_PRESEED_DIR, scp_opts=['-r']) util.execc(cmd) # Move them to the maas dir script = """ chown maas:maas preseeds/* sudo mv preseeds/* /etc/maas/preseeds/ rmdir preseeds """ util.exec_script_remote(maas_config['user'], self.ip_addr, script) # Start juju domain for n in nodes: name = n['name'] try: log.info('Starting: %s' % name) util.virsh(['start', name]) except CalledProcessError as exc: # Ignore already started error msg = 'Domain is already active' if msg not in exc.output: raise log.debug(msg) self._wait_for_nodes_to_commission(client) self._claim_sticky_ip_address(client, maas_config) log.debug("Done")