def _semantic_modifiable_fields(patch_obj, force_action=False): # Prevent auto populated fields from being updated state_rel_path = [ '/uuid', '/id', '/host_id', '/datatype', '/sensortype' ] if any(p['path'] in state_rel_path for p in patch_obj): raise wsme.exc.ClientSideError( _("The following fields can not be " "modified: %s ") % state_rel_path) if not (pecan.request.user_agent.startswith('hwmon') or force_action): state_rel_path = [ '/sensorgroupname', '/path', '/state', '/possible_states', '/actions_critical_choices', '/actions_major_choices', '/actions_minor_choices', '/unit_base_group', '/unit_modifier_group', '/unit_rate_group', '/t_minor_lower_group', '/t_minor_upper_group', '/t_major_lower_group', '/t_major_upper_group', '/t_critical_lower_group', '/t_critical_upper_group', ] if any(p['path'] in state_rel_path for p in patch_obj): raise wsme.exc.ClientSideError( _("The following fields are not remote-modifiable: %s") % state_rel_path)
def _get_ports_collection(self, uuid, node_uuid, marker, limit, sort_key, sort_dir, expand=False, resource_url=None): if self._from_hosts and not uuid: raise exception.InvalidParameterValue(_("Host id not specified.")) if self._from_node and not uuid: raise exception.InvalidParameterValue(_("node id not specified.")) limit = utils.validate_limit(limit) sort_dir = utils.validate_sort_dir(sort_dir) marker_obj = None if marker: marker_obj = objects.EthernetPort.get_by_uuid( pecan.request.context, marker) if self._from_hosts: ports = objects.EthernetPort.get_by_host(pecan.request.context, uuid, limit, marker=marker_obj, sort_key=sort_key, sort_dir=sort_dir) elif self._from_node: ports = objects.EthernetPort.get_by_numa_node( pecan.request.context, uuid, limit, marker=marker_obj, sort_key=sort_key, sort_dir=sort_dir) else: if uuid: ports = objects.EthernetPort.get_by_host(pecan.request.context, uuid, limit, marker=marker_obj, sort_key=sort_key, sort_dir=sort_dir) else: ports = objects.EthernetPort.list(pecan.request.context, limit, marker=marker_obj, sort_key=sort_key, sort_dir=sort_dir) return EthernetPortCollection.convert_with_links(ports, limit, url=resource_url, expand=expand, sort_key=sort_key, sort_dir=sort_dir)
def is_valid_hostname(hostname): """Determine whether an address is valid as per RFC 1123. """ # Maximum length of 255 rc = True length = len(hostname) if length > 255: raise wsme.exc.ClientSideError( _("Hostname {} is too long. Length {} is greater than 255." "Please configure valid hostname.").format(hostname, length)) # Allow a single dot on the right hand side if hostname[-1] == ".": hostname = hostname[:-1] # Create a regex to ensure: # - hostname does not begin or end with a dash # - each segment is 1 to 63 characters long # - valid characters are A-Z (any case) and 0-9 valid_re = re.compile("(?!-)[A-Z\d-]{1,63}(?<!-)$", re.IGNORECASE) rc = all(valid_re.match(x) for x in hostname.split(".")) if not rc: raise wsme.exc.ClientSideError( _("Hostname %s is invalid. Hostname may not begin or end with" " a dash. Each segment is 1 to 63 chars long and valid" " characters are A-Z, a-z, and 0-9." " Please configure valid hostname.") % (hostname)) return rc
def __init__(self, stdout=None, stderr=None, exit_code=None, cmd=None, description=None): self.exit_code = exit_code self.stderr = stderr self.stdout = stdout self.cmd = cmd self.description = description if description is None: description = _('Unexpected error while running command.') if exit_code is None: exit_code = '-' message = (_('%(description)s\nCommand: %(cmd)s\n' 'Exit code: %(exit_code)s\nStdout: %(stdout)r\n' 'Stderr: %(stderr)r') % { 'description': description, 'cmd': cmd, 'exit_code': exit_code, 'stdout': stdout, 'stderr': stderr }) IOError.__init__(self, message)
def post(self, sensor): """Create a new sensor.""" if self._from_hosts: raise exception.OperationNotPermitted self._new_sensor_semantic_checks(sensor) try: ihost = pecan.request.dbapi.host_get(sensor.host_uuid) if hasattr(sensor, 'datatype'): if sensor.datatype == 'discrete': new_sensor = pecan.request.dbapi.sensor_discrete_create( ihost.id, sensor.as_dict()) elif sensor.datatype == 'analog': new_sensor = pecan.request.dbapi.sensor_analog_create( ihost.id, sensor.as_dict()) else: raise wsme.exc.ClientSideError( _("Invalid datatype. {}").format(sensor.datatype)) else: raise wsme.exc.ClientSideError(_("Unspecified datatype.")) except exception.InventoryException as e: LOG.exception(e) raise wsme.exc.ClientSideError(_("Invalid data")) return sensor.convert_with_links(new_sensor)
def validate_patch(patch): """Performs a basic validation on patch.""" if not isinstance(patch, list): patch = [patch] for p in patch: path_pattern = re.compile("^/[a-zA-Z0-9-_]+(/[a-zA-Z0-9-_]+)*$") if not isinstance(p, dict) or \ any(key for key in ["path", "op"] if key not in p): raise wsme.exc.ClientSideError( _("Invalid patch format: %s") % str(p)) path = p["path"] op = p["op"] if op not in ["add", "replace", "remove"]: raise wsme.exc.ClientSideError( _("Operation not supported: %s") % op) if not path_pattern.match(path): raise wsme.exc.ClientSideError(_("Invalid path: %s") % path) if op == "add": if path.count('/') == 1: raise wsme.exc.ClientSideError( _("Adding an additional attribute (%s) to the " "resource is not allowed") % path)
def _check_host(host): if utils.is_aio_simplex_host_unlocked(host): raise wsme.exc.ClientSideError(_('Host must be locked.')) elif host.administrative != k_host.ADMIN_LOCKED and not \ utils.is_host_simplex_controller(host): raise wsme.exc.ClientSideError(_('Host must be locked.')) if k_host.COMPUTE not in host.subfunctions: raise wsme.exc.ClientSideError( _('Can only modify compute node cores.'))
def init_host(self, admin_context=None): super(AgentManager, self).init_host(admin_context) if os.path.isfile('/etc/inventory/inventory.conf'): LOG.info( _("inventory-agent started, " "system config to be reported by audit")) else: LOG.info(_("No config file for inventory-agent found.")) if tsc.system_mode == constants.SYSTEM_MODE_SIMPLEX: utils.touch(INVENTORY_READY_FLAG)
def _get_lldp_tlvs_collection(self, uuid, marker, limit, sort_key, sort_dir, expand=False, resource_url=None): if self._from_lldp_agents and not uuid: raise exception.InvalidParameterValue( _("LLDP agent id not specified.")) if self._from_lldp_neighbours and not uuid: raise exception.InvalidParameterValue( _("LLDP neighbour id not specified.")) limit = utils.validate_limit(limit) sort_dir = utils.validate_sort_dir(sort_dir) marker_obj = None if marker: marker_obj = objects.LLDPTLV.get_by_id(pecan.request.context, marker) if self._from_lldp_agents: tlvs = objects.LLDPTLV.get_by_agent(pecan.request.context, uuid, limit, marker_obj, sort_key=sort_key, sort_dir=sort_dir) elif self._from_lldp_neighbours: tlvs = objects.LLDPTLV.get_by_neighbour(pecan.request.context, uuid, limit, marker_obj, sort_key=sort_key, sort_dir=sort_dir) else: tlvs = objects.LLDPTLV.list(pecan.request.context, limit, marker_obj, sort_key=sort_key, sort_dir=sort_dir) return LLDPTLVCollection.convert_with_links(tlvs, limit, url=resource_url, expand=expand, sort_key=sort_key, sort_dir=sort_dir)
def _check_host(ihost): if utils.is_aio_simplex_host_unlocked(ihost): raise wsme.exc.ClientSideError(_("Host must be locked.")) elif ihost['administrative'] != 'locked': unlocked = False current_ihosts = pecan.request.dbapi.ihost_get_list() for h in current_ihosts: if (h['administrative'] != 'locked' and h['hostname'] != ihost['hostname']): unlocked = True if unlocked: raise wsme.exc.ClientSideError(_("Host must be locked."))
def validate_load_for_delete(load): if not load: raise exception.InventoryException(_("Load not found")) valid_delete_states = [ constants.IMPORTED_LOAD_STATE, constants.ERROR_LOAD_STATE, constants.DELETING_LOAD_STATE ] if load.state not in valid_delete_states: raise exception.InventoryException( _("Only a load in an imported or error state can be deleted"))
def _new_sensorgroup_semantic_checks(sensorgroup): datatype = sensorgroup.as_dict().get('datatype') or "" sensortype = sensorgroup.as_dict().get('sensortype') or "" if not (datatype and sensortype): raise wsme.exc.ClientSideError( _("sensorgroup-add: Cannot " "add a sensorgroup " "without a valid datatype " "and sensortype.")) if datatype not in constants.SENSOR_DATATYPE_VALID_LIST: raise wsme.exc.ClientSideError( _("sensorgroup datatype must be one of %s.") % constants.SENSOR_DATATYPE_VALID_LIST)
def _get_value_as_type(self, forced_type=None): """Convert metadata value to the specified data type. This method is called during metadata query to help convert the querying metadata to the data type specified by user. If there is no data type given, the metadata will be parsed by ast.literal_eval to try to do a smart converting. NOTE (flwang) Using "_" as prefix to avoid an InvocationError raised from wsmeext/sphinxext.py. It's OK to call it outside the Query class. Because the "public" side of that class is actually the outside of the API, and the "private" side is the API implementation. The method is only used in the API implementation, so it's OK. :returns: metadata value converted with the specified data type. """ type = forced_type or self.type try: converted_value = self.value if not type: try: converted_value = ast.literal_eval(self.value) except (ValueError, SyntaxError): msg = _('Failed to convert the metadata value %s' ' automatically') % (self.value) LOG.debug(msg) else: if type not in self._supported_types: # Types must be explicitly declared so the # correct type converter may be used. Subclasses # of Query may define _supported_types and # _type_converters to define their own types. raise TypeError() converted_value = self._type_converters[type](self.value) except ValueError: msg = _('Failed to convert the value %(value)s' ' to the expected data type %(type)s.') % \ {'value': self.value, 'type': type} raise wsme.exc.ClientSideError(msg) except TypeError: msg = _('The data type %(type)s is not supported. The supported' ' data type list is: %(supported)s') % \ {'type': type, 'supported': self._supported_types} raise wsme.exc.ClientSideError(msg) except Exception: msg = _('Unexpected exception converting %(value)s to' ' the expected data type %(type)s.') % \ {'value': self.value, 'type': type} raise wsme.exc.ClientSideError(msg) return converted_value
def _update_sensors(op, sensorgroup, ihost, sensors): sensors = sensors.split(',') this_sensorgroup_datatype = None this_sensorgroup_sensortype = None if op == "add": this_sensorgroup_id = 0 else: this_sensorgroup_id = sensorgroup['id'] this_sensorgroup_datatype = sensorgroup['datatype'] this_sensorgroup_sensortype = sensorgroup['sensortype'] if sensors: # Update Sensors' sensorgroup_uuid attribute sensors_list = pecan.request.dbapi.sensor_get_all(host_id=ihost['id']) for p in sensors_list: # if new sensor associated if (p.uuid in sensors or p.sensorname in sensors) \ and not p.sensorgroup_id: values = {'sensorgroup_id': sensorgroup['id']} # else if old sensor disassociated elif ((p.uuid not in sensors and p.sensorname not in sensors) and p.sensorgroup_id and p.sensorgroup_id == this_sensorgroup_id): values = {'sensorgroup_id': None} else: continue if p.datatype != this_sensorgroup_datatype: msg = _("Invalid datatype: host {} sensor {}: Expected: {} " "Received: {}.").format( (ihost['hostname'], p.sensorname, this_sensorgroup_datatype, p.datatype)) raise wsme.exc.ClientSideError(msg) if p.sensortype != this_sensorgroup_sensortype: msg = _("Invalid sensortype: host {} sensor {}: Expected: {} " "Received: {}.").format(ihost['hostname'], p.sensorname, this_sensorgroup_sensortype, p.sensortype) raise wsme.exc.ClientSideError(msg) try: pecan.request.dbapi.sensor_update(p.uuid, values) except exception.HTTPNotFound: msg = _("Sensor update of sensorgroup_uuid failed: host {} " "sensor {}").format(ihost['hostname'], p.sensorname) raise wsme.exc.ClientSideError(msg)
def _get_lldp_agents_collection(self, uuid, marker, limit, sort_key, sort_dir, expand=False, resource_url=None): if self._from_hosts and not uuid: raise exception.InvalidParameterValue(_("Host id not specified.")) if self._from_ports and not uuid: raise exception.InvalidParameterValue(_("Port id not specified.")) limit = utils.validate_limit(limit) sort_dir = utils.validate_sort_dir(sort_dir) marker_obj = None if marker: marker_obj = objects.LLDPAgent.get_by_uuid(pecan.request.context, marker) if self._from_hosts: agents = objects.LLDPAgent.get_by_host(pecan.request.context, uuid, limit, marker_obj, sort_key=sort_key, sort_dir=sort_dir) elif self._from_ports: agents = [] agent = objects.LLDPAgent.get_by_port(pecan.request.context, uuid) agents.append(agent) else: agents = objects.LLDPAgent.list(pecan.request.context, limit, marker_obj, sort_key=sort_key, sort_dir=sort_dir) return LLDPAgentCollection.convert_with_links(agents, limit, url=resource_url, expand=expand, sort_key=sort_key, sort_dir=sort_dir)
def init_host(self, admin_context=None): """Initialize the agent host. :param admin_context: the admin context to pass to periodic tasks. :raises: RuntimeError when agent is already running. """ if self._started: raise RuntimeError( _('Attempt to start an already running ' 'agent manager')) rejection_func = rejection.reject_when_reached(64) # CONF.conductor.workers_pool_size) self._executor = futurist.GreenThreadPoolExecutor( 64, check_and_reject=rejection_func) # JK max_workers=CONF.conductor.workers_pool_size, """Executor for performing tasks async.""" # Collect driver-specific periodic tasks. # Conductor periodic tasks accept context argument, LOG.info('Collecting periodic tasks') self._periodic_task_callables = [] self._collect_periodic_tasks(self, (admin_context, )) self._periodic_tasks = periodics.PeriodicWorker( self._periodic_task_callables, executor_factory=periodics.ExistingExecutor(self._executor)) # 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._started = True
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.message % kwargs except Exception as e: # kwargs doesn't match a variable in the message # log the issue and the kwargs LOG.exception(_('Exception in string format operation')) for name, value in kwargs.iteritems(): LOG.error("%s: %s" % (name, value)) if CONF.fatal_exception_format_errors: raise e else: # at least get the core message out if something happened message = self.message super(InventoryException, self).__init__(message)
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 serve_pxe(api_service, conf, workers=1): global _launcher_pxe if _launcher_pxe: raise RuntimeError(_('serve() _launcher_pxe can only be called once')) _launcher_pxe = service.launch(conf, api_service, workers=workers)
def relearn(self, body): """Handle Sensor Model Relearn Request.""" host_uuid = self._get_host_uuid(body) # LOG.info("Host UUID: %s - BM_TYPE: %s" % (host_uuid, bm_type )) # hwmon_sensorgroup = {'ihost_uuid': host_uuid} request_body = {'host_uuid': host_uuid} hwmon_response = hwmon_api.sensorgroup_relearn( self._api_token, self._hwmon_address, self._hwmon_port, request_body, constants.HWMON_DEFAULT_TIMEOUT_IN_SECS) if not hwmon_response: hwmon_response = { 'status': 'fail', 'reason': 'no response', 'action': 'retry' } elif hwmon_response['status'] != 'pass': msg = _("HWMON has returned with " "a status of {}, reason: {}, " "recommended action: {}").format( hwmon_response.get('status'), hwmon_response.get('reason'), hwmon_response.get('action')) raise wsme.exc.ClientSideError(msg)
def before(self, state): headers = state.request.headers environ = state.request.environ user_name = headers.get('X-User-Name') user_id = headers.get('X-User-Id') project = headers.get('X-Project-Name') project_id = headers.get('X-Project-Id') domain_id = headers.get('X-User-Domain-Id') domain_name = headers.get('X-User-Domain-Name') auth_token = headers.get('X-Auth-Token') roles = headers.get('X-Roles', '').split(',') catalog_header = headers.get('X-Service-Catalog') service_catalog = None if catalog_header: try: service_catalog = jsonutils.loads(catalog_header) except ValueError: raise webob.exc.HTTPInternalServerError( _('Invalid service catalog json.')) auth_token_info = environ.get('keystone.token_info') auth_url = CONF.keystone_authtoken.auth_uri state.request.context = context.make_context( auth_token=auth_token, auth_url=auth_url, auth_token_info=auth_token_info, user_name=user_name, user_id=user_id, project_name=project, project_id=project_id, domain_id=domain_id, domain_name=domain_name, roles=roles, service_catalog=service_catalog)
def get_local_controller_hostname(): try: local_hostname = socket.gethostname() except Exception as e: raise exception.InventoryException( _("Failed to get the local hostname: %s") % str(e)) return local_hostname
def get_mate_controller_hostname(hostname=None): if not hostname: try: hostname = socket.gethostname() except Exception as e: raise exception.InventoryException( _("Failed to get the local hostname: %s") % str(e)) if hostname == k_host.CONTROLLER_0_HOSTNAME: mate_hostname = k_host.CONTROLLER_1_HOSTNAME elif hostname == k_host.CONTROLLER_1_HOSTNAME: mate_hostname = k_host.CONTROLLER_0_HOSTNAME else: raise exception.InventoryException( _("Unknown local hostname: %s)") % hostname) return mate_hostname
def enable_backend(sb, backend_enable_function): """In-service enable storage backend """ try: # Initiate manifest application LOG.info(_("Initializing configuration of storage %s backend.") % sb.backend.title()) backend_enable_function(pecan.request.context) LOG.info("Configuration of storage %s backend initialized, " "continuing in background." % sb.backend.title()) except exception.InventoryException: LOG.exception("Manifests failed!") # Set lvm backend to error so that it can be recreated values = {'state': constants.SB_STATE_CONFIG_ERR, 'task': None} pecan.request.dbapi.storage_backend_update(sb.uuid, values) msg = (_("%s configuration failed, check node status and retry. " "If problem persists contact next level of support.") % sb.backend.title()) raise wsme.exc.ClientSideError(msg)
def tempdir(**kwargs): tempfile.tempdir = CONF.tempdir tmpdir = tempfile.mkdtemp(**kwargs) try: yield tmpdir finally: try: shutil.rmtree(tmpdir) except OSError as e: LOG.error(_('Could not remove tmpdir: %s'), str(e))
def get_active_load(loads): active_load = None for db_load in loads: if db_load.state == constants.ACTIVE_LOAD_STATE: active_load = db_load if active_load is None: raise exception.InventoryException(_("No active load found")) return active_load
def get_imported_load(loads): imported_load = None for db_load in loads: if db_load.state == constants.IMPORTED_LOAD_STATE: imported_load = db_load if imported_load is None: raise exception.InventoryException(_("No imported load found")) return imported_load
def set_img_conversions_defaults(dbapi, controller_fs_api): """ initialize img_conversion partitions with default values if not already done :param dbapi :param controller_fs_api """ # Img conversions identification values = { 'name': constants.FILESYSTEM_NAME_IMG_CONVERSIONS, 'logical_volume': constants.FILESYSTEM_LV_DICT[ constants.FILESYSTEM_NAME_IMG_CONVERSIONS], 'replicated': False } # Abort if is already defined controller_fs_list = dbapi.controller_fs_get_list() for fs in controller_fs_list: if values['name'] == fs.name: LOG.info("Image conversions already defined, " "avoiding reseting values") return # Check if there is enough space available rootfs_max_GiB, cgtsvg_max_free_GiB = \ controller_fs_api.get_controller_fs_limit() args = { 'avail': cgtsvg_max_free_GiB, 'min': constants.DEFAULT_SMALL_IMG_CONVERSION_STOR_SIZE, 'lvg': constants.LVG_CGTS_VG } if cgtsvg_max_free_GiB >= constants.DEFAULT_IMG_CONVERSION_STOR_SIZE: img_conversions_gib = constants.DEFAULT_IMG_CONVERSION_STOR_SIZE elif (cgtsvg_max_free_GiB >= constants.DEFAULT_SMALL_IMG_CONVERSION_STOR_SIZE): img_conversions_gib = \ constants.DEFAULT_SMALL_IMG_CONVERSION_STOR_SIZE else: msg = _("Not enough space for image conversion partition. " "Please ensure that '%(lvg)s' VG has " "at least %(min)s GiB free space." "Currently available: %(avail)s GiB.") % args raise wsme.exc.ClientSideError(msg) args['size'] = img_conversions_gib LOG.info("Available space in '%(lvg)s' is %(avail)s GiB " "from which img_conversions will use %(size)s GiB." % args) # Create entry values['size'] = img_conversions_gib dbapi.controller_fs_create(values)
def main(): # Parse config file and command line options, then start logging # The mac is to be truncated to 17 characters, which is the proper # length of a mac address, in order to handle IPv6 where a DUID # is provided instead of a mac address. The truncated DUID is # then equivalent to the mac address. inventory_service.prepare_service(sys.argv) LOG = log.getLogger(__name__) if CONF.action.name in ['add', 'del', 'old']: msg = (_("Called '%(action)s' for mac '%(mac)s' with ip '%(ip)s'") % { "action": CONF.action.name, "mac": CONF.action.mac[-17:], "ip": CONF.action.ip }) LOG.info(msg) CONF.action.func(CONF.action.mac[-17:], CONF.action.ip) else: LOG.error( _("Unknown action: %(action)") % {"action": CONF.action.name})
def validate_address_within_address_pool(ip, pool): """Determine whether an IP address is within the specified IP address pool. :param ip netaddr.IPAddress object :param pool objects.AddressPool object """ ipset = netaddr.IPSet() for start, end in pool.ranges: ipset.update(netaddr.IPRange(start, end)) if netaddr.IPAddress(ip) not in ipset: raise wsme.exc.ClientSideError( _("IP address %s is not within address pool ranges") % str(ip))