Ejemplo n.º 1
0
    def ask(self):
        # If we are a control node, skip this question.
        role = config_get(Role.config_key)
        if role == 'control':
            ip = config_get(ControlIp.config_key)
            config_set(**{self.config_key: ip})
            return

        return super().ask()
Ejemplo n.º 2
0
def generate_selfsigned():
    """Generate a self-signed certificate with associated keys.

    The certificate will have a fake CNAME and subjAltName since
    the expectation is that this certificate will only be used by
    clients that know its fingerprint and will not use a validation
    via a CA certificate and hostname. This approach is similar to
    Certificate Pinning, however, here a certificate is not embedded
    into the application but is generated on initialization at one
    node and its fingerprint is copied in a token to another node
    via a secure channel.
    https://owasp.org/www-community/controls/Certificate_and_Public_Key_Pinning
    """
    cert_path, key_path = (
        Path(shell.config_get('config.cluster.tls-cert-path')),
        Path(shell.config_get('config.cluster.tls-key-path')),
    )
    # Do not generate a new certificate and key if there is already an existing
    # pair. TODO: improve this check and allow renewal.
    if cert_path.exists() and key_path.exists():
        return

    dummy_cn = 'microstack.run'
    key = rsa.generate_private_key(
        public_exponent=65537,
        key_size=2048,
        backend=default_backend(),
    )
    common_name = x509.Name(
        [x509.NameAttribute(NameOID.COMMON_NAME, dummy_cn)])
    san = x509.SubjectAlternativeName([x509.DNSName(dummy_cn)])
    basic_contraints = x509.BasicConstraints(ca=True, path_length=0)
    now = datetime.utcnow()
    cert = (x509.CertificateBuilder().subject_name(common_name).issuer_name(
        common_name).public_key(key.public_key()).serial_number(
            x509.random_serial_number()).not_valid_before(now).not_valid_after(
                now + relativedelta(years=10)).add_extension(
                    basic_contraints,
                    False).add_extension(san,
                                         False).sign(key, hashes.SHA256(),
                                                     default_backend()))

    cert_fprint = cert.fingerprint(hashes.SHA256()).hex()
    shell.config_set(**{'config.cluster.fingerprint': cert_fprint})

    serialized_cert = cert.public_bytes(encoding=serialization.Encoding.PEM)
    serialized_key = key.private_bytes(
        encoding=serialization.Encoding.PEM,
        format=serialization.PrivateFormat.PKCS8,
        encryption_algorithm=serialization.NoEncryption(),
    )
    cert_path.write_bytes(serialized_cert)
    key_path.write_bytes(serialized_key)
Ejemplo n.º 3
0
    def yes(self, answer: str) -> None:
        log.info('Configuring the Placement service...')

        if not call('openstack', 'user', 'show', 'placement'):
            check(
                'openstack',
                'user',
                'create',
                '--domain',
                'default',
                '--password',
                shell.config_get('config.credentials.placement-password'),
                'placement',
            )
            check('openstack', 'role', 'add', '--project', 'service', '--user',
                  'placement', 'admin')

        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://{control_ip}:8778'.format(**_env))

        log.info('Running Placement DB migrations...')
        check('snap-openstack', 'launch', 'placement-manage', 'db', 'sync')
        enable('placement-uwsgi')
Ejemplo n.º 4
0
 def ask(self):
     # Skip this question for a compute node since the control IP
     # address is taken from the connection string instead.
     role = config_get(Role.config_key)
     if role == 'compute':
         return
     return super().ask()
Ejemplo n.º 5
0
    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')
Ejemplo n.º 6
0
 def ask(self):
     # Skip this question for a control node since we are not connecting
     # to ourselves.
     role = config_get(Role.config_key)
     if role == 'control':
         return
     return super().ask()
Ejemplo n.º 7
0
    def yes(self, answer: str) -> None:

        log.info('Configuring Glance ...')

        if not call('openstack', 'user', 'show', 'glance'):
            check('openstack', 'user', 'create', '--domain', 'default',
                  '--password',
                  shell.config_get('config.credentials.glance-password'),
                  'glance')
            check('openstack', 'role', 'add', '--project', 'service', '--user',
                  'glance', 'admin')

        if not call('openstack', 'service', 'show', 'image'):
            check('openstack', 'service', 'create', '--name', 'glance',
                  '--description', '"OpenStack Image"', 'image')
            for endpoint in ['internal', 'admin', 'public']:
                check('openstack', 'endpoint', 'create', '--region',
                      'microstack', 'image', endpoint,
                      'http://{compute_ip}:9292'.format(**_env))

        check('snap-openstack', 'launch', 'glance-manage', 'db_sync')
        # TODO: remove the glance registry
        # https://blueprints.launchpad.net/glance/+spec/deprecate-registry
        for service in [
                'glance-api',
                'registry',
        ]:
            enable(service)

        nc_wait(_env['compute_ip'], '9292')

        sleep(5)  # TODO: log_wait

        self._fetch_cirros()
Ejemplo n.º 8
0
 def yes(self, answer):
     clustered = config_get('config.is-clustered')
     if not clustered:
         ip_dict = {
             'config.network.control-ip': answer,
             'config.network.compute-ip': answer,
         }
         config_set(**ip_dict)
         _env.update(ip_dict)
     else:
         ip_dict = config_get(
             *['config.network.control-ip', 'config.network.compute-ip'])
         _env.update({
             'control_ip': ip_dict['config.network.control-ip'],
             'compute_ip': ip_dict['config.network.compute-ip'],
         })
Ejemplo n.º 9
0
    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()
Ejemplo n.º 10
0
    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')
Ejemplo n.º 11
0
    def yes(self, answer: str) -> None:
        log.info('Configuring Neutron')

        if not call('openstack', 'user', 'show', 'neutron'):
            check('openstack', 'user', 'create', '--domain', 'default',
                  '--password',
                  shell.config_get('config.credentials.neutron-password'),
                  'neutron')
            check('openstack', 'role', 'add', '--project', 'service', '--user',
                  'neutron', 'admin')

        if not call('openstack', 'service', 'show', 'network'):
            check('openstack', 'service', 'create', '--name', 'neutron',
                  '--description', '"OpenStack Network"', 'network')
            for endpoint in ['public', 'internal', 'admin']:
                call('openstack', 'endpoint', 'create', '--region',
                     'microstack', 'network', endpoint,
                     'http://{control_ip}:9696'.format(**_env))

        check('snap-openstack', 'launch', 'neutron-db-manage', 'upgrade',
              'head')
        enable('neutron-api')
        enable('neutron-ovn-metadata-agent')

        nc_wait(_env['control_ip'], '9696')

        sleep(5)  # TODO: log_wait

        if not call('openstack', 'network', 'show', 'test'):
            check('openstack', 'network', 'create', 'test')

        if not call('openstack', 'subnet', 'show', 'test-subnet'):
            check('openstack', 'subnet', 'create', '--network', 'test',
                  '--subnet-range', '192.168.222.0/24', 'test-subnet')

        if not call('openstack', 'network', 'show', 'external'):
            check('openstack', 'network', 'create', '--external',
                  '--provider-physical-network=physnet1',
                  '--provider-network-type=flat', 'external')
        if not call('openstack', 'subnet', 'show', 'external-subnet'):
            check('openstack', 'subnet', 'create', '--network', 'external',
                  '--subnet-range', _env['extcidr'], '--no-dhcp',
                  'external-subnet')

        if not call('openstack', 'router', 'show', 'test-router'):
            check('openstack', 'router', 'create', 'test-router')
            check('openstack', 'router', 'add', 'subnet', 'test-router',
                  'test-subnet')
            check('openstack', 'router', 'set', '--external-gateway',
                  'external', 'test-router')
Ejemplo n.º 12
0
 def _create_dbs(self) -> None:
     db_creds = shell.config_get('config.credentials')
     for service_user, db_name in (('neutron', 'neutron'), ('nova', 'nova'),
                                   ('nova', 'nova_api'), ('nova',
                                                          'nova_cell0'),
                                   ('cinder', 'cinder'), ('glance',
                                                          'glance'),
                                   ('keystone', 'keystone'), ('placement',
                                                              'placement')):
         db_password = db_creds[f'{service_user}-password']
         sql("CREATE USER IF NOT EXISTS '{user}'@'%'"
             " IDENTIFIED BY '{db_password}';".format(
                 user=service_user, db_password=db_password, **_env))
         sql("CREATE DATABASE IF NOT EXISTS `{db}`;".format(db=db_name))
         sql("GRANT ALL PRIVILEGES ON {db}.* TO '{user}'@'%';"
             "".format(db=db_name, user=service_user, **_env))
Ejemplo n.º 13
0
    def yes(self, answer: str) -> None:
        log.info('restarting libvirt and virtlogd ...')
        # This fixes an issue w/ logging not getting set.
        # TODO: fix issue.
        restart('libvirtd')
        restart('virtlogd')
        restart('nova-compute')

        role = shell.config_get('config.cluster.role')
        if role == 'control':
            # TODO: since snap-openstack launch is used, this depends on the
            # database readiness and hence the clustering service is enabled
            # and started here. There needs to be a better way to do this.
            enable('cluster-uwsgi')
            enable('horizon-uwsgi')

        check('snapctl', 'set', 'initialized=true')
        log.info('Complete. Marked microstack as initialized!')
Ejemplo n.º 14
0
def _setup_secrets():
    # If a user runs init multiple times we do not want to generate
    # new credentials to keep the init operation idempotent.
    existing_creds = shell.config_get('config.credentials')
    if isinstance(existing_creds, dict):
        existing_cred_keys = existing_creds.keys()
    else:
        existing_cred_keys = []
    shell.config_set(
        **{
            f'config.credentials.{k}': credentials.generate_password()
            for k in [
                'mysql-root-password',
                'rabbitmq-password',
                'keystone-password',
                'nova-password',
                'cinder-password',
                'neutron-password',
                'placement-password',
                'glance-password',
                'ovn-metadata-proxy-shared-secret',
            ] if k not in existing_cred_keys
        })
Ejemplo n.º 15
0
    def _load(self):
        role = config_get(Role.config_key)
        if role == 'compute':
            return fetch_ip_address() or super().load()

        return super()._load()
Ejemplo n.º 16
0
    def _load(self):
        role = config_get(Role.config_key)
        if role == 'compute':
            return default_source_address() or super().load()

        return super()._load()
Ejemplo n.º 17
0
 def _load(self):
     if config_get(Role.config_key) == 'control':
         return default_source_address() or super()._load()
     return super()._load()
Ejemplo n.º 18
0
 def _load(self):
     if config_get(Role.config_key) == 'control':
         return fetch_ip_address() or super()._load()
     return super()._load()