Adding shared and protected resources support
Adding shared across tenants and protected from modifictaion resources support. Implemented for clusters, cluster templates, node group templates, data sources, job executions, jobs, job binaries and job binary internals. Changes: * Added validation checks to service.validations.acl * Changed validation schema's to support "is_public" and "is_protected" fields * Added validation of "is_protected" field for update(scale/cancel) and delete methods of corresponding resources * Extended get and list methods outputs with "is_public" resources * Added unit tests for "is_public" and "is_protected" resources Change-Id: I1a3cb14b8de70256e6aa27312dde341e85fc376c Partially-Implements: blueprint shared-protected-resources
This commit is contained in:
52
sahara/service/validations/acl.py
Normal file
52
sahara/service/validations/acl.py
Normal file
@@ -0,0 +1,52 @@
|
||||
# Copyright (c) 2013 Mirantis Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
# implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
|
||||
from sahara import exceptions as ex
|
||||
from sahara.i18n import _
|
||||
|
||||
|
||||
def check_tenant_for_delete(context, object):
|
||||
if object.tenant_id != context.tenant_id:
|
||||
raise ex.DeletionFailed(
|
||||
_("{object} with id '{id}' could not be deleted because "
|
||||
"it wasn't created in this tenant").format(
|
||||
object=type(object).__name__, id=object.id))
|
||||
|
||||
|
||||
def check_tenant_for_update(context, object):
|
||||
if object.tenant_id != context.tenant_id:
|
||||
raise ex.UpdateFailedException(
|
||||
object.id,
|
||||
_("{object} with id '%s' could not be updated because "
|
||||
"it wasn't created in this tenant").format(
|
||||
object=type(object).__name__))
|
||||
|
||||
|
||||
def check_protected_from_delete(object):
|
||||
if object.is_protected:
|
||||
raise ex.DeletionFailed(
|
||||
_("{object} with id '{id}' could not be deleted because "
|
||||
"it's marked as protected").format(
|
||||
object=type(object).__name__, id=object.id))
|
||||
|
||||
|
||||
def check_protected_from_update(object, data):
|
||||
if object.is_protected and data.get('is_protected', True):
|
||||
raise ex.UpdateFailedException(
|
||||
object.id,
|
||||
_("{object} with id '%s' could not be updated "
|
||||
"because it's marked as protected").format(
|
||||
object=type(object).__name__))
|
||||
@@ -93,6 +93,12 @@ CLUSTER_TEMPLATE_SCHEMA = {
|
||||
"shares": copy.deepcopy(shares.SHARE_SCHEMA),
|
||||
"use_autoconfig": {
|
||||
"type": ["boolean", "null"],
|
||||
},
|
||||
"is_public": {
|
||||
"type": ["boolean", "null"],
|
||||
},
|
||||
"is_protected": {
|
||||
"type": ["boolean", "null"],
|
||||
}
|
||||
},
|
||||
"additionalProperties": False,
|
||||
|
||||
@@ -15,9 +15,11 @@
|
||||
|
||||
from oslo_config import cfg
|
||||
|
||||
from sahara import context
|
||||
import sahara.exceptions as ex
|
||||
from sahara.i18n import _
|
||||
import sahara.service.api as api
|
||||
from sahara.service.validations import acl
|
||||
import sahara.service.validations.base as b
|
||||
|
||||
|
||||
@@ -101,3 +103,10 @@ def _get_cluster_field(cluster, field):
|
||||
return cluster_template[field]
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def check_cluster_delete(cluster_id, **kwargs):
|
||||
cluster = api.get_cluster(cluster_id)
|
||||
|
||||
acl.check_tenant_for_delete(context.current(), cluster)
|
||||
acl.check_protected_from_delete(cluster)
|
||||
|
||||
@@ -13,19 +13,28 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
|
||||
from sahara import context
|
||||
import sahara.exceptions as ex
|
||||
from sahara.i18n import _
|
||||
import sahara.plugins.base as plugin_base
|
||||
import sahara.service.api as api
|
||||
from sahara.service.validations import acl
|
||||
import sahara.service.validations.base as b
|
||||
from sahara.utils import cluster as c_u
|
||||
|
||||
|
||||
def check_cluster_scaling(data, cluster_id, **kwargs):
|
||||
ctx = context.current()
|
||||
cluster = api.get_cluster(id=cluster_id)
|
||||
|
||||
if cluster is None:
|
||||
raise ex.NotFoundException(
|
||||
{'id': cluster_id}, _('Object with %s not found'))
|
||||
|
||||
acl.check_tenant_for_update(ctx, cluster)
|
||||
acl.check_protected_from_update(cluster, data)
|
||||
|
||||
cluster_engine = cluster.sahara_info.get(
|
||||
'infrastructure_engine') if cluster.sahara_info else None
|
||||
|
||||
|
||||
@@ -60,6 +60,12 @@ CLUSTER_UPDATE_SCHEMA = {
|
||||
"minLength": 1,
|
||||
"maxLength": 50,
|
||||
"format": "valid_name_hostname",
|
||||
},
|
||||
"is_public": {
|
||||
"type": ["boolean", "null"],
|
||||
},
|
||||
"is_protected": {
|
||||
"type": ["boolean", "null"],
|
||||
}
|
||||
},
|
||||
"additionalProperties": False,
|
||||
|
||||
@@ -35,6 +35,12 @@ DATA_SOURCE_SCHEMA = {
|
||||
},
|
||||
"credentials": {
|
||||
"type": "object"
|
||||
},
|
||||
"is_public": {
|
||||
"type": ["boolean", "null"],
|
||||
},
|
||||
"is_protected": {
|
||||
"type": ["boolean", "null"],
|
||||
}
|
||||
},
|
||||
"additionalProperties": False,
|
||||
|
||||
@@ -23,6 +23,12 @@ JOB_BINARY_UPDATE_SCHEMA = {
|
||||
"maxLength": 50,
|
||||
"format": "valid_name"
|
||||
},
|
||||
"is_public": {
|
||||
"type": ["boolean", "null"],
|
||||
},
|
||||
"is_protected": {
|
||||
"type": ["boolean", "null"],
|
||||
}
|
||||
},
|
||||
"additionalProperties": False,
|
||||
"required": []
|
||||
|
||||
@@ -31,6 +31,12 @@ JOB_BINARY_SCHEMA = {
|
||||
"type": "string",
|
||||
"format": "valid_job_location"
|
||||
},
|
||||
"is_public": {
|
||||
"type": ["boolean", "null"],
|
||||
},
|
||||
"is_protected": {
|
||||
"type": ["boolean", "null"],
|
||||
},
|
||||
# extra is simple_config for now because we may need not only
|
||||
# user-password it the case of external storage
|
||||
"extra": {
|
||||
|
||||
@@ -18,6 +18,7 @@ from sahara import context
|
||||
from sahara import exceptions as ex
|
||||
from sahara.i18n import _
|
||||
from sahara.plugins import base as plugin_base
|
||||
from sahara.service.validations import acl
|
||||
import sahara.service.validations.edp.base as b
|
||||
import sahara.service.validations.edp.job_interface as j_i
|
||||
|
||||
@@ -83,3 +84,27 @@ def check_data_sources(data, job):
|
||||
b.check_data_source_exists(data['output_id'])
|
||||
|
||||
b.check_data_sources_are_different(data['input_id'], data['output_id'])
|
||||
|
||||
|
||||
def check_job_execution_cancel(job_execution_id, **kwargs):
|
||||
ctx = context.current()
|
||||
je = conductor.job_execution_get(ctx, job_execution_id)
|
||||
|
||||
if je.tenant_id != ctx.tenant_id:
|
||||
raise ex.CancelingFailed(
|
||||
_("Job execution with id '%s' cannot be canceled "
|
||||
"because it wasn't created in this tenant")
|
||||
% job_execution_id)
|
||||
|
||||
if je.is_protected:
|
||||
raise ex.CancelingFailed(
|
||||
_("Job Execution with id '%s' cannot be canceled "
|
||||
"because it's marked as protected") % job_execution_id)
|
||||
|
||||
|
||||
def check_job_execution_delete(job_execution_id, **kwargs):
|
||||
ctx = context.current()
|
||||
je = conductor.job_execution_get(ctx, job_execution_id)
|
||||
|
||||
acl.check_tenant_for_delete(ctx, je)
|
||||
acl.check_protected_from_delete(je)
|
||||
|
||||
@@ -36,6 +36,12 @@ JOB_EXEC_SCHEMA = {
|
||||
"type": "simple_config",
|
||||
},
|
||||
"job_configs": b.job_configs,
|
||||
"is_public": {
|
||||
"type": ["boolean", "null"],
|
||||
},
|
||||
"is_protected": {
|
||||
"type": ["boolean", "null"],
|
||||
}
|
||||
},
|
||||
"additionalProperties": False,
|
||||
"required": [
|
||||
@@ -46,7 +52,14 @@ JOB_EXEC_SCHEMA = {
|
||||
|
||||
JOB_EXEC_UPDATE_SCHEMA = {
|
||||
"type": "object",
|
||||
"properties": {},
|
||||
"properties": {
|
||||
"is_public": {
|
||||
"type": ["boolean", "null"],
|
||||
},
|
||||
"is_protected": {
|
||||
"type": ["boolean", "null"],
|
||||
}
|
||||
},
|
||||
"additionalProperties": False,
|
||||
"required": []
|
||||
}
|
||||
|
||||
@@ -53,7 +53,13 @@ JOB_SCHEMA = {
|
||||
"streaming": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"interface": j_i.INTERFACE_ARGUMENT_SCHEMA
|
||||
"interface": j_i.INTERFACE_ARGUMENT_SCHEMA,
|
||||
"is_public": {
|
||||
"type": ["boolean", "null"],
|
||||
},
|
||||
"is_protected": {
|
||||
"type": ["boolean", "null"],
|
||||
}
|
||||
},
|
||||
"additionalProperties": False,
|
||||
"required": [
|
||||
@@ -74,6 +80,12 @@ JOB_UPDATE_SCHEMA = {
|
||||
},
|
||||
"description": {
|
||||
"type": ["string", "null"]
|
||||
},
|
||||
"is_public": {
|
||||
"type": ["boolean", "null"],
|
||||
},
|
||||
"is_protected": {
|
||||
"type": ["boolean", "null"],
|
||||
}
|
||||
},
|
||||
"additionalProperties": False,
|
||||
|
||||
@@ -92,6 +92,12 @@ NODE_GROUP_TEMPLATE_SCHEMA = {
|
||||
"shares": copy.deepcopy(shares.SHARE_SCHEMA),
|
||||
"use_autoconfig": {
|
||||
"type": ["boolean", "null"]
|
||||
},
|
||||
"is_public": {
|
||||
"type": ["boolean", "null"],
|
||||
},
|
||||
"is_protected": {
|
||||
"type": ["boolean", "null"],
|
||||
}
|
||||
},
|
||||
"additionalProperties": False,
|
||||
|
||||
Reference in New Issue
Block a user