def post(self, data): """Create a new alarm. :param data: an alarm within the request body. """ rbac.enforce('create_alarm', pecan.request.headers, pecan.request.enforcer) conn = pecan.request.alarm_storage_conn now = timeutils.utcnow() data.alarm_id = str(uuid.uuid4()) user_limit, project_limit = rbac.get_limited_to( pecan.request.headers, pecan.request.enforcer) def _set_ownership(aspect, owner_limitation, header): attr = '%s_id' % aspect requested_owner = getattr(data, attr) explicit_owner = requested_owner != wtypes.Unset caller = pecan.request.headers.get(header) if (owner_limitation and explicit_owner and requested_owner != caller): raise base.ProjectNotAuthorized(requested_owner, aspect) actual_owner = (owner_limitation or requested_owner if explicit_owner else caller) setattr(data, attr, actual_owner) _set_ownership('user', user_limit, 'X-User-Id') _set_ownership('project', project_limit, 'X-Project-Id') # Check if there's room for one more alarm if is_over_quota(conn, data.project_id, data.user_id): raise OverQuota(data) data.timestamp = now data.state_timestamp = now ALARMS_RULES[data.type].plugin.create_hook(data) change = data.as_dict(models.Alarm) data.update_actions() # make sure alarms are unique by name per project. alarms = list(conn.get_alarms(name=data.name, project=data.project_id)) if alarms: raise base.ClientSideError(_("Alarm with name='%s' exists") % data.name, status_code=409) try: alarm_in = models.Alarm(**change) except Exception: LOG.exception(_("Error while posting alarm: %s") % change) raise base.ClientSideError(_("Alarm incorrect")) alarm = conn.create_alarm(alarm_in) self._record_creation(conn, change, alarm.alarm_id, now) v2_utils.set_resp_location_hdr("/v2/alarms/" + alarm.alarm_id) return Alarm.from_db_model(alarm)
def get(self): """Return this alarm.""" rbac.enforce('get_alarm', pecan.request.headers, pecan.request.enforcer) return Alarm.from_db_model(self._alarm())
def put(self, data): """Modify this alarm. :param data: an alarm within the request body. """ rbac.enforce('change_alarm', pecan.request.headers, pecan.request.enforcer) # Ensure alarm exists alarm_in = self._alarm() now = timeutils.utcnow() data.alarm_id = self._id user, project = rbac.get_limited_to(pecan.request.headers, pecan.request.enforcer) if user: data.user_id = user elif data.user_id == wtypes.Unset: data.user_id = alarm_in.user_id if project: data.project_id = project elif data.project_id == wtypes.Unset: data.project_id = alarm_in.project_id data.timestamp = now if alarm_in.state != data.state: data.state_timestamp = now else: data.state_timestamp = alarm_in.state_timestamp # make sure alarms are unique by name per project. if alarm_in.name != data.name: alarms = list( self.conn.get_alarms(name=data.name, project=data.project_id)) if alarms: raise base.ClientSideError(_("Alarm with name=%s exists") % data.name, status_code=409) ALARMS_RULES[data.type].plugin.update_hook(data) old_data = Alarm.from_db_model(alarm_in) old_alarm = old_data.as_dict(models.Alarm) data.update_actions(old_data) updated_alarm = data.as_dict(models.Alarm) try: alarm_in = models.Alarm(**updated_alarm) except Exception: LOG.exception(_("Error while putting alarm: %s") % updated_alarm) raise base.ClientSideError(_("Alarm incorrect")) alarm = self.conn.update_alarm(alarm_in) change = dict( (k, v) for k, v in updated_alarm.items() if v != old_alarm[k] and k not in ['timestamp', 'state_timestamp']) self._record_change(change, now, on_behalf_of=alarm.project_id) return Alarm.from_db_model(alarm)
def put(self, data): """Modify this alarm. :param data: an alarm within the request body. """ rbac.enforce('change_alarm', pecan.request.headers, pecan.request.enforcer) # Ensure alarm exists alarm_in = self._alarm() now = timeutils.utcnow() data.alarm_id = self._id user, project = rbac.get_limited_to(pecan.request.headers, pecan.request.enforcer) if user: data.user_id = user elif data.user_id == wtypes.Unset: data.user_id = alarm_in.user_id if project: data.project_id = project elif data.project_id == wtypes.Unset: data.project_id = alarm_in.project_id data.timestamp = now if alarm_in.state != data.state: data.state_timestamp = now else: data.state_timestamp = alarm_in.state_timestamp # make sure alarms are unique by name per project. if alarm_in.name != data.name: alarms = list(self.conn.get_alarms(name=data.name, project=data.project_id)) if alarms: raise base.ClientSideError( _("Alarm with name=%s exists") % data.name, status_code=409) ALARMS_RULES[data.type].plugin.update_hook(data) old_data = Alarm.from_db_model(alarm_in) old_alarm = old_data.as_dict(models.Alarm) data.update_actions(old_data) updated_alarm = data.as_dict(models.Alarm) try: alarm_in = models.Alarm(**updated_alarm) except Exception: LOG.exception(_("Error while putting alarm: %s") % updated_alarm) raise base.ClientSideError(_("Alarm incorrect")) alarm = self.conn.update_alarm(alarm_in) change = dict((k, v) for k, v in updated_alarm.items() if v != old_alarm[k] and k not in ['timestamp', 'state_timestamp']) self._record_change(change, now, on_behalf_of=alarm.project_id) return Alarm.from_db_model(alarm)
def get_state(self): """Get the state of this alarm.""" rbac.enforce('get_alarm_state', pecan.request) alarm = self._alarm() return alarm.state
def get_all(self, q=None, sort=None, limit=None, marker=None): """Return all alarms, based on the query provided. :param q: Filter rules for the alarms to be returned. :param sort: A list of pairs of sort key and sort dir. :param limit: The maximum number of items to be return. :param marker: The pagination query marker. """ target = rbac.target_from_segregation_rule(pecan.request.headers, pecan.request.enforcer) rbac.enforce('get_alarms', pecan.request.headers, pecan.request.enforcer, target) q = q or [] # Timestamp is not supported field for Simple Alarm queries kwargs = v2_utils.query_to_kwargs(q, pecan.request.storage.get_alarms, allow_timestamps=False) if sort or limit or marker: kwargs['pagination'] = v2_utils.get_pagination_options( sort, limit, marker, models.Alarm) return [ Alarm.from_db_model(m) for m in pecan.request.storage.get_alarms(**kwargs) ]
def get_state(self): """Get the state of this alarm.""" rbac.enforce('get_alarm_state', pecan.request.headers, pecan.request.enforcer) alarm = self._alarm() return alarm.state
def post(self, data): """Create a new alarm. :param data: an alarm within the request body. """ rbac.enforce('create_alarm', pecan.request) conn = pecan.request.alarm_storage_conn now = timeutils.utcnow() data.alarm_id = str(uuid.uuid4()) user_limit, project_limit = rbac.get_limited_to(pecan.request.headers) def _set_ownership(aspect, owner_limitation, header): attr = '%s_id' % aspect requested_owner = getattr(data, attr) explicit_owner = requested_owner != wtypes.Unset caller = pecan.request.headers.get(header) if (owner_limitation and explicit_owner and requested_owner != caller): raise base.ProjectNotAuthorized(requested_owner, aspect) actual_owner = (owner_limitation or requested_owner if explicit_owner else caller) setattr(data, attr, actual_owner) _set_ownership('user', user_limit, 'X-User-Id') _set_ownership('project', project_limit, 'X-Project-Id') # Check if there's room for one more alarm if is_over_quota(conn, data.project_id, data.user_id): raise OverQuota(data) data.timestamp = now data.state_timestamp = now ALARMS_RULES[data.type].plugin.create_hook(data) change = data.as_dict(alarm_models.Alarm) # make sure alarms are unique by name per project. alarms = list(conn.get_alarms(name=data.name, project=data.project_id)) if alarms: raise base.ClientSideError( _("Alarm with name='%s' exists") % data.name, status_code=409) try: alarm_in = alarm_models.Alarm(**change) except Exception: LOG.exception(_("Error while posting alarm: %s") % change) raise base.ClientSideError(_("Alarm incorrect")) alarm = conn.create_alarm(alarm_in) self._record_creation(conn, change, alarm.alarm_id, now) return Alarm.from_db_model(alarm)
def _enforce_rbac(self, rbac_directive): # TODO(sileht): We should be able to relax this since we # pass the alarm object to the enforcer. auth_project = rbac.get_limited_to_project(pecan.request.headers, pecan.request.enforcer) alarms = list(pecan.request.storage.get_alarms(alarm_id=self._id, project=auth_project)) if not alarms: raise base.AlarmNotFound(alarm=self._id, auth_project=auth_project) alarm = alarms[0] target = {"user_id": alarm.user_id, "project_id": alarm.project_id} rbac.enforce(rbac_directive, pecan.request.headers, pecan.request.enforcer, target) return alarm
def delete(self): """Delete this alarm.""" rbac.enforce('delete_alarm', pecan.request.headers, pecan.request.enforcer) # ensure alarm exists before deleting alarm = self._alarm() self.conn.delete_alarm(alarm.alarm_id) alarm_object = Alarm.from_db_model(alarm) alarm_object.delete_actions()
def delete(self): """Delete this alarm.""" rbac.enforce('delete_alarm', pecan.request) # ensure alarm exists before deleting alarm = self._alarm() self.conn.delete_alarm(alarm.alarm_id) change = Alarm.from_db_model(alarm).as_dict(alarm_models.Alarm) self._record_change(change, timeutils.utcnow(), type=alarm_models.AlarmChange.DELETION)
def post(self, data): """Create a new alarm. :param data: an alarm within the request body. """ rbac.enforce("create_alarm", pecan.request.headers, pecan.request.enforcer, {}) conn = pecan.request.storage now = timeutils.utcnow() data.alarm_id = str(uuid.uuid4()) user_limit, project_limit = rbac.get_limited_to(pecan.request.headers, pecan.request.enforcer) def _set_ownership(aspect, owner_limitation, header): attr = "%s_id" % aspect requested_owner = getattr(data, attr) explicit_owner = requested_owner != wtypes.Unset caller = pecan.request.headers.get(header) if owner_limitation and explicit_owner and requested_owner != caller: raise base.ProjectNotAuthorized(requested_owner, aspect) actual_owner = owner_limitation or requested_owner if explicit_owner else caller setattr(data, attr, actual_owner) _set_ownership("user", user_limit, "X-User-Id") _set_ownership("project", project_limit, "X-Project-Id") # Check if there's room for one more alarm if is_over_quota(conn, data.project_id, data.user_id): raise OverQuota(data) data.timestamp = now data.state_timestamp = now ALARMS_RULES[data.type].plugin.create_hook(data) change = data.as_dict(models.Alarm) data.update_actions() try: alarm_in = models.Alarm(**change) except Exception: LOG.exception(_LE("Error while posting alarm: %s"), change) raise base.ClientSideError(_("Alarm incorrect")) alarm = conn.create_alarm(alarm_in) self._record_creation(conn, change, alarm.alarm_id, now) v2_utils.set_resp_location_hdr("/v2/alarms/" + alarm.alarm_id) return Alarm.from_db_model(alarm)
def get_all(self, q=None): """Return all alarms, based on the query provided. :param q: Filter rules for the alarms to be returned. """ rbac.enforce('get_alarms', pecan.request) q = q or [] # Timestamp is not supported field for Simple Alarm queries kwargs = v2_utils.query_to_kwargs( q, pecan.request.alarm_storage_conn.get_alarms, allow_timestamps=False) return [Alarm.from_db_model(m) for m in pecan.request.alarm_storage_conn.get_alarms(**kwargs)]
def _enforce_rbac(self, rbac_directive): # TODO(sileht): We should be able to relax this since we # pass the alarm object to the enforcer. auth_project = rbac.get_limited_to_project(pecan.request.headers, pecan.request.enforcer) alarms = list( pecan.request.storage.get_alarms(alarm_id=self._id, project=auth_project)) if not alarms: raise base.AlarmNotFound(alarm=self._id, auth_project=auth_project) alarm = alarms[0] target = {'user_id': alarm.user_id, 'project_id': alarm.project_id} rbac.enforce(rbac_directive, pecan.request.headers, pecan.request.enforcer, target) return alarm
def post(self, body): """Define query for retrieving Alarm data. :param body: Query rules for the alarms to be returned. """ rbac.enforce('query_alarm', pecan.request.headers, pecan.request.enforcer) query = ValidatedComplexQuery(body, models.Alarm) query.validate(visibility_field="project_id") conn = pecan.request.alarm_storage_conn return [ alarms.Alarm.from_db_model(s) for s in conn.query_alarms( query.filter_expr, query.orderby, query.limit) ]
def _enforce_rbac(self, rbac_directive): auth_project = pecan.request.headers.get('X-Project-Id') filters = {'alarm_id': self._id} if not rbac.is_admin(pecan.request.headers): filters['project_id'] = auth_project alarms = pecan.request.storage.get_alarms(**filters) if not alarms: raise base.AlarmNotFound(alarm=self._id, auth_project=None) alarm = alarms[0] target = {'user_id': alarm.user_id, 'project_id': alarm.project_id} rbac.enforce(rbac_directive, pecan.request.headers, pecan.request.enforcer, target) return alarm
def post(self, body): """Create or update quota.""" rbac.enforce('update_quotas', pecan.request.headers, pecan.request.enforcer, {}) params = body.to_dict() project_id = params['project_id'] input_quotas = [] for i in params.get('quotas', []): input_quotas.append(i.to_dict()) db_quotas = pecan.request.storage.set_quotas(project_id, input_quotas) quotas = [Quota.from_db_model(i) for i in db_quotas] return Quotas(project_id=project_id, quotas=quotas)
def post(self, body): """Define query for retrieving Alarm data. :param body: Query rules for the alarms to be returned. """ rbac.enforce('query_alarm', pecan.request) query = ValidatedComplexQuery(body, alarm_models.Alarm) query.validate(visibility_field="project_id") conn = pecan.request.alarm_storage_conn return [alarms.Alarm.from_db_model(s) for s in conn.query_alarms(query.filter_expr, query.orderby, query.limit)]
def post(self, body): """Define query for retrieving AlarmChange data. :param body: Query rules for the alarm history to be returned. """ rbac.enforce('query_alarm_history', pecan.request.headers, pecan.request.enforcer) query = ValidatedComplexQuery(body, models.AlarmChange) query.validate(visibility_field="on_behalf_of") conn = pecan.request.alarm_storage_conn return [alarms.AlarmChange.from_db_model(s) for s in conn.query_alarm_history(query.filter_expr, query.orderby, query.limit)]
def get_all(self, q=None, sort=None, limit=None, marker=None): """Return all alarms, based on the query provided. :param q: Filter rules for the alarms to be returned. :param sort: A list of pairs of sort key and sort dir. :param limit: The maximum number of items to be return. :param marker: The pagination query marker. """ target = rbac.target_from_segregation_rule(pecan.request.headers, pecan.request.enforcer) rbac.enforce("get_alarms", pecan.request.headers, pecan.request.enforcer, target) q = q or [] # Timestamp is not supported field for Simple Alarm queries kwargs = v2_utils.query_to_kwargs(q, pecan.request.storage.get_alarms, allow_timestamps=False) if sort or limit or marker: kwargs["pagination"] = v2_utils.get_pagination_options(sort, limit, marker, models.Alarm) return [Alarm.from_db_model(m) for m in pecan.request.storage.get_alarms(**kwargs)]
def post(self, body): """Define query for retrieving AlarmChange data. :param body: Query rules for the alarm history to be returned. """ target = rbac.target_from_segregation_rule(pecan.request.headers, pecan.request.enforcer) rbac.enforce('query_alarm_history', pecan.request.headers, pecan.request.enforcer, target) query = ValidatedComplexQuery(body, models.AlarmChange) query.validate(visibility_field="on_behalf_of") conn = pecan.request.storage return [ alarms.AlarmChange.from_db_model(s) for s in conn.query_alarm_history(query.filter_expr, query.orderby, query.limit) ]
def history(self, q=None): """Assembles the alarm history requested. :param q: Filter rules for the changes to be described. """ rbac.enforce('alarm_history', pecan.request) q = q or [] # allow history to be returned for deleted alarms, but scope changes # returned to those carried out on behalf of the auth'd tenant, to # avoid inappropriate cross-tenant visibility of alarm history auth_project = rbac.get_limited_to_project(pecan.request.headers) conn = pecan.request.alarm_storage_conn kwargs = v2_utils.query_to_kwargs( q, conn.get_alarm_changes, ['on_behalf_of', 'alarm_id']) return [AlarmChange.from_db_model(ac) for ac in conn.get_alarm_changes(self._id, auth_project, **kwargs)]
def get_all(self, q=None): """Return all alarms, based on the query provided. :param q: Filter rules for the alarms to be returned. """ rbac.enforce('get_alarms', pecan.request.headers, pecan.request.enforcer) q = q or [] # Timestamp is not supported field for Simple Alarm queries kwargs = v2_utils.query_to_kwargs( q, pecan.request.alarm_storage_conn.get_alarms, allow_timestamps=False) return [ Alarm.from_db_model(m) for m in pecan.request.alarm_storage_conn.get_alarms(**kwargs) ]
def put_state(self, state): """Set the state of this alarm. :param state: an alarm state within the request body. """ rbac.enforce('change_alarm_state', pecan.request) # note(sileht): body are not validated by wsme # Workaround for https://bugs.launchpad.net/wsme/+bug/1227229 if state not in state_kind: raise base.ClientSideError(_("state invalid")) now = timeutils.utcnow() alarm = self._alarm() alarm.state = state alarm.state_timestamp = now alarm = self.conn.update_alarm(alarm) change = {'state': alarm.state} self._record_change(change, now, on_behalf_of=alarm.project_id, type=alarm_models.AlarmChange.STATE_TRANSITION) return alarm.state
def history(self, q=None): """Assembles the alarm history requested. :param q: Filter rules for the changes to be described. """ rbac.enforce('alarm_history', pecan.request.headers, pecan.request.enforcer) q = q or [] # allow history to be returned for deleted alarms, but scope changes # returned to those carried out on behalf of the auth'd tenant, to # avoid inappropriate cross-tenant visibility of alarm history auth_project = rbac.get_limited_to_project(pecan.request.headers, pecan.request.enforcer) conn = pecan.request.alarm_storage_conn kwargs = v2_utils.query_to_kwargs(q, conn.get_alarm_changes, ['on_behalf_of', 'alarm_id']) return [ AlarmChange.from_db_model(ac) for ac in conn.get_alarm_changes(self._id, auth_project, **kwargs) ]
def put_state(self, state): """Set the state of this alarm. :param state: an alarm state within the request body. """ rbac.enforce('change_alarm_state', pecan.request.headers, pecan.request.enforcer) # note(sileht): body are not validated by wsme # Workaround for https://bugs.launchpad.net/wsme/+bug/1227229 if state not in state_kind: raise base.ClientSideError(_("state invalid")) now = timeutils.utcnow() alarm = self._alarm() alarm.state = state alarm.state_timestamp = now alarm = self.conn.update_alarm(alarm) change = {'state': alarm.state} self._record_change(change, now, on_behalf_of=alarm.project_id, type=models.AlarmChange.STATE_TRANSITION) return alarm.state
def delete(self, project_id): """Delete quotas for the given project.""" rbac.enforce('delete_quotas', pecan.request.headers, pecan.request.enforcer, {}) pecan.request.storage.delete_quotas(project_id)
def get_all(self, q=None, sort=None, limit=None, marker=None): """Return all alarms, based on the query provided. :param q: Filter rules for the alarms to be returned. :param sort: A list of pairs of sort key and sort dir. :param limit: The maximum number of items to be return. :param marker: The pagination query marker. """ target = rbac.target_from_segregation_rule(pecan.request.headers, pecan.request.enforcer) rbac.enforce('get_alarms', pecan.request.headers, pecan.request.enforcer, target) q = q or [] filters = {} # Check field keys = set([query.field for query in q]) if not keys.issubset(ALARM_QUERY_FIELDS_ALLOWED): raise wsme.exc.InvalidInput( 'field', keys, 'only fields %s are allowed' % ALARM_QUERY_FIELDS_ALLOWED) # Check op ops = set([query.op for query in q]) if any([op not in ALARM_QUERY_OPS_ALLOWED for op in ops]): raise wsme.exc.InvalidInput( 'op', ops, 'only operations %s are allowed' % ALARM_QUERY_OPS_ALLOWED) if 'all_projects' in keys: if v2_utils.get_query_value(q, 'all_projects', 'boolean'): rbac.enforce('get_alarms:all_projects', pecan.request.headers, pecan.request.enforcer, target) keys.remove('all_projects') else: project_id = pecan.request.headers.get('X-Project-Id') is_admin = rbac.is_admin(pecan.request.headers) if not v2_utils.is_field_exist(q, 'project_id'): q.append( base.Query(field='project_id', op='eq', value=project_id)) keys.add('project_id') else: request_project = v2_utils.get_query_value(q, 'project_id') if not is_admin and request_project != project_id: raise base.ProjectNotAuthorized(request_project) for query in q: if query.field in keys: filters[query.field] = {query.op: query.get_value(query.type)} if sort or limit or marker: filters['pagination'] = v2_utils.get_pagination_options( sort, limit, marker, models.Alarm) LOG.debug('Getting alarms from database, filters: %s', filters) return [ Alarm.from_db_model_scrubbed(m) for m in pecan.request.storage.get_alarms(**filters) ]