def _pre_setup_checks(ssh_client, log): cmd = 'docker --version' msg, errmsg = _exec_ssh_cmd(cmd, ssh_client, log) if errmsg: raise CommandError("'%s' failed. Is docker installed? : %s" % (cmd, errmsg)) if 'Docker version' not in msg: raise CommandError("'%s' failed. Is docker installed? : %s" % (cmd, msg)) version = msg.split('version ')[1].split(',')[0] if StrictVersion(version) < StrictVersion(MIN_DOCKER_VERSION): raise CommandError('docker version (%s) below minimum (%s)' % (version, msg)) # docker is installed, now check if it is running cmd = 'docker info' _, errmsg = _exec_ssh_cmd(cmd, ssh_client, log) # docker info can return warning messages in stderr, ignore them if errmsg and 'WARNING' not in errmsg: raise CommandError("'%s' failed. Is docker running? : %s" % (cmd, errmsg)) # check for docker-py cmd = 'python -c "import docker"' msg, errmsg = _exec_ssh_cmd(cmd, ssh_client, log) if errmsg: raise CommandError('host check failed. ' + 'Is docker-py installed?')
def add_host(self, hostname, groupname=None): """add host if groupname is none, create a new host if group name is not none, add host to group """ if groupname and groupname not in self._groups: raise CommandError('Group name (%s) does not exist' % groupname) if groupname and hostname not in self._hosts: raise CommandError('Host name (%s) does not exist' % hostname) if not groupname and not self.remote_mode and len(self._hosts) >= 1: raise CommandError('Cannot have more than one host when in ' + 'local deploy mode') # create new host if it doesn't exist host = Host(hostname) if hostname not in self.get_hostnames(): # a new host is being added to the inventory self._hosts[hostname] = host # a host is to be added to an existing group elif groupname: group = self._groups[groupname] if hostname not in group.get_hostnames(): group.add_host(host)
def __init__(self): super(KollaCli, self).__init__( description='Command-Line Client for OpenStack Kolla', version='0.1', command_manager=CommandManager('kolla.cli'), ) # check that current user is in the kolla group inventory_path = os.path.join(get_kollacli_etc(), INVENTORY_PATH) errString = 'Required file %s does not exist.\n' + \ 'Please re-install the kollacli to recreate the file.' if os.path.isfile(inventory_path) is False: raise CommandError(errString % inventory_path) inventory_file = None try: inventory_file = open(inventory_path, 'r+') except Exception: raise CommandError('Permission denied to run the kollacli.' + '\nPlease add user to the kolla group and ' + 'then log out and back in.') finally: if inventory_file and inventory_file.close is False: inventory_file.close() self.rotating_log_dir = get_kolla_log_dir() self.max_bytes = get_kolla_log_file_size() self.backup_count = 4 self.dump_stack_trace = False self.add_rotational_log()
def remove_group_from_service(self, groupname, servicename): if groupname not in self._groups: raise CommandError('Group (%s) not found.' % groupname) if servicename in self._services: service = self.get_service(servicename) service.remove_groupname(groupname) elif servicename in self._sub_services: sub_service = self.get_sub_service(servicename) sub_service.remove_groupname(groupname) else: raise CommandError('Service (%s) not found.' % servicename)
def get_yml_data(self, yml_path): if not os.path.isfile(yml_path): raise CommandError('No file exists at %s. ' % yml_path + 'An absolute file path is required.') with open(yml_path, 'r') as hosts_file: file_data = hosts_file.read() hosts_info = yaml.load(file_data) if not hosts_info: raise CommandError('%s is empty' % yml_path) return hosts_info
def setup_hosts(self, hosts_info): """setup multiple hosts hosts_info is a dict of format: {'hostname1': { 'password': password 'uname': user_name } } The uname entry is optional. """ failed_hosts = {} for hostname, host_info in hosts_info.items(): host = self.get_host(hostname) if not host: failed_hosts[hostname] = "Host doesn't exist" continue if not host_info or 'password' not in host_info: failed_hosts[hostname] = 'No password in yml file' continue passwd = host_info['password'] uname = None if 'uname' in host_info: uname = host_info['uname'] try: self.setup_host(hostname, passwd, uname) except Exception as e: failed_hosts[hostname] = '%s' % e if failed_hosts: summary = '\n' for hostname, err in failed_hosts.items(): summary = summary + '- %s: %s\n' % (hostname, err) raise CommandError('Not all hosts were set up: %s' % summary) else: self.log.info('All hosts were successfully set up')
def _post_setup_checks(net_addr, log): try: ssh_client = ssh_connect(net_addr, get_admin_user(), '') except Exception as e: raise CommandError("remote login failed : %s" % e) try: # a basic test ssh_client.exec_command('ls') except Exception as e: raise CommandError("remote command 'ls' failed : %s" % e) finally: _close_ssh_client(ssh_client)
def take_action(self, parsed_args): try: if parsed_args.hosts and parsed_args.groups: raise CommandError('Hosts and Groups arguments cannot both ' + 'be present at the same time.') self._run_rules() playbook = AnsiblePlaybook() kolla_home = get_kolla_home() playbook.playbook_path = os.path.join(kolla_home, 'ansible/site.yml') if parsed_args.hosts: host_list = parsed_args.hosts.strip() host_list = convert_to_unicode(host_list) playbook.hosts = host_list.split(',') if parsed_args.groups: group_list = parsed_args.groups.strip() group_list = convert_to_unicode(group_list) playbook.groups = group_list.split(',') if parsed_args.services: tag_list = parsed_args.services.strip() tag_list = convert_to_unicode(tag_list) playbook.services = tag_list.split(',') if parsed_args.serial: playbook.serial = True playbook.verbose_level = self.app.options.verbose_level playbook.run() except CommandError as e: raise e except Exception: raise Exception(traceback.format_exc())
def set_deploy_mode(self, remote_flag): if not remote_flag and len(self._hosts) > 1: raise CommandError('Cannot set local deploy mode when multiple ' + 'hosts exist') self.remote_mode = remote_flag for group in self.get_groups(): group.set_remote(remote_flag)
def clear_password(pwd_key): """clear a password if the password exists, it will be removed from the passwords file """ cmd = '%s -k %s -c' % (_get_cmd_prefix(), pwd_key) err_msg, output = utils.run_cmd(cmd, print_output=False) if err_msg: raise CommandError('%s %s' % (err_msg, output))
def set_password(pwd_key, pwd_value): """set a password value If the password name exists, it will be changed. If it doesn't exist, a new password will be added. """ cmd = '%s -k %s -v %s' % (_get_cmd_prefix(), pwd_key, pwd_value) err_msg, output = utils.run_cmd(cmd, print_output=False) if err_msg: raise CommandError('%s %s' % (err_msg, output))
def get_password_names(): """return a list of password names""" cmd = '%s -l' % (_get_cmd_prefix()) err_msg, output = utils.run_cmd(cmd, print_output=False) if err_msg: raise CommandError('%s %s' % (err_msg, output)) pwd_names = [] if output and ',' in output: pwd_names = output.strip().split(',') return pwd_names
def take_action(self, parsed_args): try: if not parsed_args.hostname and not parsed_args.file: raise CommandError('Hostname or hosts info file path ' + 'is required') if parsed_args.hostname and parsed_args.file: raise CommandError('Hostname and hosts info file path ' + 'cannot both be present') inventory = Inventory.load() if parsed_args.file: # multi-host setup via xml file hosts_data = self.get_yml_data(parsed_args.file.strip()) inventory.setup_hosts(hosts_data) else: # single host setup hostname = parsed_args.hostname.strip() hostname = utils.convert_to_unicode(hostname) if not inventory.get_host(hostname): _host_not_found(self.log, hostname) check_ok = inventory.check_host(hostname, True) if check_ok: self.log.info( 'Skipping setup of host (%s) as check is ok' % hostname) return True if parsed_args.insecure: password = parsed_args.insecure.strip() else: setup_user = get_setup_user() password = getpass.getpass('%s password for %s: ' % (setup_user, hostname)) password = utils.convert_to_unicode(password) inventory.setup_host(hostname, password) except CommandError as e: raise e except Exception as e: raise Exception(traceback.format_exc())
def save(inventory): """Save the inventory in a pickle file""" inventory_path = os.path.join(utils.get_kollacli_etc(), INVENTORY_PATH) try: # multiple trips thru json to render a readable inventory file data = jsonpickle.encode(inventory) data_str = json.loads(data) pretty_data = json.dumps(data_str, indent=4) utils.sync_write_file(inventory_path, pretty_data) except Exception as e: raise CommandError('saving inventory failed: %s' % e)
def remove_group(self, groupname): if groupname in PROTECTED_GROUPS: raise CommandError('Cannot remove %s group. ' % groupname + 'It is required by kolla.') # remove group from services & subservices for service in self._services.values(): service.remove_groupname(groupname) for subservice in self._sub_services.values(): subservice.remove_groupname(groupname) if groupname in self._groups: del self._groups[groupname]
def add_group(self, groupname): # Group names cannot overlap with service names: if groupname in self._services or groupname in self._sub_services: raise CommandError('Invalid group name. A service name ' 'cannot be used for a group name.') if groupname not in self._groups: self._groups[groupname] = HostGroup(groupname) group = self._groups[groupname] group.set_remote(self.remote_mode) return group
def take_action(self, parsed_args): try: mode = parsed_args.mode.strip() remote_flag = False if mode == 'remote': remote_flag = True elif mode != 'local': raise CommandError('Invalid deploy mode. Mode must be ' + 'either "local" or "remote"') inventory = Inventory.load() inventory.set_deploy_mode(remote_flag) Inventory.save(inventory) except CommandError as e: raise e except Exception: raise Exception(traceback.format_exc())
def _run_rules(self): # check that ring files are in /etc/kolla/config/swift if # swift is enabled expected_files = ['account.ring.gz', 'container.ring.gz', 'object.ring.gz'] properties = AnsibleProperties() is_enabled = properties.get_property('enable_swift') if is_enabled == 'yes': path_pre = os.path.join(get_kolla_etc(), 'config', 'swift') for expected_file in expected_files: path = os.path.join(path_pre, expected_file) if not os.path.isfile(path): msg = ('Deploy failed. ' + 'Swift is enabled but ring buffers have ' + 'not yet been set up. Please see the ' + 'documentation for swift configuration ' + 'instructions.') raise CommandError(msg)
def remove_host(self, hostname, groupname=None): """remove host if groupname is none, delete host if group name is not none, remove host from group """ if groupname and groupname not in self._groups: raise CommandError('Group name (%s) does not exist' % groupname) if hostname not in self._hosts: return host = self._hosts[hostname] groups = self.get_groups(host) for group in groups: if not groupname or groupname == group.name: group.remove_host(host) if not groupname: del self._hosts[hostname]
def load(): """load the inventory from a pickle file""" inventory_path = os.path.join(utils.get_kollacli_etc(), INVENTORY_PATH) data = '' try: if os.path.exists(inventory_path): data = utils.sync_read_file(inventory_path) if data.strip(): inventory = jsonpickle.decode(data) # upgrade version handling if inventory.version != inventory.class_version: inventory.upgrade() else: inventory = Inventory() except Exception: raise CommandError('loading inventory failed: %s' % traceback.format_exc()) return inventory
def _host_not_found(log, hostname): raise CommandError('Host (%s) not found. ' % hostname + 'Please add it with "host add"')
def run(self): globals_string = None password_string = None inventory_path = None cmd = '' try: flag = '' # verbose levels: 1=not verbose, 2=more verbose if self.verbose_level > 1: flag = '-vvv' admin_user = get_admin_user() command_string = ('/usr/bin/sudo -u %s ansible-playbook %s' % (admin_user, flag)) inventory = Inventory.load() inventory_filter = {} if self.hosts: for hostname in self.hosts: host = inventory.get_host(hostname) if not host: raise CommandError('Host (%s) not found. ' % hostname) inventory_filter['deploy_hosts'] = self.hosts elif self.groups: for groupname in self.groups: group = inventory.get_group(groupname) if not group: raise CommandError('Group (%s) not found. ' % groupname) inventory_filter['deploy_groups'] = self.groups inventory_path = inventory.create_json_gen_file(inventory_filter) inventory_string = '-i ' + inventory_path cmd = (command_string + ' ' + inventory_string) if self.include_globals: globals_string = self._get_globals_path() cmd = (cmd + ' ' + globals_string) if self.include_passwords: password_string = self._get_password_path() cmd = (cmd + ' ' + password_string) cmd = (cmd + ' ' + self.playbook_path) if self.extra_vars or self.serial: extra_vars = '' if self.extra_vars: extra_vars = self.extra_vars if self.serial: extra_vars += ' ' if self.serial: extra_vars += 'serial_var=1' cmd = (cmd + ' --extra-vars \"' + extra_vars + '\"') if self.services: service_string = '' first = True for service in self.services: valid_service = inventory.get_service(service) if not valid_service: raise CommandError('Service (%s) not found. ' % service) if not first: service_string = service_string + ',' else: first = False service_string = service_string + service cmd = (cmd + ' --tags ' + service_string) if self.flush_cache: cmd = (cmd + ' --flush-cache') if self.verbose_level > 1: # log the ansible command self.log.debug('cmd:' + cmd) if self.verbose_level > 2: # log the inventory dbg_gen = inventory_path (inv, _) = \ subprocess.Popen(dbg_gen.split(' '), stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate() self.log.debug(inv) err_msg, output = run_cmd(cmd, self.print_output) if err_msg: if not self.print_output: # since the user didn't see the output, include it in # the error message err_msg = '%s %s' % (err_msg, output) raise CommandError(err_msg) self.log.info('Success') except CommandError as e: raise e except Exception: raise Exception(traceback.format_exc()) finally: if inventory_path: os.remove(inventory_path)