def add_command_parsers(subparsers): command_object = DBCommand() parser = subparsers.add_parser( 'upgrade', help=_("Upgrade the database schema to the latest version. " "Optionally, use --revision to specify an alembic revision " "string to upgrade to.")) parser.set_defaults(func=command_object.upgrade) parser.add_argument('--revision', nargs='?') parser = subparsers.add_parser('stamp') parser.add_argument('--revision', nargs='?') parser.set_defaults(func=command_object.stamp) parser = subparsers.add_parser( 'revision', help=_("Create a new alembic revision. " "Use --message to set the message string.")) parser.add_argument('-m', '--message') parser.add_argument('--autogenerate', action='store_true') parser.set_defaults(func=command_object.revision) parser = subparsers.add_parser( 'version', help=_("Print the current version information and exit.")) parser.set_defaults(func=command_object.version) parser = subparsers.add_parser('create_schema', help=_("Create the database schema.")) parser.set_defaults(func=command_object.create_schema)
def _wait_for_active(self, server): """Wait for the node to be marked as ACTIVE in Ironic.""" server.refresh() if server.status in (states.DELETING, states.ERROR, states.DELETED): raise exception.ServerDeployAborted( _("Server %s provisioning was aborted") % server.uuid) node = self._validate_server_and_node(server) if node.provision_state == ironic_states.ACTIVE: # job is done LOG.debug("Ironic node %(node)s is now ACTIVE", dict(node=node.uuid), server=server) raise loopingcall.LoopingCallDone() if node.target_provision_state in (ironic_states.DELETED, ironic_states.AVAILABLE): # ironic is trying to delete it now raise exception.ServerNotFound(server=server.uuid) if node.provision_state in (ironic_states.NOSTATE, ironic_states.AVAILABLE): # ironic already deleted it raise exception.ServerNotFound(server=server.uuid) if node.provision_state == ironic_states.DEPLOYFAIL: # ironic failed to deploy msg = (_("Failed to provision server %(server)s: %(reason)s") % { 'server': server.uuid, 'reason': node.last_error }) raise exception.ServerDeployFailure(msg) _log_ironic_polling('become ACTIVE', node, server)
def check_string_length(value, name=None, min_length=0, max_length=None): """Check the length of specified string :param value: the value of the string :param name: the name of the string :param min_length: the min_length of the string :param max_length: the max_length of the string """ if not isinstance(value, six.string_types): if name is None: msg = "The input is not a string or unicode" else: msg = "%s is not a string or unicode" % name raise exception.Invalid(message=msg) if name is None: name = value if len(value) < min_length: msg = _("%(name)s has a minimum character requirement of " "%(min_length)s.") % { 'name': name, 'min_length': min_length } raise exception.Invalid(message=msg) if max_length and len(value) > max_length: msg = _("%(name)s has more than %(max_length)s " "characters.") % { 'name': name, 'max_length': max_length } raise exception.Invalid(message=msg)
def _build_networks(self, context, server, requested_networks): # TODO(zhenguo): This seems not needed as our scheduler has already # guaranteed this. ports = self.manager.driver.get_ports_from_node(server.node_uuid) if len(requested_networks) > len(ports): raise exception.InterfacePlugException( _("Ironic node: %(id)s virtual to physical interface count" " mismatch" " (Vif count: %(vif_count)d, Pif count: %(pif_count)d)") % { 'id': server.node_uuid, 'vif_count': len(requested_networks), 'pif_count': len(ports) }) nics_obj = objects.ServerNics(context) for vif in requested_networks: try: if vif.get('net_id'): port = self.manager.network_api.create_port( context, vif['net_id'], server.uuid) port_dict = port['port'] elif vif.get('port_id'): port_dict = self.manager.network_api.show_port( context, vif.get('port_id')) nic_dict = { 'port_id': port_dict['id'], 'network_id': port_dict['network_id'], 'mac_address': port_dict['mac_address'], 'fixed_ips': port_dict['fixed_ips'], 'server_uuid': server.uuid } server_nic = objects.ServerNic(context, **nic_dict) nics_obj.objects.append(server_nic) self.manager.driver.plug_vif(server.node_uuid, port_dict['id']) # Get updated VIF info port_dict = self.manager.network_api.show_port( context, port_dict.get('id')) # Update the real physical mac address from ironic. server_nic.mac_address = port_dict['mac_address'] except Exception as e: # Set nics here, so we can clean up the # created networks during reverting. server.nics = nics_obj LOG.error( "Server %(server)s: create or get network " "failed. The reason is %(reason)s", { "server": server.uuid, "reason": e }) raise exception.NetworkError( _("Build network for server failed.")) return nics_obj
def _max_attempts(self): max_attempts = CONF.scheduler.scheduler_max_attempts if max_attempts < 1: raise exception.InvalidParameterValue( err=_("Invalid value for 'scheduler_max_attempts', " "must be >=1")) return max_attempts
def _create_server(self, context, server, requested_networks, user_data, injected_files, key_pair, request_spec=None, filter_properties=None): """Perform a deployment.""" LOG.debug("Starting server...", server=server) notifications.notify_about_server_action( context, server, self.host, action=fields.NotificationAction.CREATE, phase=fields.NotificationPhase.START) fsm = utils.get_state_machine(start_state=server.status, target_state=states.ACTIVE) try: flow_engine = create_server.get_flow( context, self, server, requested_networks, user_data, injected_files, key_pair, request_spec, filter_properties, ) except Exception: with excutils.save_and_reraise_exception(): utils.process_event(fsm, server, event='error') self._rollback_servers_quota(context, -1) msg = _("Create manager server flow failed.") LOG.exception(msg) def _run_flow(): # This code executes create server flow. If something goes wrong, # flow reverts all job that was done and reraises an exception. # Otherwise, all data that was generated by flow becomes available # in flow engine's storage. with flow_utils.DynamicLogListener(flow_engine, logger=LOG): flow_engine.run() try: _run_flow() except Exception as e: with excutils.save_and_reraise_exception(): server.power_state = states.NOSTATE utils.process_event(fsm, server, event='error') self._rollback_servers_quota(context, -1) LOG.error("Created server %(uuid)s failed." "Exception: %(exception)s", {"uuid": server.uuid, "exception": e}) else: # Advance the state model for the given event. Note that this # doesn't alter the server in any way. This may raise # InvalidState, if this event is not allowed in the current state. server.power_state = self.driver.get_power_state(context, server.uuid) server.launched_at = timeutils.utcnow() utils.process_event(fsm, server, event='done') LOG.info("Created server %s successfully.", server.uuid)
def _wait_for_provision_state(): try: node = self._validate_server_and_node(server) except exception.ServerNotFound: LOG.debug("Server already removed from Ironic", server=server) raise loopingcall.LoopingCallDone() if node.provision_state in (ironic_states.NOSTATE, ironic_states.CLEANING, ironic_states.CLEANWAIT, ironic_states.CLEANFAIL, ironic_states.AVAILABLE): # From a user standpoint, the node is unprovisioned. If a node # gets into CLEANFAIL state, it must be fixed in Ironic, but we # can consider the server unprovisioned. LOG.debug( "Ironic node %(node)s is in state %(state)s, " "server is now unprovisioned.", dict(node=node.uuid, state=node.provision_state), server=server) raise loopingcall.LoopingCallDone() if data['tries'] >= CONF.ironic.api_max_retries + 1: msg = (_("Error destroying the server on node %(node)s. " "Provision state still '%(state)s'.") % { 'state': node.provision_state, 'node': node.uuid }) LOG.error(msg) raise exception.MoganException(msg) else: data['tries'] += 1 _log_ironic_polling('unprovision', node, server)
def validate(value): try: json.dumps(value) except TypeError: raise exception.Invalid(_('%s is not JSON serializable') % value) else: return value
def validate_sort_dir(sort_dir): if sort_dir not in ['asc', 'desc']: raise wsme.exc.ClientSideError( _("Invalid sort direction: %s. " "Acceptable values are " "'asc' or 'desc'") % sort_dir) return sort_dir
def post(self, aggregate_uuid, node): """Add node to the given aggregate.""" validation.check_schema(node, agg_schema.add_aggregate_node) node = node['node'] # check whether the node is already in another az db_aggregate = objects.Aggregate.get(pecan.request.context, aggregate_uuid) if 'availability_zone' in db_aggregate['metadata']: node_aggs = pecan.request.engine_api.list_node_aggregates( pecan.request.context, node) aggregates = objects.AggregateList.get_by_metadata_key( pecan.request.context, 'availability_zone') agg_az = db_aggregate.metadata['availability_zone'] conflict_azs = [ agg.metadata['availability_zone'] for agg in aggregates if agg.uuid in node_aggs['aggregates'] and agg.metadata['availability_zone'] != agg_az ] if conflict_azs: msg = _("Node %(node)s is already in availability zone(s) " "%(az)s") % { "node": node, "az": conflict_azs } raise wsme.exc.ClientSideError( msg, status_code=http_client.BAD_REQUEST) pecan.request.engine_api.add_aggregate_node(pecan.request.context, aggregate_uuid, node)
def validate_limit(limit): if limit is None: return CONF.api.max_limit if limit <= 0: raise wsme.exc.ClientSideError(_("Limit must be positive")) return min(CONF.api.max_limit, limit)
def _check_metadata_conflicts(self, aggregate_uuid, key, value): """Check if metadata conflict with the given key""" nodes = pecan.request.engine_api.list_aggregate_nodes( pecan.request.context, aggregate_uuid) aggregates = objects.AggregateList.get_by_metadata_key( pecan.request.context, key) filtered_aggs = [] for agg in aggregates: agg_nodes = \ pecan.request.engine_api.list_aggregate_nodes( pecan.request.context, agg.uuid) for node in agg_nodes['nodes']: if node in nodes['nodes']: filtered_aggs.append(agg) break conflicts = [agg.metadata[key] for agg in filtered_aggs if agg.metadata[key] != value and agg.uuid != aggregate_uuid] if conflicts: msg = _("One or more nodes already in different " "%(key)s(s) %(conflicts)s") % {"key": key, "conflicts": conflicts} raise wsme.exc.ClientSideError( msg, status_code=http_client.BAD_REQUEST)
def rebuild(self, context, server): """Rebuild/redeploy a server. :param context: The security context. :param server: The server object. """ LOG.debug('Rebuild called for server', server=server) node_uuid = server.node_uuid node = self._get_node(node_uuid) self._add_server_info_to_node(node, server) # trigger the node rebuild try: self.ironicclient.call("node.set_provision_state", node_uuid, ironic_states.REBUILD) except (ironic_exc.InternalServerError, ironic_exc.BadRequest) as e: msg = (_("Failed to request Ironic to rebuild server " "%(server)s: %(reason)s") % { 'server': server.uuid, 'reason': six.text_type(e) }) raise exception.ServerDeployFailure(msg) # Although the target provision state is REBUILD, it will actually go # to ACTIVE once the redeploy is finished. timer = loopingcall.FixedIntervalLoopingCall(self._wait_for_active, server) timer.start(interval=CONF.ironic.api_retry_interval).wait() LOG.info('Server was successfully rebuilt', server=server)
def apply_jsonpatch(doc, patch): for p in patch: if p['op'] == 'add' and p['path'].count('/') == 1: if p['path'].lstrip('/') not in doc: msg = _('Adding a new attribute (%s) to the root of ' ' the resource is not allowed') raise wsme.exc.ClientSideError(msg % p['path']) return jsonpatch.apply_patch(doc, jsonpatch.JsonPatch(patch))
def _validate_target_state(self, target): """Validate the target state. A target state must be a valid state that is 'stable'. :param target: The target state :raises exception.InvalidState: if it is an invalid target state """ if target is None: return if target not in self._states: raise excp.InvalidState( _("Target state '%s' does not exist") % target) if not self.is_stable(target): raise excp.InvalidState( _("Target state '%s' is not a 'stable' state") % target)
def _choose_nodes(self, filtered_nodes, request_spec): num_servers = request_spec['num_servers'] if num_servers > len(filtered_nodes): msg = 'Not enough nodes found for servers, request ' \ 'servers: %s, but only available nodes: %s' \ % (str(num_servers), str(len(filtered_nodes))) raise exception.NoValidNode(_("Choose Node: %s") % msg) return filtered_nodes[:num_servers]
def quota_update(self, context, project_id, resource_name, updates): if 'resource_name' in updates or 'project_id' in updates: msg = _("Cannot overwrite resource_name/project_id for " "an existing Quota.") raise exception.InvalidParameterValue(err=msg) try: return self._do_update_quota(context, project_id, resource_name, updates) except db_exc.DBDuplicateEntry: pass
def server_update(self, context, server_id, values): if 'uuid' in values: msg = _("Cannot overwrite UUID for an existing Server.") raise exception.InvalidParameterValue(err=msg) try: return self._do_update_server(context, server_id, values) except db_exc.DBDuplicateEntry as e: if 'name' in e.columns: raise exception.DuplicateName(name=values['name'])
def validate(patch): _path = '/' + patch.path.split('/')[1] if _path in patch.internal_attrs(): msg = _("'%s' is an internal attribute and can not be updated") raise wsme.exc.ClientSideError(msg % patch.path) if patch.path in patch.non_removable_attrs() and patch.op == 'remove': msg = _("'%s' is a mandatory attribute and can not be removed") raise wsme.exc.ClientSideError(msg % patch.path) if patch.op != 'remove': if patch.value is wsme.Unset: msg = _("'add' and 'replace' operations need a value") raise wsme.exc.ClientSideError(msg) ret = {'path': patch.path, 'op': patch.op} if patch.value is not wsme.Unset: ret['value'] = patch.value return ret
def get_session(group): auth = mogan_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=mogan_auth.LEGACY_SECTION) session = kaloading.load_session_from_conf_options(CONF, group, auth=auth) return session
def main(): command_opt = cfg.SubCommandOpt('command', title='Command', help=_('Available commands'), handler=add_command_parsers) CONF.register_cli_opt(command_opt) service.prepare_service(sys.argv) CONF.command.func()
class MoganException(Exception): """Base Mogan Exception To correctly use this class, inherit from it and define a '_msg_fmt' property. That message will get printf'd with the keyword arguments provided to the constructor. If you need to access the message from an exception you should use six.text_type(exc) """ _msg_fmt = _("An unknown exception occurred.") code = http_client.INTERNAL_SERVER_ERROR headers = {} safe = False def __init__(self, message=None, **kwargs): self.kwargs = kwargs if 'code' not in self.kwargs: try: self.kwargs['code'] = self.code except AttributeError: pass if not message: try: message = self._msg_fmt % kwargs except Exception: # kwargs doesn't match a variable in self._msg_fmt # log the issue and the kwargs LOG.exception('Exception in string format operation') for name, value in kwargs.items(): LOG.error("%s: %s" % (name, value)) if CONF.fatal_exception_format_errors: raise else: # at least get the core self._msg_fmt out if something # happened message = self._msg_fmt super(MoganException, self).__init__(message) def __str__(self): """Encode to utf-8 then wsme api can consume it as well.""" if not six.PY3: return unicode(self.args[0]).encode('utf-8') return self.args[0] def __unicode__(self): """Return a unicode representation of the exception message.""" return unicode(self.args[0])
def post(self, server): """Create a new server. :param server: a server within the request body. """ validation.check_schema(server, server_schemas.create_server) scheduler_hints = server.get('scheduler_hints', {}) server = server.get('server') min_count = server.get('min_count', 1) max_count = server.get('max_count', min_count) if min_count > max_count: msg = _('min_count must be <= max_count') raise wsme.exc.ClientSideError(msg, status_code=http_client.BAD_REQUEST) requested_networks = server.pop('networks', None) flavor_uuid = server.get('flavor_uuid') image_uuid = server.get('image_uuid') user_data = server.get('user_data') key_name = server.get('key_name') partitions = server.get('partitions') personality = server.pop('personality', None) password = self._get_server_admin_password(server) injected_files = [] if personality: for item in personality: injected_files.append((item['path'], item['contents'])) flavor = objects.Flavor.get(pecan.request.context, flavor_uuid) if flavor.disabled: raise exception.FlavorDisabled(flavor_id=flavor.uuid) servers = pecan.request.engine_api.create( pecan.request.context, flavor, image_uuid=image_uuid, name=server.get('name'), description=server.get('description'), availability_zone=server.get('availability_zone'), metadata=server.get('metadata'), requested_networks=requested_networks, user_data=user_data, injected_files=injected_files, admin_password=password, key_name=key_name, min_count=min_count, max_count=max_count, partitions=partitions, scheduler_hints=scheduler_hints) # Set the HTTP Location Header for the first server. pecan.response.location = link.build_url('server', servers[0].uuid) return Server.convert_with_links(servers[0])
def is_stable(self, state): """Is the state stable? :param state: the state of interest :raises InvalidState: if the state is invalid :returns: True if it is a stable state; False otherwise """ try: return self._states[state]['stable'] except KeyError: raise excp.InvalidState(_("State '%s' does not exist") % state)
def aggregate_update(self, context, aggregate_id, values): if 'uuid' in values: msg = _("Cannot overwrite UUID for an existing aggregate.") raise exception.InvalidParameterValue(err=msg) try: result = self._do_update_aggregate(context, aggregate_id, values) except db_exc.DBDuplicateEntry as e: if 'name' in e.columns: raise exception.DuplicateName(name=values['name']) return result
def post(self, flavor_uuid, tenant): """Add flavor access for the given tenant.""" validation.check_schema(tenant, flavor_access.add_tenant_access) flavor = objects.Flavor.get(pecan.request.context, flavor_uuid) if flavor.is_public: msg = _("Can not add access to a public flavor.") raise wsme.exc.ClientSideError(msg, status_code=http_client.CONFLICT) flavor.projects.append(tenant['tenant_id']) flavor.save()
def get_all(self, flavor_uuid): """Retrieve a list of extra specs of the queried flavor.""" flavor = objects.Flavor.get(pecan.request.context, flavor_uuid) # public flavor to all projects if flavor.is_public: msg = _("Access list not available for public flavors.") raise wsme.exc.ClientSideError(msg, status_code=http_client.NOT_FOUND) # private flavor to listed projects only return _marshall_flavor_access(flavor)
def wrapper(**kw): obj_type = kw.pop('object_type') result = func(**kw) extra_args = set(kw) - set(result) if extra_args: raise exception.InvalidParameterValue( _("Unknown keyword arguments (%(extra)s) were passed " "while creating a test %(object_type)s object.") % {"extra": ", ".join(extra_args), "object_type": obj_type}) return result
def generate_x509_fingerprint(pem_key): try: if isinstance(pem_key, six.text_type): pem_key = pem_key.encode('utf-8') cert = x509.load_pem_x509_certificate(pem_key, backends.default_backend()) raw_fp = binascii.hexlify(cert.fingerprint(hashes.SHA1())) if six.PY3: raw_fp = raw_fp.decode('ascii') return ':'.join(a + b for a, b in zip(raw_fp[::2], raw_fp[1::2])) except (ValueError, TypeError, binascii.Error) as ex: raise exception.InvalidKeypair( reason=_('failed to generate X509 fingerprint. ' 'Error message: %s') % ex)
def _check_aggregates_conflict(self, node, node_aggregates, key, value): """Check aggregates conflict with the given key""" aggregates = objects.AggregateList.get_by_metadata_key( pecan.request.context, key) conflicts = [ agg.metadata[key] for agg in aggregates if agg.uuid in node_aggregates and agg.metadata[key] != value] if conflicts: msg = _("Node %(node)s is already in %(key)s(s) " "%(conflicts)s") % {"node": node, "key": key, "conflicts": conflicts} raise wsme.exc.ClientSideError( msg, status_code=http_client.BAD_REQUEST)