def yes(self, answer: str) -> None: log.info('Configuring nova control plane services ...') if not call('openstack', 'user', 'show', 'nova'): check('openstack', 'user', 'create', '--domain', 'default', '--password', shell.config_get('config.credentials.nova-password'), 'nova') check('openstack', 'role', 'add', '--project', 'service', '--user', 'nova', 'admin') # Assign the reader role to the nova user so that read-only # application credentials can be created. check('openstack', 'role', 'add', '--project', 'service', '--user', 'nova', 'reader') log.info('Running Nova API DB migrations' ' (this may take a lot of time)...') check('snap-openstack', 'launch', 'nova-manage', 'api_db', 'sync') if 'cell0' not in check_output('snap-openstack', 'launch', 'nova-manage', 'cell_v2', 'list_cells'): check('snap-openstack', 'launch', 'nova-manage', 'cell_v2', 'map_cell0') if 'cell1' not in check_output('snap-openstack', 'launch', 'nova-manage', 'cell_v2', 'list_cells'): check('snap-openstack', 'launch', 'nova-manage', 'cell_v2', 'create_cell', '--name=cell1', '--verbose') log.info('Running Nova DB migrations' ' (this may take a lot of time)...') check('snap-openstack', 'launch', 'nova-manage', 'db', 'sync') enable('nova-api') restart('nova-compute') for service in [ 'nova-api-metadata', 'nova-conductor', 'nova-scheduler', ]: enable(service) nc_wait(_env['compute_ip'], '8774') sleep(5) # TODO: log_wait if not call('openstack', 'service', 'show', 'compute'): check('openstack', 'service', 'create', '--name', 'nova', '--description', '"Openstack Compute"', 'compute') for endpoint in ['public', 'internal', 'admin']: call('openstack', 'endpoint', 'create', '--region', 'microstack', 'compute', endpoint, 'http://{control_ip}:8774/v2.1'.format(**_env)) log.info('Creating default flavors...') self._flavors()
def yes(self, answer: bool): log.info('Configuring clustering ...') role_question = clustering.Role() if not (self.interactive and self.role_interactive): role_question.interactive = False role_question.ask() questions = [ # Skipped for the compute role and is automatically taken # from the connection string. clustering.ControlIp(), # Skipped for the control role since it is identical to the # control node IP. clustering.ComputeIp(), ] for question in questions: if not self.interactive: question.interactive = False question.ask() connection_string_question = clustering.ConnectionString() if not (self.interactive and self.connection_string_interactive): connection_string_question.interactive = False connection_string_question.ask() role = shell.config_get('config.cluster.role') if role == 'compute': log.info('Setting up as a compute node.') # Gets config info and sets local env vals. check_output('microstack_join') shell.config_set( **{ 'config.services.control-plane': 'false', 'config.services.hypervisor': 'true', }) if role == 'control': log.info('Setting up as a control node.') shell.config_set( **{ 'config.services.control-plane': 'true', 'config.services.hypervisor': 'true', }) # Generate a self-signed certificate for the clustering service. cluster_tls.generate_selfsigned() # Write templates check('snap-openstack', 'setup')
def yes(self, answer: str) -> None: """Since this is an auto question, we always execute yes.""" log.info('Loading config and writing templates ...') log.info('Validating config ...') for key in ['ospassword', 'extgateway', 'extcidr', 'dns']: val = check_output('snapctl', 'get', key) if not val: raise ConfigError( 'Expected config value {} is not set.'.format(key)) _env[key] = val log.info('Writing out templates ...') check('snap-openstack', 'setup') # Parse microstack.rc, and load into _env # TODO: write something more robust (this breaks on comments # at end of line.) mstackrc = '{SNAP_COMMON}/etc/microstack.rc'.format(**_env) with open(mstackrc, 'r') as rc_file: for line in rc_file.readlines(): if not line.startswith('export'): continue key, val = line[7:].split('=') _env[key.strip()] = val.strip()
def yes(self, answer: str) -> None: log.info('Configuring the Cinder services...') if not call('openstack', 'user', 'show', 'cinder'): check('openstack', 'user', 'create', '--domain', 'default', '--password', shell.config_get('config.credentials.cinder-password'), 'cinder') check('openstack', 'role', 'add', '--project', 'service', '--user', 'cinder', 'admin') control_ip = _env['control_ip'] for endpoint in ['public', 'internal', 'admin']: for api_version in ['v2', 'v3']: if not call('openstack', 'service', 'show', f'cinder{api_version}'): check('openstack', 'service', 'create', '--name', f'cinder{api_version}', '--description', f'"Cinder {api_version} API"', f'volume{api_version}') if not check_output('openstack', 'endpoint', 'list', '--service', f'volume{api_version}', '--interface', endpoint): check( 'openstack', 'endpoint', 'create', '--region', 'microstack', f'volume{api_version}', endpoint, f'http://{control_ip}:8776/{api_version}/' '$(project_id)s') log.info('Running Cinder DB migrations...') check('snap-openstack', 'launch', 'cinder-manage', 'db', 'sync') enable('cinder-uwsgi') enable('cinder-scheduler')
def _wait(self) -> None: enable('mysqld') mysql_port = check_output('snapctl', 'get', 'config.network.ports.mysql') nc_wait(_env['control_ip'], mysql_port) log_wait('{SNAP_COMMON}/log/mysql/error.log'.format(**_env), 'mysqld: ready for connections.')
def yes(self, answer: str) -> None: if 'microstack' not in check_output('openstack', 'keypair', 'list'): log.info('Creating microstack keypair (~/.ssh/{})'.format(answer)) check('mkdir', '-p', '{HOME}/.ssh'.format(**_env)) check('chmod', '700', '{HOME}/.ssh'.format(**_env)) id_ = check_output('openstack', 'keypair', 'create', 'microstack') id_path = '{HOME}/.ssh/{answer}'.format(HOME=_env['HOME'], answer=answer) with open(id_path, 'w') as file_: file_.write(id_) check('chmod', '600', id_path) # TODO: too many assumptions in the below. Make it portable! user = _env['HOME'].split("/")[2] check('chown', '{}:{}'.format(user, user), id_path)
def _wait(self) -> None: enable('rabbitmq-server') rabbit_port = check_output('snapctl', 'get', 'config.network.ports.rabbit') nc_wait(_env['control_ip'], rabbit_port) log_file = '{SNAP_COMMON}/log/rabbitmq/startup_log'.format(**_env) log_wait(log_file, 'completed')
def wrapper(*args, **kwargs): if int(check_output('id', '-u')): log.error("This script must be run with root privileges. " "Please re-run with sudo.") sys.exit(1) return func(*args, **kwargs)
def yes(self, answer: str) -> None: # Create security group rules log.info('Creating security group rules ...') group_id = check_output('openstack', 'security', 'group', 'list', '--project', 'admin', '-f', 'value', '-c', 'ID') rules = check_output('openstack', 'security', 'group', 'rule', 'list', '--format', 'json') ping_rule = False ssh_rule = False for rule in json.loads(rules): if rule['Security Group'] == group_id: if rule['IP Protocol'] == 'icmp': ping_rule = True if rule['IP Protocol'] == 'tcp': ssh_rule = True if not ping_rule: check('openstack', 'security', 'group', 'rule', 'create', group_id, '--proto', 'icmp') if not ssh_rule: check('openstack', 'security', 'group', 'rule', 'create', group_id, '--proto', 'tcp', '--dst-port', '22')
def _is_hw_virt_supported(): # Sample lscpu outputs: util-linux/tests/expected/lscpu/ cpu_info = json.loads(check_output('lscpu', '-J'))['lscpu'] architecture = next( filter(lambda x: x['field'] == 'Architecture:', cpu_info), None)['data'].split() flags = next(filter(lambda x: x['field'] == 'Flags:', cpu_info), None) if flags is not None: flags = flags['data'].split() vendor_id = next( filter(lambda x: x['field'] == 'Vendor ID:', cpu_info), None) if vendor_id is not None: vendor_id = vendor_id['data'] # Mimic virt-host-validate code (from libvirt) and assume nested # support on ppc64 LE or BE. if architecture in ['ppc64', 'ppc64le']: return True elif vendor_id is not None and flags is not None: if vendor_id == 'AuthenticAMD' and 'svm' in flags: return True elif vendor_id == 'GenuineIntel' and 'vmx' in flags: return True elif vendor_id == 'IBM/S390' and 'sie' in flags: return True elif vendor_id == 'ARM': # ARM 8.3-A added nested virtualization support but it is yet # to land upstream https://lwn.net/Articles/812280/ at the time # of writing (Nov 2020). log.warning('Nested virtualization is not supported on ARM' ' - will use emulation') return False else: log.warning('Unable to determine hardware virtualization' f' support by CPU vendor id "{vendor_id}":' ' assuming it is not supported.') return False else: log.warning('Unable to determine hardware virtualization support' ' by the output of lscpu: assuming it is not' ' supported') return False
def _load(self): """Get the current value of the answer to this question. Useful for loading defaults during init, and for loading operator specified settings during updates. """ # Translate the CamelCase name of this class to the dash # seperated name of a key in the snapctl config. key = inflection.dasherize( inflection.underscore(self.__class__.__name__)) answer = shell.check_output('snapctl', 'get', 'questions.{key}'.format(key=key)) # Convert boolean values in to human friendly "yes" or "no" # values. if answer.strip().lower() == 'true': answer = 'yes' if answer.strip().lower() == 'false': answer = 'no' return answer
def yes(self, answer: str) -> None: """Possibly force us to use qemu emulation rather than kvm.""" cpuinfo = check_output('cat', '/proc/cpuinfo') if 'vmx' in cpuinfo or 'svm' in cpuinfo: # We have processor extensions installed. No need to Force # Qemu emulation. return _path = '{SNAP_COMMON}/etc/nova/nova.conf.d/hypervisor.conf'.format( **_env) with open(_path, 'w') as _file: _file.write("""\ [DEFAULT] compute_driver = libvirt.LibvirtDriver [workarounds] disable_rootwrap = True [libvirt] virt_type = qemu cpu_mode = host-model """)
def _load(self): """Get the current value of the answer to this question. Useful for loading defaults during init, and for loading operator specified settings during updates. """ if self._type == 'auto': return answer = shell.check_output('snapctl', 'get', '{key}'.format(key=self.config_key)) # Convert boolean values in to human friendly "yes" or "no" # values. if answer.strip().lower() == 'true': answer = 'yes' if answer.strip().lower() == 'false': answer = 'no' # Convert null to None if answer.strip().lower() == 'null': answer = None return answer
def yes(self, answer: str) -> None: log.info('Configuring nova ...') if not call('openstack', 'user', 'show', 'nova'): check('openstack', 'user', 'create', '--domain', 'default', '--password', 'nova', 'nova') check('openstack', 'role', 'add', '--project', 'service', '--user', 'nova', 'admin') if not call('openstack', 'user', 'show', 'placement'): check('openstack', 'user', 'create', '--domain', 'default', '--password', 'placement', 'placement') check('openstack', 'role', 'add', '--project', 'service', '--user', 'placement', 'admin') if not call('openstack', 'service', 'show', 'compute'): check('openstack', 'service', 'create', '--name', 'nova', '--description', '"Openstack Compute"', 'compute') for endpoint in ['public', 'internal', 'admin']: call('openstack', 'endpoint', 'create', '--region', 'microstack', 'compute', endpoint, 'http://{extgateway}:8774/v2.1'.format(**_env)) if not call('openstack', 'service', 'show', 'placement'): check('openstack', 'service', 'create', '--name', 'placement', '--description', '"Placement API"', 'placement') for endpoint in ['public', 'internal', 'admin']: call('openstack', 'endpoint', 'create', '--region', 'microstack', 'placement', endpoint, 'http://{extgateway}:8778'.format(**_env)) # Grant nova user access to cell0 sql( "GRANT ALL PRIVILEGES ON nova_cell0.* TO 'nova'@'{extgateway}' \ IDENTIFIED BY \'nova';".format(**_env)) check('snap-openstack', 'launch', 'nova-manage', 'api_db', 'sync') if 'cell0' not in check_output('snap-openstack', 'launch', 'nova-manage', 'cell_v2', 'list_cells'): check('snap-openstack', 'launch', 'nova-manage', 'cell_v2', 'map_cell0') if 'cell1' not in check_output('snap-openstack', 'launch', 'nova-manage', 'cell_v2', 'list_cells'): check('snap-openstack', 'launch', 'nova-manage', 'cell_v2', 'create_cell', '--name=cell1', '--verbose') check('snap-openstack', 'launch', 'nova-manage', 'db', 'sync') restart('nova-*') nc_wait(_env['extgateway'], '8774') sleep(5) # TODO: log_wait log.info('Creating default flavors...') self._flavors()
def yes(self, answer): log.info('Configuring networking ...') role = check_output('snapctl', 'get', 'config.cluster.role') # Enable and start the services. enable('ovsdb-server') enable('ovs-vswitchd') enable('ovn-ovsdb-server-sb') enable('ovn-ovsdb-server-nb') network.ExtGateway().ask() network.ExtCidr().ask() control_ip = check_output('snapctl', 'get', 'config.network.control-ip') if role == 'control': nb_conn = 'unix:{SNAP_COMMON}/run/ovn/ovnnb_db.sock'.format(**_env) sb_conn = 'unix:{SNAP_COMMON}/run/ovn/ovnsb_db.sock'.format(**_env) check_output('ovs-vsctl', 'set', 'open', '.', f'external-ids:ovn-encap-ip={control_ip}') elif role == 'compute': sb_conn = f'tcp:{control_ip}:6642' # Not used by any compute node services. nb_conn = '' compute_ip = check_output('snapctl', 'get', 'config.network.compute-ip') # Set the IP address to be used for a tunnel endpoint. check_output('ovs-vsctl', 'set', 'open', '.', f'external-ids:ovn-encap-ip={compute_ip}') else: raise Exception(f'Unexpected node role: {role}') # ovn-controller does not start unless both the ovn-encap-ip and the # ovn-encap-type are set. check_output('ovs-vsctl', 'set', 'open', '.', 'external-ids:ovn-encap-type=geneve') # Configure OVN SB and NB sockets based on the role node. For # single-node deployments there is no need to use a TCP socket. check_output('snapctl', 'set', f'config.network.ovn-nb-connection={nb_conn}') check_output('snapctl', 'set', f'config.network.ovn-sb-connection={sb_conn}') # Set SB database connection details for ovn-controller to pick up. check_output('ovs-vsctl', 'set', 'open', '.', f'external-ids:ovn-remote={sb_conn}') check_output('ovs-vsctl', 'set', 'open', '.', 'external-ids:ovn-cms-options=enable-chassis-as-gw') # Now that we have default or overriden values, setup the # bridge and write all the proper values into our config # files. check('setup-br-ex') check('snap-openstack', 'setup') if role == 'control': enable('ovn-northd') enable('ovn-controller') network.IpForwarding().ask()
def yes(self, answer: str) -> None: log.info('Configuring nova ...') if not call('openstack', 'user', 'show', 'nova'): check('openstack', 'user', 'create', '--domain', 'default', '--password', 'nova', 'nova') check('openstack', 'role', 'add', '--project', 'service', '--user', 'nova', 'admin') if not call('openstack', 'user', 'show', 'placement'): check('openstack', 'user', 'create', '--domain', 'default', '--password', 'placement', 'placement') check('openstack', 'role', 'add', '--project', 'service', '--user', 'placement', 'admin') if not call('openstack', 'service', 'show', 'compute'): check('openstack', 'service', 'create', '--name', 'nova', '--description', '"Openstack Compute"', 'compute') for endpoint in ['public', 'internal', 'admin']: call('openstack', 'endpoint', 'create', '--region', 'microstack', 'compute', endpoint, 'http://{extgateway}:8774/v2.1'.format(**_env)) if not call('openstack', 'service', 'show', 'placement'): check('openstack', 'service', 'create', '--name', 'placement', '--description', '"Placement API"', 'placement') for endpoint in ['public', 'internal', 'admin']: call('openstack', 'endpoint', 'create', '--region', 'microstack', 'placement', endpoint, 'http://{extgateway}:8778'.format(**_env)) # Grant nova user access to cell0 sql("GRANT ALL PRIVILEGES ON nova_cell0.* TO 'nova'@'{extgateway}' \ IDENTIFIED BY \'nova';".format(**_env)) # Use snapctl to start nova services. We need to call them # out manually, because systemd doesn't know about them yet. # TODO: parse the output of `snapctl services` to get this # list automagically. for service in [ environ['SNAP_INSTANCE_NAME'] + '.nova-api', environ['SNAP_INSTANCE_NAME'] + '.nova-api-metadata', environ['SNAP_INSTANCE_NAME'] + '.nova-compute', environ['SNAP_INSTANCE_NAME'] + '.nova-conductor', environ['SNAP_INSTANCE_NAME'] + '.nova-scheduler', environ['SNAP_INSTANCE_NAME'] + '.nova-uwsgi', ]: check('snapctl', 'start', service) check('snap-openstack', 'launch', 'nova-manage', 'api_db', 'sync') if 'cell0' not in check_output('snap-openstack', 'launch', 'nova-manage', 'cell_v2', 'list_cells'): check('snap-openstack', 'launch', 'nova-manage', 'cell_v2', 'map_cell0') if 'cell1' not in check_output('snap-openstack', 'launch', 'nova-manage', 'cell_v2', 'list_cells'): check('snap-openstack', 'launch', 'nova-manage', 'cell_v2', 'create_cell', '--name=cell1', '--verbose') check('snap-openstack', 'launch', 'nova-manage', 'db', 'sync') restart('nova-*') nc_wait(_env['extgateway'], '8774') sleep(5) # TODO: log_wait log.info('Creating default flavors...') self._flavors()