def patch(self, audit, patch): """Update an existing audit. :param audit: UUID or name of an audit. :param patch: a json PATCH document to apply to this audit. """ if self.from_audits: raise exception.OperationNotPermitted context = pecan.request.context audit_to_update = api_utils.get_resource('Audit', audit, eager=True) policy.enforce(context, 'audit:update', audit_to_update, action='audit:update') try: audit_dict = audit_to_update.as_dict() initial_state = audit_dict['state'] new_state = api_utils.get_patch_value(patch, 'state') if not api_utils.check_audit_state_transition( patch, initial_state): error_message = _("State transition not allowed: " "(%(initial_state)s -> %(new_state)s)") raise exception.PatchError( patch=patch, reason=error_message % dict(initial_state=initial_state, new_state=new_state)) patch_path = api_utils.get_patch_key(patch, 'path') if patch_path in ('start_time', 'end_time'): patch_value = api_utils.get_patch_value(patch, patch_path) # convert string format to UTC time new_patch_value = wutils.parse_isodatetime( patch_value).replace(tzinfo=tz.tzlocal()).astimezone( tz.tzutc()).replace(tzinfo=None) api_utils.set_patch_value(patch, patch_path, new_patch_value) audit = Audit(**api_utils.apply_jsonpatch(audit_dict, patch)) except api_utils.JSONPATCH_EXCEPTIONS as e: raise exception.PatchError(patch=patch, reason=e) # Update only the fields that have changed for field in objects.Audit.fields: try: patch_val = getattr(audit, field) except AttributeError: # Ignore fields that aren't exposed in the API continue if patch_val == wtypes.Unset: patch_val = None if audit_to_update[field] != patch_val: audit_to_update[field] = patch_val audit_to_update.save() return Audit.convert_with_links(audit_to_update)
def patch(self, audit_uuid, patch): """Update an existing audit. :param audit_uuid: UUID of a audit. :param patch: a json PATCH document to apply to this audit. """ if self.from_audits: raise exception.OperationNotPermitted context = pecan.request.context audit_to_update = api_utils.get_resource('Audit', audit_uuid, eager=True) policy.enforce(context, 'audit:update', audit_to_update, action='audit:update') try: audit_dict = audit_to_update.as_dict() audit = Audit(**api_utils.apply_jsonpatch(audit_dict, patch)) except api_utils.JSONPATCH_EXCEPTIONS as e: raise exception.PatchError(patch=patch, reason=e) initial_state = audit_dict['state'] new_state = api_utils.get_patch_value(patch, 'state') allowed_states = ALLOWED_AUDIT_TRANSITIONS.get(initial_state, []) if new_state is not None and new_state not in allowed_states: error_message = _("State transition not allowed: " "(%(initial_state)s -> %(new_state)s)") raise exception.PatchError( patch=patch, reason=error_message % dict(initial_state=initial_state, new_state=new_state)) # Update only the fields that have changed for field in objects.Audit.fields: try: patch_val = getattr(audit, field) except AttributeError: # Ignore fields that aren't exposed in the API continue if patch_val == wtypes.Unset: patch_val = None if audit_to_update[field] != patch_val: audit_to_update[field] = patch_val audit_to_update.save() return Audit.convert_with_links(audit_to_update)
def patch(self, action_uuid, patch): """Update an existing action. :param action_uuid: UUID of a action. :param patch: a json PATCH document to apply to this action. """ # FIXME: blueprint edit-action-plan-flow raise exception.OperationNotPermitted( _("Cannot modify an action directly")) if self.from_actions: raise exception.OperationNotPermitted action_to_update = objects.Action.get_by_uuid(pecan.request.context, action_uuid) try: action_dict = action_to_update.as_dict() action = Action(**api_utils.apply_jsonpatch(action_dict, patch)) except api_utils.JSONPATCH_EXCEPTIONS as e: raise exception.PatchError(patch=patch, reason=e) # Update only the fields that have changed for field in objects.Action.fields: try: patch_val = getattr(action, field) except AttributeError: # Ignore fields that aren't exposed in the API continue if patch_val == wtypes.Unset: patch_val = None if action_to_update[field] != patch_val: action_to_update[field] = patch_val action_to_update.save() return Action.convert_with_links(action_to_update)
def validate(patch): serialized_patch = {'path': patch.path, 'op': patch.op} if patch.path in AuditPatchType.mandatory_attrs(): msg = _("%(field)s can't be updated.") raise exception.PatchError(patch=serialized_patch, reason=msg % dict(field=patch.path)) return types.JsonPatchType.validate(patch)
def patch(self, audit_uuid, patch): """Update an existing audit. :param audit_uuid: UUID of a audit. :param patch: a json PATCH document to apply to this audit. """ if self.from_audits: raise exception.OperationNotPermitted audit_to_update = objects.Audit.get_by_uuid(pecan.request.context, audit_uuid) try: audit_dict = audit_to_update.as_dict() audit = Audit(**api_utils.apply_jsonpatch(audit_dict, patch)) except api_utils.JSONPATCH_EXCEPTIONS as e: raise exception.PatchError(patch=patch, reason=e) # Update only the fields that have changed for field in objects.Audit.fields: try: patch_val = getattr(audit, field) except AttributeError: # Ignore fields that aren't exposed in the API continue if patch_val == wtypes.Unset: patch_val = None if audit_to_update[field] != patch_val: audit_to_update[field] = patch_val audit_to_update.save() return Audit.convert_with_links(audit_to_update)
def _validate_state(patch): serialized_patch = {'path': patch.path, 'op': patch.op} if patch.value is not wtypes.Unset: serialized_patch['value'] = patch.value # todo: use state machines to handle state transitions state_value = patch.value if state_value and not hasattr(ap_objects.State, state_value): msg = _("Invalid state: %(state)s") raise exception.PatchError(patch=serialized_patch, reason=msg % dict(state=state_value))
def validate(patch): def is_new_state_none(p): return p.path == '/state' and p.op == 'replace' and p.value is None serialized_patch = { 'path': patch.path, 'op': patch.op, 'value': patch.value } if (patch.path in AuditPatchType.mandatory_attrs() or is_new_state_none(patch)): msg = _("%(field)s can't be updated.") raise exception.PatchError(patch=serialized_patch, reason=msg % dict(field=patch.path)) return types.JsonPatchType.validate(patch)
def patch(self, audit_template, patch): """Update an existing audit template. :param audit template_uuid: UUID of a audit template. :param patch: a json PATCH document to apply to this audit template. """ if self.from_audit_templates: raise exception.OperationNotPermitted context = pecan.request.context audit_template_to_update = api_utils.get_resource('AuditTemplate', audit_template) policy.enforce(context, 'audit_template:update', audit_template_to_update, action='audit_template:update') if common_utils.is_uuid_like(audit_template): audit_template_to_update = objects.AuditTemplate.get_by_uuid( pecan.request.context, audit_template) else: audit_template_to_update = objects.AuditTemplate.get_by_name( pecan.request.context, audit_template) try: audit_template_dict = audit_template_to_update.as_dict() audit_template = AuditTemplate(**api_utils.apply_jsonpatch( audit_template_dict, patch)) except api_utils.JSONPATCH_EXCEPTIONS as e: raise exception.PatchError(patch=patch, reason=e) # Update only the fields that have changed for field in objects.AuditTemplate.fields: try: patch_val = getattr(audit_template, field) except AttributeError: # Ignore fields that aren't exposed in the API continue if patch_val == wtypes.Unset: patch_val = None if audit_template_to_update[field] != patch_val: audit_template_to_update[field] = patch_val audit_template_to_update.save() return AuditTemplate.convert_with_links(audit_template_to_update)
def patch(self, action_plan_uuid, patch): """Update an existing action plan. :param action_plan_uuid: UUID of a action plan. :param patch: a json PATCH document to apply to this action plan. """ if self.from_actionsPlans: raise exception.OperationNotPermitted context = pecan.request.context action_plan_to_update = api_utils.get_resource('ActionPlan', action_plan_uuid, eager=True) policy.enforce(context, 'action_plan:update', action_plan_to_update, action='action_plan:update') try: action_plan_dict = action_plan_to_update.as_dict() action_plan = ActionPlan( **api_utils.apply_jsonpatch(action_plan_dict, patch)) except api_utils.JSONPATCH_EXCEPTIONS as e: raise exception.PatchError(patch=patch, reason=e) launch_action_plan = False cancel_action_plan = False # transitions that are allowed via PATCH allowed_patch_transitions = [ (ap_objects.State.RECOMMENDED, ap_objects.State.PENDING), (ap_objects.State.RECOMMENDED, ap_objects.State.CANCELLED), (ap_objects.State.ONGOING, ap_objects.State.CANCELLING), (ap_objects.State.PENDING, ap_objects.State.CANCELLED), ] # todo: improve this in blueprint watcher-api-validation if hasattr(action_plan, 'state'): transition = (action_plan_to_update.state, action_plan.state) if transition not in allowed_patch_transitions: error_message = _("State transition not allowed: " "(%(initial_state)s -> %(new_state)s)") raise exception.PatchError( patch=patch, reason=error_message % dict(initial_state=action_plan_to_update.state, new_state=action_plan.state)) if action_plan.state == ap_objects.State.PENDING: launch_action_plan = True if action_plan.state == ap_objects.State.CANCELLED: cancel_action_plan = True # Update only the fields that have changed for field in objects.ActionPlan.fields: try: patch_val = getattr(action_plan, field) except AttributeError: # Ignore fields that aren't exposed in the API continue if patch_val == wtypes.Unset: patch_val = None if action_plan_to_update[field] != patch_val: action_plan_to_update[field] = patch_val if (field == 'state' and patch_val == objects.action_plan.State.PENDING): launch_action_plan = True action_plan_to_update.save() # NOTE: if action plan is cancelled from pending or recommended # state update action state here only if cancel_action_plan: filters = {'action_plan_uuid': action_plan.uuid} actions = objects.Action.list(pecan.request.context, filters=filters, eager=True) for a in actions: a.state = objects.action.State.CANCELLED a.save() if launch_action_plan: self.applier_client.launch_action_plan(pecan.request.context, action_plan.uuid) action_plan_to_update = objects.ActionPlan.get_by_uuid( pecan.request.context, action_plan_uuid) return ActionPlan.convert_with_links(action_plan_to_update)
def patch(self, action_plan_uuid, patch): """Update an existing action plan. :param action_plan_uuid: UUID of a action plan. :param patch: a json PATCH document to apply to this action plan. """ launch_action_plan = True if self.from_actionsPlans: raise exception.OperationNotPermitted action_plan_to_update = objects.ActionPlan.get_by_uuid( pecan.request.context, action_plan_uuid) try: action_plan_dict = action_plan_to_update.as_dict() action_plan = ActionPlan( **api_utils.apply_jsonpatch(action_plan_dict, patch)) except api_utils.JSONPATCH_EXCEPTIONS as e: raise exception.PatchError(patch=patch, reason=e) launch_action_plan = False # transitions that are allowed via PATCH allowed_patch_transitions = [ (ap_objects.State.RECOMMENDED, ap_objects.State.TRIGGERED), (ap_objects.State.RECOMMENDED, ap_objects.State.CANCELLED), (ap_objects.State.ONGOING, ap_objects.State.CANCELLED), (ap_objects.State.TRIGGERED, ap_objects.State.CANCELLED), ] # todo: improve this in blueprint watcher-api-validation if hasattr(action_plan, 'state'): transition = (action_plan_to_update.state, action_plan.state) if transition not in allowed_patch_transitions: error_message = _("State transition not allowed: " "(%(initial_state)s -> %(new_state)s)") raise exception.PatchError( patch=patch, reason=error_message % dict(initial_state=action_plan_to_update.state, new_state=action_plan.state)) if action_plan.state == ap_objects.State.TRIGGERED: launch_action_plan = True # Update only the fields that have changed for field in objects.ActionPlan.fields: try: patch_val = getattr(action_plan, field) except AttributeError: # Ignore fields that aren't exposed in the API continue if patch_val == wtypes.Unset: patch_val = None if action_plan_to_update[field] != patch_val: action_plan_to_update[field] = patch_val if (field == 'state' and patch_val == objects.action_plan.State.TRIGGERED): launch_action_plan = True action_plan_to_update.save() if launch_action_plan: applier_client = rpcapi.ApplierAPI() applier_client.launch_action_plan(pecan.request.context, action_plan.uuid) action_plan_to_update = objects.ActionPlan.get_by_uuid( pecan.request.context, action_plan_uuid) return ActionPlan.convert_with_links(action_plan_to_update)