def put(self, name): """Update a config value. Settings which have is_editable set to False, can only be edited when passing the force flag. If a schema is specified for the given setting, the new value must validate. """ sm = get_storage_manager() data = rest_utils.get_json_and_verify_params({ 'value': {}, 'force': { 'type': bool, 'optional': True } }) value = data['value'] force = data.get('force', False) inst = sm.get(models.Config, None, filters={'name': name}) if not inst.is_editable and not force: raise ConflictError('{0} is not editable'.format(name)) if inst.schema: try: jsonschema.validate(value, inst.schema) except jsonschema.ValidationError as e: raise ConflictError(e.args[0]) inst.value = value inst.updated_at = datetime.now() inst.updated_by = current_user sm.update(inst) return inst
def put(self, blueprint_id, **kwargs): """ Upload a blueprint (id specified) """ rest_utils.validate_inputs({'blueprint_id': blueprint_id}) visibility = rest_utils.get_visibility_parameter( optional=True, is_argument=True, valid_values=VisibilityState.STATES) # Fail fast if trying to upload a duplicate blueprint current_tenant = request.headers.get('tenant') if visibility == VisibilityState.GLOBAL: existing_duplicates = get_storage_manager().list( models.Blueprint, filters={'id': blueprint_id}) if existing_duplicates: raise IllegalActionError( "Can't set or create the resource `{0}`, it's visibility " "can't be global because it also exists in other " "tenants".format(blueprint_id)) else: existing_duplicates = get_storage_manager().list(models.Blueprint, filters={ 'id': blueprint_id, 'tenant_name': current_tenant }) if existing_duplicates: raise ConflictError( 'blueprint with id={0} already exists on tenant {1} or ' 'with global visibility'.format(blueprint_id, current_tenant)) return UploadedBlueprintsManager().\ receive_uploaded_data(data_id=blueprint_id, visibility=visibility)
def put(self, blueprint_id, **kwargs): """ Upload a blueprint (id specified) """ rest_utils.validate_inputs({'blueprint_id': blueprint_id}) args = get_args_and_verify_arguments([ Argument('async_upload', type=boolean, default=False), Argument('labels') ]) async_upload = args.async_upload visibility = rest_utils.get_visibility_parameter( optional=True, is_argument=True, valid_values=VisibilityState.STATES) labels = self._get_labels_from_args(args) # Fail fast if trying to upload a duplicate blueprint. # Allow overriding an existing blueprint which failed to upload current_tenant = request.headers.get('tenant') override_failed = False if visibility == VisibilityState.GLOBAL: existing_duplicates = get_storage_manager().list( models.Blueprint, filters={'id': blueprint_id}) if existing_duplicates: if existing_duplicates[0].state in \ BlueprintUploadState.FAILED_STATES: override_failed = True else: raise IllegalActionError( "Can't set or create the resource `{0}`, it's " "visibility can't be global because it also exists in " "other tenants".format(blueprint_id)) else: existing_duplicates = get_storage_manager().list(models.Blueprint, filters={ 'id': blueprint_id, 'tenant_name': current_tenant }) if existing_duplicates: if existing_duplicates[0].state in \ BlueprintUploadState.FAILED_STATES: override_failed = True else: raise ConflictError( 'blueprint with id={0} already exists on tenant {1} ' 'or with global visibility'.format( blueprint_id, current_tenant)) response = UploadedBlueprintsManager().\ receive_uploaded_data(data_id=blueprint_id, visibility=visibility, override_failed=override_failed, labels=labels) if not async_upload: sm = get_storage_manager() blueprint, _ = response response = rest_utils.get_uploaded_blueprint(sm, blueprint) return response
def update_db(self, config_dict, force=False): """ Update the config table in the DB with values passed in the config dictionary parameter """ from manager_rest.storage import models engine = create_engine(self.db_url) session = orm.Session(bind=engine) stored_configs = session.query(models.Config).all() config_mappings = [] for name, value in config_dict.items(): if name == 'ldap_ca_cert': cert = session.query(models.Certificate).filter_by( name=LDAP_CA_NAME, ).one_or_none() or models.Certificate() cert.name = LDAP_CA_NAME cert.value = value session.add(cert) continue entry = self._find_entry(stored_configs, name) if not entry.is_editable and not force: raise ConflictError('{0} is not editable'.format(entry.name)) if entry.schema: try: jsonschema.validate(config_dict[entry.name], entry.schema) except jsonschema.ValidationError as e: raise ConflictError( 'Error validating {name}: {err}'.format(name=name, err=e.args[0])) config_mappings.append({ 'name': entry.name, 'scope': entry.scope, 'value': value, 'updated_at': datetime.now(), '_updater_id': current_user.id, }) session.bulk_update_mappings(models.Config, config_mappings) session.commit() session.close() engine.dispose() for name, value in config_dict.items(): setattr(self, name, value)
def validate_no_active_updates_per_blueprint(self, blueprint_id, force=False): active_updates = self.list_plugins_updates(filters={ 'blueprint_id': blueprint_id, 'state': ACTIVE_STATES }).items if not active_updates: return if not force: raise ConflictError( 'There are plugins updates still active, update IDs: ' '{0}'.format(', '.join(u.id for u in active_updates))) execs_to_updates = { u.execution_id: u for u in active_updates if u.execution_id } running_updates = [ execs_to_updates[execution.id] for execution in self.sm.list(models.Execution, filters={ 'id': execs_to_updates.keys() }).items if execution.status not in ExecutionState.END_STATES ] if running_updates: raise ConflictError( 'There are plugins updates still active; the "force" flag ' 'was used yet these updates have actual executions running ' 'update IDs: {0}'.format(', '.join(u.id for u in running_updates))) else: # the active updates aren't really active - either their # executions were failed/cancelled, or the update failed at # the finalizing stage. # updating their states to failed and continuing. for plugins_update in active_updates: plugins_update.state = STATES.FAILED self.sm.update(plugins_update)
def validate_no_active_updates_per_blueprint(self, blueprint_id): active_updates = self.list_plugins_updates(filters={ 'blueprint_id': blueprint_id, 'state': ACTIVE_STATES }).items if not active_updates: return raise ConflictError( 'There are plugins updates still active, update IDs: ' '{0}'.format(', '.join(u.id for u in active_updates)))
def update_db(self, config_dict, force=False): """ Update the config table in the DB with values passed in the config dictionary parameter """ from manager_rest.storage import models from sqlalchemy import create_engine, orm engine = create_engine(self.db_url) session = orm.Session(bind=engine) stored_configs = session.query(models.Config).all() config_mappings = [] for name, value in config_dict.items(): entry = self._find_entry(stored_configs, name) if not entry.is_editable and not force: raise ConflictError('{0} is not editable'.format(entry.name)) if entry.schema: try: jsonschema.validate(config_dict[entry.name], entry.schema) except jsonschema.ValidationError as e: raise ConflictError(e.args[0]) config_mappings.append({ 'name': entry.name, 'scope': entry.scope, 'value': value, 'updated_at': datetime.now(), '_updater_id': current_user.id, }) session.bulk_update_mappings(models.Config, config_mappings) session.commit() session.close() engine.dispose() for name, value in config_dict.items(): setattr(self, name, value)
def patch(self, blueprint_id, **kwargs): """ Update a blueprint. Used for updating the blueprint's state (and error) while uploading, and updating the blueprint's other attributes upon a successful upload. This method is for internal use only. """ if not request.json: raise IllegalActionError('Update a blueprint request must include ' 'at least one parameter to update') request_schema = { 'plan': { 'type': dict, 'optional': True }, 'description': { 'type': text_type, 'optional': True }, 'main_file_name': { 'type': text_type, 'optional': True }, 'visibility': { 'type': text_type, 'optional': True }, 'state': { 'type': text_type, 'optional': True }, 'error': { 'type': text_type, 'optional': True }, 'error_traceback': { 'type': text_type, 'optional': True }, 'labels': { 'type': list, 'optional': True } } request_dict = rest_utils.get_json_and_verify_params(request_schema) invalid_params = set(request_dict.keys()) - set(request_schema.keys()) if invalid_params: raise BadParametersError("Unknown parameters: {}".format( ','.join(invalid_params))) sm = get_storage_manager() rm = get_resource_manager() blueprint = sm.get(models.Blueprint, blueprint_id) # if finished blueprint validation - cleanup DB entry # and uploaded blueprints folder if blueprint.state == BlueprintUploadState.VALIDATING: uploaded_blueprint_path = join( config.instance.file_server_root, FILE_SERVER_UPLOADED_BLUEPRINTS_FOLDER, blueprint.tenant.name, blueprint.id) remove(uploaded_blueprint_path) sm.delete(blueprint) return blueprint # set blueprint visibility visibility = request_dict.get('visibility') if visibility: if visibility not in VisibilityState.STATES: raise BadParametersError( "Invalid visibility: `{0}`. Valid visibility's values " "are: {1}".format(visibility, VisibilityState.STATES)) blueprint.visibility = visibility # set other blueprint attributes. if 'plan' in request_dict: blueprint.plan = request_dict['plan'] if 'description' in request_dict: blueprint.description = request_dict['description'] if 'main_file_name' in request_dict: blueprint.main_file_name = request_dict['main_file_name'] provided_labels = request_dict.get('labels') if request_dict.get('plan'): dsl_labels = request_dict['plan'].get('labels', {}) csys_obj_parents = dsl_labels.get('csys-obj-parent') if csys_obj_parents: dep_parents = csys_obj_parents['values'] missing_parents = rm.get_missing_deployment_parents( dep_parents) if missing_parents: raise DeploymentParentNotFound( 'Blueprint {0}: is referencing deployments' ' using label `csys-obj-parent` that does not exist, ' 'make sure that deployment(s) {1} exist before ' 'creating blueprint'.format(blueprint.id, ','.join(missing_parents))) # set blueprint state state = request_dict.get('state') if state: if state not in BlueprintUploadState.STATES: raise BadParametersError( "Invalid state: `{0}`. Valid blueprint state values are: " "{1}".format(state, BlueprintUploadState.STATES)) if (state != BlueprintUploadState.UPLOADED and provided_labels is not None): raise ConflictError( 'Blueprint labels can be created only if the provided ' 'blueprint state is {0}'.format( BlueprintUploadState.UPLOADED)) blueprint.state = state blueprint.error = request_dict.get('error') blueprint.error_traceback = request_dict.get('error_traceback') # On finalizing the blueprint upload, extract archive to file # server if state == BlueprintUploadState.UPLOADED: UploadedBlueprintsManager(). \ extract_blueprint_archive_to_file_server( blueprint_id=blueprint_id, tenant=blueprint.tenant.name) _create_blueprint_labels(blueprint, provided_labels) # If failed for any reason, cleanup the blueprint archive from # server elif state in BlueprintUploadState.FAILED_STATES: UploadedBlueprintsManager(). \ cleanup_blueprint_archive_from_file_server( blueprint_id=blueprint_id, tenant=blueprint.tenant.name) else: # Updating the blueprint not as part of the upload process if provided_labels is not None: if blueprint.state != BlueprintUploadState.UPLOADED: raise ConflictError( 'Blueprint labels can only be updated if the blueprint' ' was uploaded successfully') rm = get_resource_manager() labels_list = rest_utils.get_labels_list(provided_labels) rm.update_resource_labels(models.BlueprintLabel, blueprint, labels_list) blueprint.updated_at = get_formatted_timestamp() return sm.update(blueprint)