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:
Andrey Pavlov
2015-08-20 10:33:39 +03:00
parent f084139cd7
commit c3b1c08a57
32 changed files with 764 additions and 33 deletions

View 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__))

View File

@@ -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,

View File

@@ -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)

View File

@@ -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

View File

@@ -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,

View File

@@ -35,6 +35,12 @@ DATA_SOURCE_SCHEMA = {
},
"credentials": {
"type": "object"
},
"is_public": {
"type": ["boolean", "null"],
},
"is_protected": {
"type": ["boolean", "null"],
}
},
"additionalProperties": False,

View File

@@ -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": []

View File

@@ -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": {

View File

@@ -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)

View File

@@ -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": []
}

View File

@@ -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,

View File

@@ -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,