def __init__(self, context): self.context = context self.client = None params = { 'timeout': CONF.neutron.url_timeout, 'insecure': CONF.keystone_authtoken.insecure, 'ca_cert': CONF.keystone_authtoken.certfile, } if CONF.neutron.auth_strategy not in ['noauth', 'keystone']: raise exception.ConfigInvalid(_('Neutron auth_strategy should be ' 'either "noauth" or "keystone".')) if CONF.neutron.auth_strategy == 'noauth': params['endpoint_url'] = CONF.neutron.url params['auth_strategy'] = 'noauth' elif (CONF.neutron.auth_strategy == 'keystone' and context.auth_token is None): params['endpoint_url'] = (CONF.neutron.url or keystone.get_service_url('neutron')) params['username'] = CONF.keystone_authtoken.admin_user params['tenant_name'] = CONF.keystone_authtoken.admin_tenant_name params['password'] = CONF.keystone_authtoken.admin_password params['auth_url'] = (CONF.keystone_authtoken.auth_uri or '') else: params['token'] = context.auth_token params['endpoint_url'] = CONF.neutron.url params['auth_strategy'] = None self.client = clientv20.Client(**params)
def _build_client(token=None): """Utility function to create Neutron client.""" params = { 'timeout': CONF.neutron.url_timeout, 'retries': CONF.neutron.retries, 'insecure': CONF.keystone_authtoken.insecure, 'ca_cert': CONF.keystone_authtoken.certfile, } if CONF.neutron.auth_strategy not in ['noauth', 'keystone']: raise exception.ConfigInvalid(_('Neutron auth_strategy should be ' 'either "noauth" or "keystone".')) if CONF.neutron.auth_strategy == 'noauth': params['endpoint_url'] = CONF.neutron.url params['auth_strategy'] = 'noauth' elif (CONF.neutron.auth_strategy == 'keystone' and token is None): params['endpoint_url'] = (CONF.neutron.url or keystone.get_service_url('neutron')) params['username'] = CONF.keystone_authtoken.admin_user params['tenant_name'] = CONF.keystone_authtoken.admin_tenant_name params['password'] = CONF.keystone_authtoken.admin_password params['auth_url'] = (CONF.keystone_authtoken.auth_uri or '') if CONF.keystone.region_name: params['region_name'] = CONF.keystone.region_name else: params['token'] = token params['endpoint_url'] = CONF.neutron.url params['auth_strategy'] = None return clientv20.Client(**params)
def get_session(group): auth = ironic_auth.load_auth(CONF, group) or _get_legacy_auth() if not auth: msg = _("Failed to load auth from either [%(new)s] or [%(old)s] " "config sections.") raise exception.ConfigInvalid(message=msg, new=group, old=ironic_auth.LEGACY_SECTION) session = kaloading.load_session_from_conf_options( CONF, group, auth=auth) return session
def __init__(self, app, conf, public_api_routes=[]): route_pattern_tpl = '%s(\.json|\.xml)?$' try: self.public_api_routes = [re.compile(route_pattern_tpl % route_tpl) for route_tpl in public_api_routes] except re.error as e: msg = _('Cannot compile public API routes: %s') % e LOG.error(msg) raise exception.ConfigInvalid(error_msg=msg) super(AuthTokenMiddleware, self).__init__(app, conf)
def __init__(self, app, conf, public_api_routes=None): api_routes = [] if public_api_routes is None else public_api_routes self._ironic_app = app # TODO(mrda): Remove .xml and ensure that doesn't result in a # 401 Authentication Required instead of 404 Not Found route_pattern_tpl = '%s(\.json|\.xml)?$' try: self.public_api_routes = [re.compile(route_pattern_tpl % route_tpl) for route_tpl in api_routes] except re.error as e: raise exception.ConfigInvalid( error_msg=_('Cannot compile public API routes: %s') % e) super(AuthTokenMiddleware, self).__init__(app, conf)
def wrapper(*args, **kwargs): try: return f(*args, **kwargs) except ks_exception.EndpointNotFound: service_type = kwargs.get('service_type', 'baremetal') endpoint_type = kwargs.get('endpoint_type', 'internal') raise exception.CatalogNotFound(service_type=service_type, endpoint_type=endpoint_type) except (ks_exception.Unauthorized, ks_exception.AuthorizationFailure): raise exception.KeystoneUnauthorized() except (ks_exception.NoMatchingPlugin, ks_exception.MissingRequiredOptions) as e: raise exception.ConfigInvalid(str(e)) except Exception as e: LOG.exception('Keystone request failed: %(msg)s', {'msg': str(e)}) raise exception.KeystoneFailure(str(e))
def _check_enabled_interfaces(): """Sanity-check enabled_*_interfaces configs. We do this before we even bother to try to load up drivers. If we have any dynamic drivers enabled, then we need interfaces enabled as well. :raises: ConfigInvalid if an enabled interfaces config option is empty. """ empty_confs = [] iface_types = ['enabled_%s_interfaces' % i for i in driver_base.ALL_INTERFACES] for iface_type in iface_types: conf_value = getattr(CONF, iface_type) if not conf_value: empty_confs.append(iface_type) if empty_confs: msg = (_('Configuration options %s cannot be an empty list.') % ', '.join(empty_confs)) raise exception.ConfigInvalid(error_msg=msg)
def __init__(self, name, use_ssl=False): """Initialize, but do not start the WSGI server. :param name: The name of the WSGI server given to the loader. :param use_ssl: Wraps the socket in an SSL context if True. :returns: None """ self.name = name self.app = app.VersionSelectorApplication() self.workers = (CONF.api.api_workers or processutils.get_worker_count()) if self.workers and self.workers < 1: raise exception.ConfigInvalid( _("api_workers value of %d is invalid, " "must be greater than 0.") % self.workers) self.server = wsgi.Server(CONF, name, self.app, host=CONF.api.host_ip, port=CONF.api.port, use_ssl=use_ssl)
def __init__(self, name, use_ssl=False): """Initialize, but do not start the WSGI server. :param name: The name of the WSGI server given to the loader. :param use_ssl: Wraps the socket in an SSL context if True. :returns: None """ self.name = name self.app = app.VersionSelectorApplication() self.workers = ( CONF.api.api_workers # NOTE(dtantsur): each worker takes a substantial amount of memory, # so we don't want to end up with dozens of them. or min(processutils.get_worker_count(), _MAX_DEFAULT_WORKERS) ) if self.workers and self.workers < 1: raise exception.ConfigInvalid( _("api_workers value of %d is invalid, " "must be greater than 0.") % self.workers) self.server = wsgi.Server(CONF, name, self.app, host=CONF.api.host_ip, port=CONF.api.port, use_ssl=use_ssl)
def init_host(self, admin_context=None): """Initialize the conductor host. :param admin_context: the admin context to pass to periodic tasks. :raises: RuntimeError when conductor is already running. :raises: NoDriversLoaded when no drivers are enabled on the conductor. :raises: DriverNotFound if a driver is enabled that does not exist. :raises: DriverLoadError if an enabled driver cannot be loaded. :raises: DriverNameConflict if a classic driver and a dynamic driver are both enabled and have the same name. :raises: ConfigInvalid if required config options for connection with radosgw are missing while storing config drive. """ if self._started: raise RuntimeError( _('Attempt to start an already running ' 'conductor manager')) self._shutdown = False self.dbapi = dbapi.get_instance() self._keepalive_evt = threading.Event() """Event for the keepalive thread.""" # TODO(dtantsur): make the threshold configurable? rejection_func = rejection.reject_when_reached( CONF.conductor.workers_pool_size) self._executor = futurist.GreenThreadPoolExecutor( max_workers=CONF.conductor.workers_pool_size, check_and_reject=rejection_func) """Executor for performing tasks async.""" self.ring_manager = hash_ring.HashRingManager() """Consistent hash ring which maps drivers to conductors.""" _check_enabled_interfaces() # NOTE(deva): these calls may raise DriverLoadError or DriverNotFound # NOTE(vdrok): Instantiate network and storage interface factory on # startup so that all the interfaces are loaded at the very # beginning, and failures prevent the conductor from starting. drivers = driver_factory.drivers() hardware_types = driver_factory.hardware_types() driver_factory.NetworkInterfaceFactory() driver_factory.StorageInterfaceFactory() # NOTE(jroll) this is passed to the dbapi, which requires a list, not # a generator (which keys() returns in py3) driver_names = list(drivers) hardware_type_names = list(hardware_types) # check that at least one driver is loaded, whether classic or dynamic if not driver_names and not hardware_type_names: msg = ("Conductor %s cannot be started because no drivers " "were loaded. This could be because no classic drivers " "were specified in the 'enabled_drivers' config option " "and no dynamic drivers were specified in the " "'enabled_hardware_types' config option.") LOG.error(msg, self.host) raise exception.NoDriversLoaded(conductor=self.host) # check for name clashes between classic and dynamic drivers name_clashes = set(driver_names).intersection(hardware_type_names) if name_clashes: name_clashes = ', '.join(name_clashes) msg = ("Conductor %(host)s cannot be started because there is " "one or more name conflicts between classic drivers and " "dynamic drivers (%(names)s). Check any external driver " "plugins and the 'enabled_drivers' and " "'enabled_hardware_types' config options.") LOG.error(msg, {'host': self.host, 'names': name_clashes}) raise exception.DriverNameConflict(names=name_clashes) # Collect driver-specific periodic tasks. # Conductor periodic tasks accept context argument, driver periodic # tasks accept this manager and context. We have to ensure that the # same driver interface class is not traversed twice, otherwise # we'll have several instances of the same task. LOG.debug('Collecting periodic tasks') self._periodic_task_callables = [] periodic_task_classes = set() self._collect_periodic_tasks(self, (admin_context, )) for driver_obj in drivers.values(): for iface_name in driver_obj.all_interfaces: iface = getattr(driver_obj, iface_name, None) if iface and iface.__class__ not in periodic_task_classes: self._collect_periodic_tasks(iface, (self, admin_context)) periodic_task_classes.add(iface.__class__) if (len(self._periodic_task_callables) > CONF.conductor.workers_pool_size): LOG.warning( 'This conductor has %(tasks)d periodic tasks ' 'enabled, but only %(workers)d task workers ' 'allowed by [conductor]workers_pool_size option', { 'tasks': len(self._periodic_task_callables), 'workers': CONF.conductor.workers_pool_size }) self._periodic_tasks = periodics.PeriodicWorker( self._periodic_task_callables, executor_factory=periodics.ExistingExecutor(self._executor)) # Check for required config options if object_store_endpoint_type is # radosgw if (CONF.deploy.configdrive_use_object_store and CONF.deploy.object_store_endpoint_type == "radosgw"): if (None in (CONF.swift.auth_url, CONF.swift.username, CONF.swift.password)): msg = _("Parameters missing to make a connection with " "radosgw. Ensure that [swift]/auth_url, " "[swift]/username, and [swift]/password are all " "configured.") raise exception.ConfigInvalid(msg) # clear all target_power_state with locks by this conductor self.dbapi.clear_node_target_power_state(self.host) # clear all locks held by this conductor before registering self.dbapi.clear_node_reservations_for_conductor(self.host) try: # Register this conductor with the cluster self.conductor = objects.Conductor.register( admin_context, self.host, driver_names) except exception.ConductorAlreadyRegistered: # This conductor was already registered and did not shut down # properly, so log a warning and update the record. LOG.warning( "A conductor with hostname %(hostname)s was " "previously registered. Updating registration", {'hostname': self.host}) self.conductor = objects.Conductor.register(admin_context, self.host, driver_names, update_existing=True) # register hardware types and interfaces supported by this conductor # and validate them against other conductors try: self._register_and_validate_hardware_interfaces(hardware_types) except (exception.DriverLoadError, exception.DriverNotFound, exception.ConductorHardwareInterfacesAlreadyRegistered, exception.InterfaceNotFoundInEntrypoint, exception.NoValidDefaultForInterface) as e: with excutils.save_and_reraise_exception(): LOG.error('Failed to register hardware types. %s', e) self.del_host() # Start periodic tasks self._periodic_tasks_worker = self._executor.submit( self._periodic_tasks.start, allow_empty=True) self._periodic_tasks_worker.add_done_callback( self._on_periodic_tasks_stop) # NOTE(lucasagomes): If the conductor server dies abruptly # mid deployment (OMM Killer, power outage, etc...) we # can not resume the deployment even if the conductor # comes back online. Cleaning the reservation of the nodes # (dbapi.clear_node_reservations_for_conductor) is not enough to # unstick it, so let's gracefully fail the deployment so the node # can go through the steps (deleting & cleaning) to make itself # available again. filters = {'reserved': False, 'provision_state': states.DEPLOYING} last_error = (_("The deployment can't be resumed by conductor " "%s. Moving to fail state.") % self.host) self._fail_if_in_state(ironic_context.get_admin_context(), filters, states.DEPLOYING, 'provision_updated_at', last_error=last_error) # Start consoles if it set enabled in a greenthread. try: self._spawn_worker(self._start_consoles, ironic_context.get_admin_context()) except exception.NoFreeConductorWorker: LOG.warning('Failed to start worker for restarting consoles.') # Spawn a dedicated greenthread for the keepalive try: self._spawn_worker(self._conductor_service_record_keepalive) LOG.info( 'Successfully started conductor with hostname ' '%(hostname)s.', {'hostname': self.host}) except exception.NoFreeConductorWorker: with excutils.save_and_reraise_exception(): LOG.critical('Failed to start keepalive') self.del_host() self._started = True
def init_host(self, admin_context=None): """Initialize the conductor host. :param admin_context: the admin context to pass to periodic tasks. :raises: RuntimeError when conductor is already running. :raises: NoDriversLoaded when no drivers are enabled on the conductor. :raises: DriverNotFound if a driver is enabled that does not exist. :raises: DriverLoadError if an enabled driver cannot be loaded. :raises: DriverNameConflict if a classic driver and a dynamic driver are both enabled and have the same name. :raises: ConfigInvalid if required config options for connection with radosgw are missing while storing config drive. """ if self._started: raise RuntimeError(_('Attempt to start an already running ' 'conductor manager')) self._shutdown = False self.dbapi = dbapi.get_instance() self._keepalive_evt = threading.Event() """Event for the keepalive thread.""" # TODO(dtantsur): make the threshold configurable? rejection_func = rejection.reject_when_reached( CONF.conductor.workers_pool_size) self._executor = futurist.GreenThreadPoolExecutor( max_workers=CONF.conductor.workers_pool_size, check_and_reject=rejection_func) """Executor for performing tasks async.""" self.ring_manager = hash_ring.HashRingManager() """Consistent hash ring which maps drivers to conductors.""" # TODO(dtantsur): remove in Stein if CONF.enabled_drivers: raise RuntimeError("The enabled_drivers configuration option " "no longer has any effect and must be empty") _check_enabled_interfaces() # NOTE(deva): these calls may raise DriverLoadError or DriverNotFound # NOTE(vdrok): Instantiate network and storage interface factory on # startup so that all the interfaces are loaded at the very # beginning, and failures prevent the conductor from starting. hardware_types = driver_factory.hardware_types() driver_factory.NetworkInterfaceFactory() driver_factory.StorageInterfaceFactory() # NOTE(jroll) this is passed to the dbapi, which requires a list, not # a generator (which keys() returns in py3) hardware_type_names = list(hardware_types) # check that at least one driver is loaded, whether classic or dynamic if not hardware_type_names: msg = ("Conductor %s cannot be started because no hardware types " "were specified in the 'enabled_hardware_types' config " "option.") LOG.error(msg, self.host) raise exception.NoDriversLoaded(conductor=self.host) self._collect_periodic_tasks(admin_context) # Check for required config options if object_store_endpoint_type is # radosgw if (CONF.deploy.configdrive_use_object_store and CONF.deploy.object_store_endpoint_type == "radosgw"): if (None in (CONF.swift.auth_url, CONF.swift.username, CONF.swift.password)): msg = _("Parameters missing to make a connection with " "radosgw. Ensure that [swift]/auth_url, " "[swift]/username, and [swift]/password are all " "configured.") raise exception.ConfigInvalid(msg) # clear all target_power_state with locks by this conductor self.dbapi.clear_node_target_power_state(self.host) # clear all locks held by this conductor before registering self.dbapi.clear_node_reservations_for_conductor(self.host) try: # Register this conductor with the cluster self.conductor = objects.Conductor.register( admin_context, self.host, hardware_type_names) except exception.ConductorAlreadyRegistered: # This conductor was already registered and did not shut down # properly, so log a warning and update the record. LOG.warning("A conductor with hostname %(hostname)s was " "previously registered. Updating registration", {'hostname': self.host}) self.conductor = objects.Conductor.register( admin_context, self.host, hardware_type_names, update_existing=True) # register hardware types and interfaces supported by this conductor # and validate them against other conductors try: self._register_and_validate_hardware_interfaces(hardware_types) except (exception.DriverLoadError, exception.DriverNotFound, exception.ConductorHardwareInterfacesAlreadyRegistered, exception.InterfaceNotFoundInEntrypoint, exception.NoValidDefaultForInterface) as e: with excutils.save_and_reraise_exception(): LOG.error('Failed to register hardware types. %s', e) self.del_host() # Start periodic tasks self._periodic_tasks_worker = self._executor.submit( self._periodic_tasks.start, allow_empty=True) self._periodic_tasks_worker.add_done_callback( self._on_periodic_tasks_stop) self._fail_transient_state( states.DEPLOYING, _("The deployment can't be resumed by conductor " "%s. Moving to fail state.") % self.host) self._fail_transient_state( states.CLEANING, _("The cleaning can't be resumed by conductor " "%s. Moving to fail state.") % self.host) # Start consoles if it set enabled in a greenthread. try: self._spawn_worker(self._start_consoles, ironic_context.get_admin_context()) except exception.NoFreeConductorWorker: LOG.warning('Failed to start worker for restarting consoles.') # Spawn a dedicated greenthread for the keepalive try: self._spawn_worker(self._conductor_service_record_keepalive) LOG.info('Successfully started conductor with hostname ' '%(hostname)s.', {'hostname': self.host}) except exception.NoFreeConductorWorker: with excutils.save_and_reraise_exception(): LOG.critical('Failed to start keepalive') self.del_host() self._started = True