add initial v2 api
this change implements the initial v2 experimental api. it is implemented as an optional series of class that can be configured through the paste deploy configuration file. * add wsgi auth validator * add middleware router * add v2 endpoints * add v2 flask blueprint object * add optional paste filter and composite * add developer docs for v2 api Change-Id: I74627c0879851b354b5043f8a6ff91bae8438bb1 Partial-Implements: bp v2-api-experimental-impl
This commit is contained in:
parent
493561692c
commit
a9137addb4
99
doc/source/devref/apiv2.rst
Normal file
99
doc/source/devref/apiv2.rst
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
API Version 2 Development
|
||||||
|
=========================
|
||||||
|
|
||||||
|
The sahara project is currently in the process of creating a new
|
||||||
|
RESTful application programming interface (API). This interface is
|
||||||
|
experimental and will not be enabled until it has achieved feature
|
||||||
|
parity with the current (version 1.1) API.
|
||||||
|
|
||||||
|
This document defines the steps necessary to enable and communicate
|
||||||
|
with the new API. This API has a few fundamental changes from the
|
||||||
|
previous APIs and they should be noted before proceeding with
|
||||||
|
development work.
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
This API is currently marked as experimental. It is not supported
|
||||||
|
by the sahara python client. These instructions are included purely
|
||||||
|
for developers who wish to help participate in the development
|
||||||
|
effort.
|
||||||
|
|
||||||
|
Enabling the experimental API
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
|
There are a few changes to the WSGI pipeline that must be made to
|
||||||
|
enable the new v2 API. These changes will leave the 1.0 and 1.1 API
|
||||||
|
versions in place and will not adjust their communication parameters.
|
||||||
|
|
||||||
|
To begin, uncomment, or add, the following sections in your
|
||||||
|
api-paste.ini file:
|
||||||
|
|
||||||
|
.. sourcecode:: ini
|
||||||
|
|
||||||
|
[app:sahara_apiv2]
|
||||||
|
paste.app_factory = sahara.api.middleware.sahara_middleware:RouterV2.factory
|
||||||
|
|
||||||
|
[filter:auth_validator_v2]
|
||||||
|
paste.filter_factory = sahara.api.middleware.auth_valid:AuthValidatorV2.factory
|
||||||
|
|
||||||
|
These lines define a new authentication filter for the v2 API, and
|
||||||
|
define the application that will handle the new calls.
|
||||||
|
|
||||||
|
With these new entries in the paste configuration, we can now enable
|
||||||
|
them with the following changes to the api-paste.ini file:
|
||||||
|
|
||||||
|
.. sourcecode:: ini
|
||||||
|
|
||||||
|
[pipeline:sahara]
|
||||||
|
pipeline = cors request_id acl auth_validator_v2 sahara_api
|
||||||
|
|
||||||
|
[composite:sahara_api]
|
||||||
|
use = egg:Paste#urlmap
|
||||||
|
/: sahara_apiv2
|
||||||
|
|
||||||
|
There are 2 significant changes occurring here; changing the
|
||||||
|
authentication validator in the pipline, and changing the root "/"
|
||||||
|
application to the new v2 handler.
|
||||||
|
|
||||||
|
At this point the sahara API server should be configured to accept
|
||||||
|
requests on the new v2 endpoints.
|
||||||
|
|
||||||
|
Communicating with the v2 API
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
|
The v2 API makes at least one major change from the previous versions,
|
||||||
|
removing the OpenStack project identifier from the URL. Instead of
|
||||||
|
adding this UUID to the URL, it is now required to be included as a
|
||||||
|
header named ``OpenStack-Project-ID``.
|
||||||
|
|
||||||
|
For example, in previous versions of the API, a call to get the list of
|
||||||
|
clusters for project "12345678-1234-1234-1234-123456789ABC" would have
|
||||||
|
been made as follows::
|
||||||
|
|
||||||
|
GET /v1.1/12345678-1234-1234-1234-123456789ABC/clusters
|
||||||
|
X-Auth-Token: {valid auth token}
|
||||||
|
|
||||||
|
This call would now be made to the following URL, while including the
|
||||||
|
project identifier in a header named ``OpenStack-Project-ID``::
|
||||||
|
|
||||||
|
GET /v2/clusters
|
||||||
|
X-Auth-Token: {valid auth token}
|
||||||
|
OpenStack-Project-ID: 12345678-1234-1234-1234-123456789ABC
|
||||||
|
|
||||||
|
Using a tool like `HTTPie <https://github.com/jkbrzt/httpie>`_, the
|
||||||
|
same request could be made like this::
|
||||||
|
|
||||||
|
$ httpie http://{sahara service ip:port}/v2/clusters \
|
||||||
|
X-Auth-Token:{valid auth token} \
|
||||||
|
OpenStack-Project-ID:12345678-1234-1234-1234-123456789ABC
|
||||||
|
|
||||||
|
Following the implementation progress
|
||||||
|
-------------------------------------
|
||||||
|
|
||||||
|
As the creation of this API will be under regular change until it moves
|
||||||
|
out of the experimental phase, a wiki page has been established to help
|
||||||
|
track the progress.
|
||||||
|
|
||||||
|
https://wiki.openstack.org/wiki/Sahara/api-v2
|
||||||
|
|
||||||
|
This page will help to coordinate the various reviews, specs, and work
|
||||||
|
items that are a continuing facet of this work.
|
@ -95,6 +95,7 @@ Developer Guide
|
|||||||
devref/adding_database_migrations
|
devref/adding_database_migrations
|
||||||
devref/testing
|
devref/testing
|
||||||
devref/log.guidelines
|
devref/log.guidelines
|
||||||
|
devref/apiv2
|
||||||
|
|
||||||
**Background Concepts for Sahara**
|
**Background Concepts for Sahara**
|
||||||
|
|
||||||
|
@ -8,6 +8,10 @@ use = egg:Paste#urlmap
|
|||||||
[app:sahara_apiv11]
|
[app:sahara_apiv11]
|
||||||
paste.app_factory = sahara.api.middleware.sahara_middleware:Router.factory
|
paste.app_factory = sahara.api.middleware.sahara_middleware:Router.factory
|
||||||
|
|
||||||
|
# this app is only for use with the experimental v2 API
|
||||||
|
# [app:sahara_apiv2]
|
||||||
|
# paste.app_factory = sahara.api.middleware.sahara_middleware:RouterV2.factory
|
||||||
|
|
||||||
[filter:cors]
|
[filter:cors]
|
||||||
paste.filter_factory = oslo_middleware.cors:filter_factory
|
paste.filter_factory = oslo_middleware.cors:filter_factory
|
||||||
oslo_config_project = sahara
|
oslo_config_project = sahara
|
||||||
@ -24,5 +28,9 @@ paste.filter_factory = keystonemiddleware.auth_token:filter_factory
|
|||||||
[filter:auth_validator]
|
[filter:auth_validator]
|
||||||
paste.filter_factory = sahara.api.middleware.auth_valid:AuthValidator.factory
|
paste.filter_factory = sahara.api.middleware.auth_valid:AuthValidator.factory
|
||||||
|
|
||||||
|
# this filter is only for use with the experimental v2 API
|
||||||
|
# [filter:auth_validator_v2]
|
||||||
|
# paste.filter_factory = sahara.api.middleware.auth_valid:AuthValidatorV2.factory
|
||||||
|
|
||||||
[filter:debug]
|
[filter:debug]
|
||||||
paste.filter_factory = oslo_middleware.debug:Debug.factory
|
paste.filter_factory = oslo_middleware.debug:Debug.factory
|
||||||
|
@ -58,3 +58,45 @@ class AuthValidator(base.Middleware):
|
|||||||
raise ex.HTTPUnauthorized(
|
raise ex.HTTPUnauthorized(
|
||||||
_('Token tenant != requested tenant'))
|
_('Token tenant != requested tenant'))
|
||||||
return self.application
|
return self.application
|
||||||
|
|
||||||
|
|
||||||
|
class AuthValidatorV2(base.Middleware):
|
||||||
|
|
||||||
|
"""Handles token auth results and tenants."""
|
||||||
|
|
||||||
|
@webob.dec.wsgify
|
||||||
|
def __call__(self, req):
|
||||||
|
"""Ensures that the requested and token tenants match
|
||||||
|
|
||||||
|
Handle incoming requests by checking tenant info from the
|
||||||
|
headers and url ({tenant_id} url attribute), if using v1 or v1.1
|
||||||
|
APIs. If using the v2 API, this function will check the token
|
||||||
|
tenant and the requested tenent in the headers.
|
||||||
|
|
||||||
|
Pass request downstream on success.
|
||||||
|
Reject request if tenant_id from headers is not equal to the
|
||||||
|
tenant_id from url or v2 project header.
|
||||||
|
"""
|
||||||
|
path = req.environ['PATH_INFO']
|
||||||
|
if path != '/':
|
||||||
|
token_tenant = req.environ.get("HTTP_X_TENANT_ID")
|
||||||
|
if not token_tenant:
|
||||||
|
LOG.warning(_LW("Can't get tenant_id from env"))
|
||||||
|
raise ex.HTTPServiceUnavailable()
|
||||||
|
|
||||||
|
if path.startswith('/v2'):
|
||||||
|
version, rest = commons.split_path(path, 2, 2, True)
|
||||||
|
requested_tenant = req.headers.get('OpenStack-Project-ID')
|
||||||
|
else:
|
||||||
|
version, requested_tenant, rest = commons.split_path(
|
||||||
|
path, 3, 3, True)
|
||||||
|
|
||||||
|
if not version or not requested_tenant or not rest:
|
||||||
|
LOG.warning(_LW("Incorrect path: {path}").format(path=path))
|
||||||
|
raise ex.HTTPNotFound(_("Incorrect path"))
|
||||||
|
|
||||||
|
if token_tenant != requested_tenant:
|
||||||
|
LOG.debug("Unauthorized: token tenant != requested tenant")
|
||||||
|
raise ex.HTTPUnauthorized(
|
||||||
|
_('Token tenant != requested tenant'))
|
||||||
|
return self.application
|
||||||
|
@ -20,6 +20,7 @@ from werkzeug import exceptions as werkzeug_exceptions
|
|||||||
|
|
||||||
from sahara.api import v10 as api_v10
|
from sahara.api import v10 as api_v10
|
||||||
from sahara.api import v11 as api_v11
|
from sahara.api import v11 as api_v11
|
||||||
|
from sahara.api import v2 as api_v2
|
||||||
from sahara import context
|
from sahara import context
|
||||||
from sahara.utils import api as api_utils
|
from sahara.utils import api as api_utils
|
||||||
|
|
||||||
@ -27,23 +28,26 @@ from sahara.utils import api as api_utils
|
|||||||
CONF = cfg.CONF
|
CONF = cfg.CONF
|
||||||
|
|
||||||
|
|
||||||
def build_app():
|
def build_app(version_response=None):
|
||||||
"""App builder (wsgi).
|
"""App builder (wsgi).
|
||||||
|
|
||||||
Entry point for Sahara REST API server
|
Entry point for Sahara REST API server
|
||||||
"""
|
"""
|
||||||
app = flask.Flask('sahara.api')
|
app = flask.Flask('sahara.api')
|
||||||
|
|
||||||
@app.route('/', methods=['GET'])
|
version_response = (version_response or
|
||||||
def version_list():
|
{
|
||||||
context.set_ctx(None)
|
|
||||||
return api_utils.render({
|
|
||||||
"versions": [
|
"versions": [
|
||||||
{"id": "v1.0", "status": "SUPPORTED"},
|
{"id": "v1.0", "status": "SUPPORTED"},
|
||||||
{"id": "v1.1", "status": "CURRENT"}
|
{"id": "v1.1", "status": "CURRENT"}
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@app.route('/', methods=['GET'])
|
||||||
|
def version_list():
|
||||||
|
context.set_ctx(None)
|
||||||
|
return api_utils.render(version_response)
|
||||||
|
|
||||||
@app.teardown_request
|
@app.teardown_request
|
||||||
def teardown_request(_ex=None):
|
def teardown_request(_ex=None):
|
||||||
context.set_ctx(None)
|
context.set_ctx(None)
|
||||||
@ -69,6 +73,25 @@ def build_app():
|
|||||||
return app
|
return app
|
||||||
|
|
||||||
|
|
||||||
|
def build_v2_app():
|
||||||
|
"""App builder (wsgi).
|
||||||
|
|
||||||
|
Entry point for Experimental V2 Sahara REST API server
|
||||||
|
"""
|
||||||
|
version_response = {
|
||||||
|
"versions": [
|
||||||
|
{"id": "v1.0", "status": "SUPPORTED"},
|
||||||
|
{"id": "v1.1", "status": "CURRENT"},
|
||||||
|
{"id": "v2", "status": "EXPERIMENTAL"}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
app = build_app(version_response)
|
||||||
|
|
||||||
|
api_v2.register_blueprints(app, url_prefix='/v2')
|
||||||
|
|
||||||
|
return app
|
||||||
|
|
||||||
|
|
||||||
class Router(object):
|
class Router(object):
|
||||||
def __call__(self, environ, response):
|
def __call__(self, environ, response):
|
||||||
return self.app(environ, response)
|
return self.app(environ, response)
|
||||||
@ -77,3 +100,13 @@ class Router(object):
|
|||||||
def factory(cls, global_config, **local_config):
|
def factory(cls, global_config, **local_config):
|
||||||
cls.app = build_app()
|
cls.app = build_app()
|
||||||
return cls(**local_config)
|
return cls(**local_config)
|
||||||
|
|
||||||
|
|
||||||
|
class RouterV2(object):
|
||||||
|
def __call__(self, environ, response):
|
||||||
|
return self.app(environ, response)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def factory(cls, global_config, **local_config):
|
||||||
|
cls.app = build_v2_app()
|
||||||
|
return cls(**local_config)
|
||||||
|
66
sahara/api/v2/__init__.py
Normal file
66
sahara/api/v2/__init__.py
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
# Copyright (c) 2016 Red Hat, 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.
|
||||||
|
|
||||||
|
"""
|
||||||
|
v2 API interface package
|
||||||
|
|
||||||
|
This package contains the endpoint definitions for the version 2 API.
|
||||||
|
The modules in this package are named in accordance with the top-level
|
||||||
|
resource primitives they represent.
|
||||||
|
|
||||||
|
This module provides a convenience function to register all the
|
||||||
|
endpoint blueprints to the ``/v2`` root.
|
||||||
|
|
||||||
|
When creating new endpoint modules, the following steps should be taken
|
||||||
|
to ensure they are properly registered with the Flask application:
|
||||||
|
* create the module file with a name that indicates its endpoint
|
||||||
|
* add a sahara.utils.api.RestV2 blueprint object
|
||||||
|
* add an import to this module (__init__.py)
|
||||||
|
* add a registration line for the new endpoint to the
|
||||||
|
register_blueprint function
|
||||||
|
"""
|
||||||
|
|
||||||
|
from sahara.api.v2 import cluster_templates
|
||||||
|
from sahara.api.v2 import clusters
|
||||||
|
from sahara.api.v2 import data_sources
|
||||||
|
from sahara.api.v2 import images
|
||||||
|
from sahara.api.v2 import job_binaries
|
||||||
|
from sahara.api.v2 import job_executions
|
||||||
|
from sahara.api.v2 import job_types
|
||||||
|
from sahara.api.v2 import jobs
|
||||||
|
from sahara.api.v2 import node_group_templates
|
||||||
|
from sahara.api.v2 import plugins
|
||||||
|
|
||||||
|
|
||||||
|
def register_blueprints(app, url_prefix):
|
||||||
|
"""Register the v2 endpoints with a Flask application
|
||||||
|
|
||||||
|
This function will take a Flask application object and register all
|
||||||
|
the v2 endpoints. Register blueprints here when adding new endpoint
|
||||||
|
modules.
|
||||||
|
|
||||||
|
:param app: A Flask application object to register blueprints on
|
||||||
|
:param url_prefix: The url prefix for the blueprints
|
||||||
|
"""
|
||||||
|
app.register_blueprint(cluster_templates.rest, url_prefix=url_prefix)
|
||||||
|
app.register_blueprint(clusters.rest, url_prefix=url_prefix)
|
||||||
|
app.register_blueprint(data_sources.rest, url_prefix=url_prefix)
|
||||||
|
app.register_blueprint(images.rest, url_prefix=url_prefix)
|
||||||
|
app.register_blueprint(job_binaries.rest, url_prefix=url_prefix)
|
||||||
|
app.register_blueprint(job_executions.rest, url_prefix=url_prefix)
|
||||||
|
app.register_blueprint(job_types.rest, url_prefix=url_prefix)
|
||||||
|
app.register_blueprint(jobs.rest, url_prefix=url_prefix)
|
||||||
|
app.register_blueprint(node_group_templates.rest, url_prefix=url_prefix)
|
||||||
|
app.register_blueprint(plugins.rest, url_prefix=url_prefix)
|
66
sahara/api/v2/cluster_templates.py
Normal file
66
sahara/api/v2/cluster_templates.py
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
# Copyright (c) 2016 Red Hat, 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.api import acl
|
||||||
|
from sahara.service import api
|
||||||
|
from sahara.service import validation as v
|
||||||
|
from sahara.service.validations import cluster_template_schema as ct_schema
|
||||||
|
from sahara.service.validations import cluster_templates as v_ct
|
||||||
|
import sahara.utils.api as u
|
||||||
|
|
||||||
|
|
||||||
|
rest = u.RestV2('cluster-templates', __name__)
|
||||||
|
|
||||||
|
|
||||||
|
@rest.get('/cluster-templates')
|
||||||
|
@acl.enforce("data-processing:cluster-templates:get_all")
|
||||||
|
def cluster_templates_list():
|
||||||
|
return u.render(
|
||||||
|
cluster_templates=[t.to_dict() for t in api.get_cluster_templates(
|
||||||
|
**u.get_request_args().to_dict())])
|
||||||
|
|
||||||
|
|
||||||
|
@rest.post('/cluster-templates')
|
||||||
|
@acl.enforce("data-processing:cluster-templates:create")
|
||||||
|
@v.validate(ct_schema.CLUSTER_TEMPLATE_SCHEMA,
|
||||||
|
v_ct.check_cluster_template_create)
|
||||||
|
def cluster_templates_create(data):
|
||||||
|
return u.render(api.create_cluster_template(data).to_wrapped_dict())
|
||||||
|
|
||||||
|
|
||||||
|
@rest.get('/cluster-templates/<cluster_template_id>')
|
||||||
|
@acl.enforce("data-processing:cluster-templates:get")
|
||||||
|
@v.check_exists(api.get_cluster_template, 'cluster_template_id')
|
||||||
|
def cluster_templates_get(cluster_template_id):
|
||||||
|
return u.to_wrapped_dict(api.get_cluster_template, cluster_template_id)
|
||||||
|
|
||||||
|
|
||||||
|
@rest.put('/cluster-templates/<cluster_template_id>')
|
||||||
|
@acl.enforce("data-processing:cluster-templates:modify")
|
||||||
|
@v.check_exists(api.get_cluster_template, 'cluster_template_id')
|
||||||
|
@v.validate(ct_schema.CLUSTER_TEMPLATE_UPDATE_SCHEMA,
|
||||||
|
v_ct.check_cluster_template_update)
|
||||||
|
def cluster_templates_update(cluster_template_id, data):
|
||||||
|
return u.to_wrapped_dict(
|
||||||
|
api.update_cluster_template, cluster_template_id, data)
|
||||||
|
|
||||||
|
|
||||||
|
@rest.delete('/cluster-templates/<cluster_template_id>')
|
||||||
|
@acl.enforce("data-processing:cluster-templates:delete")
|
||||||
|
@v.check_exists(api.get_cluster_template, 'cluster_template_id')
|
||||||
|
@v.validate(None, v_ct.check_cluster_template_usage)
|
||||||
|
def cluster_templates_delete(cluster_template_id):
|
||||||
|
api.terminate_cluster_template(cluster_template_id)
|
||||||
|
return u.render()
|
84
sahara/api/v2/clusters.py
Normal file
84
sahara/api/v2/clusters.py
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
# Copyright (c) 2016 Red Hat, 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.
|
||||||
|
|
||||||
|
import six
|
||||||
|
|
||||||
|
from sahara.api import acl
|
||||||
|
from sahara.service import api
|
||||||
|
from sahara.service import validation as v
|
||||||
|
from sahara.service.validations import clusters as v_c
|
||||||
|
from sahara.service.validations import clusters_scaling as v_c_s
|
||||||
|
from sahara.service.validations import clusters_schema as v_c_schema
|
||||||
|
import sahara.utils.api as u
|
||||||
|
|
||||||
|
|
||||||
|
rest = u.RestV2('clusters', __name__)
|
||||||
|
|
||||||
|
|
||||||
|
@rest.get('/clusters')
|
||||||
|
@acl.enforce("data-processing:clusters:get_all")
|
||||||
|
def clusters_list():
|
||||||
|
return u.render(clusters=[c.to_dict() for c in api.get_clusters(
|
||||||
|
**u.get_request_args().to_dict())])
|
||||||
|
|
||||||
|
|
||||||
|
@rest.post('/clusters')
|
||||||
|
@acl.enforce("data-processing:clusters:create")
|
||||||
|
@v.validate(v_c_schema.CLUSTER_SCHEMA, v_c.check_cluster_create)
|
||||||
|
def clusters_create(data):
|
||||||
|
return u.render(api.create_cluster(data).to_wrapped_dict())
|
||||||
|
|
||||||
|
|
||||||
|
@rest.post('/clusters/multiple')
|
||||||
|
@acl.enforce("data-processing:clusters:create")
|
||||||
|
@v.validate(
|
||||||
|
v_c_schema.MULTIPLE_CLUSTER_SCHEMA, v_c.check_multiple_clusters_create)
|
||||||
|
def clusters_create_multiple(data):
|
||||||
|
return u.render(api.create_multiple_clusters(data))
|
||||||
|
|
||||||
|
|
||||||
|
@rest.put('/clusters/<cluster_id>')
|
||||||
|
@acl.enforce("data-processing:clusters:scale")
|
||||||
|
@v.check_exists(api.get_cluster, 'cluster_id')
|
||||||
|
@v.validate(v_c_schema.CLUSTER_SCALING_SCHEMA, v_c_s.check_cluster_scaling)
|
||||||
|
def clusters_scale(cluster_id, data):
|
||||||
|
return u.to_wrapped_dict(api.scale_cluster, cluster_id, data)
|
||||||
|
|
||||||
|
|
||||||
|
@rest.get('/clusters/<cluster_id>')
|
||||||
|
@acl.enforce("data-processing:clusters:get")
|
||||||
|
@v.check_exists(api.get_cluster, 'cluster_id')
|
||||||
|
def clusters_get(cluster_id):
|
||||||
|
data = u.get_request_args()
|
||||||
|
show_events = six.text_type(
|
||||||
|
data.get('show_progress', 'false')).lower() == 'true'
|
||||||
|
return u.to_wrapped_dict(api.get_cluster, cluster_id, show_events)
|
||||||
|
|
||||||
|
|
||||||
|
@rest.patch('/clusters/<cluster_id>')
|
||||||
|
@acl.enforce("data-processing:clusters:modify")
|
||||||
|
@v.check_exists(api.get_cluster, 'cluster_id')
|
||||||
|
@v.validate(v_c_schema.CLUSTER_UPDATE_SCHEMA, v_c.check_cluster_update)
|
||||||
|
def clusters_update(cluster_id, data):
|
||||||
|
return u.to_wrapped_dict(api.update_cluster, cluster_id, data)
|
||||||
|
|
||||||
|
|
||||||
|
@rest.delete('/clusters/<cluster_id>')
|
||||||
|
@acl.enforce("data-processing:clusters:delete")
|
||||||
|
@v.check_exists(api.get_cluster, 'cluster_id')
|
||||||
|
@v.validate(None, v_c.check_cluster_delete)
|
||||||
|
def clusters_delete(cluster_id):
|
||||||
|
api.terminate_cluster(cluster_id)
|
||||||
|
return u.render()
|
62
sahara/api/v2/data_sources.py
Normal file
62
sahara/api/v2/data_sources.py
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
# Copyright (c) 2016 Red Hat, 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.api import acl
|
||||||
|
from sahara.service.edp import api
|
||||||
|
from sahara.service import validation as v
|
||||||
|
from sahara.service.validations.edp import data_source as v_d_s
|
||||||
|
from sahara.service.validations.edp import data_source_schema as v_d_s_schema
|
||||||
|
import sahara.utils.api as u
|
||||||
|
|
||||||
|
|
||||||
|
rest = u.RestV2('data-sources', __name__)
|
||||||
|
|
||||||
|
|
||||||
|
@rest.get('/data-sources')
|
||||||
|
@acl.enforce("data-processing:data-sources:get_all")
|
||||||
|
def data_sources_list():
|
||||||
|
return u.render(
|
||||||
|
data_sources=[ds.to_dict() for ds in api.get_data_sources(
|
||||||
|
**u.get_request_args().to_dict())])
|
||||||
|
|
||||||
|
|
||||||
|
@rest.post('/data-sources')
|
||||||
|
@acl.enforce("data-processing:data-sources:register")
|
||||||
|
@v.validate(v_d_s_schema.DATA_SOURCE_SCHEMA, v_d_s.check_data_source_create)
|
||||||
|
def data_source_register(data):
|
||||||
|
return u.render(api.register_data_source(data).to_wrapped_dict())
|
||||||
|
|
||||||
|
|
||||||
|
@rest.get('/data-sources/<data_source_id>')
|
||||||
|
@acl.enforce("data-processing:data-sources:get")
|
||||||
|
@v.check_exists(api.get_data_source, 'data_source_id')
|
||||||
|
def data_source_get(data_source_id):
|
||||||
|
return u.to_wrapped_dict(api.get_data_source, data_source_id)
|
||||||
|
|
||||||
|
|
||||||
|
@rest.delete('/data-sources/<data_source_id>')
|
||||||
|
@acl.enforce("data-processing:data-sources:delete")
|
||||||
|
@v.check_exists(api.get_data_source, 'data_source_id')
|
||||||
|
def data_source_delete(data_source_id):
|
||||||
|
api.delete_data_source(data_source_id)
|
||||||
|
return u.render()
|
||||||
|
|
||||||
|
|
||||||
|
@rest.put('/data-sources/<data_source_id>')
|
||||||
|
@acl.enforce("data-processing:data-sources:modify")
|
||||||
|
@v.check_exists(api.get_data_source, 'data_source_id')
|
||||||
|
@v.validate(v_d_s_schema.DATA_SOURCE_UPDATE_SCHEMA)
|
||||||
|
def data_source_update(data_source_id, data):
|
||||||
|
return u.to_wrapped_dict(api.data_source_update, data_source_id, data)
|
70
sahara/api/v2/images.py
Normal file
70
sahara/api/v2/images.py
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
# Copyright (c) 2016 Red Hat, 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.api import acl
|
||||||
|
from sahara.service import api
|
||||||
|
from sahara.service import validation as v
|
||||||
|
from sahara.service.validations import images as v_images
|
||||||
|
import sahara.utils.api as u
|
||||||
|
|
||||||
|
|
||||||
|
rest = u.RestV2('images', __name__)
|
||||||
|
|
||||||
|
|
||||||
|
@rest.get('/images')
|
||||||
|
@acl.enforce("data-processing:images:get_all")
|
||||||
|
def images_list():
|
||||||
|
tags = u.get_request_args().getlist('tags')
|
||||||
|
name = u.get_request_args().get('name', None)
|
||||||
|
return u.render(images=[i.dict for i in api.get_images(name, tags)])
|
||||||
|
|
||||||
|
|
||||||
|
@rest.get('/images/<image_id>')
|
||||||
|
@acl.enforce("data-processing:images:get")
|
||||||
|
@v.check_exists(api.get_image, id='image_id')
|
||||||
|
def images_get(image_id):
|
||||||
|
return u.render(api.get_registered_image(id=image_id).wrapped_dict)
|
||||||
|
|
||||||
|
|
||||||
|
@rest.post('/images/<image_id>')
|
||||||
|
@acl.enforce("data-processing:images:register")
|
||||||
|
@v.check_exists(api.get_image, id='image_id')
|
||||||
|
@v.validate(v_images.image_register_schema, v_images.check_image_register)
|
||||||
|
def images_set(image_id, data):
|
||||||
|
return u.render(api.register_image(image_id, **data).wrapped_dict)
|
||||||
|
|
||||||
|
|
||||||
|
@rest.delete('/images/<image_id>')
|
||||||
|
@acl.enforce("data-processing:images:unregister")
|
||||||
|
@v.check_exists(api.get_image, id='image_id')
|
||||||
|
def images_unset(image_id):
|
||||||
|
api.unregister_image(image_id)
|
||||||
|
return u.render()
|
||||||
|
|
||||||
|
|
||||||
|
@rest.post('/images/<image_id>/tag')
|
||||||
|
@acl.enforce("data-processing:images:add_tags")
|
||||||
|
@v.check_exists(api.get_image, id='image_id')
|
||||||
|
@v.validate(v_images.image_tags_schema, v_images.check_tags)
|
||||||
|
def image_tags_add(image_id, data):
|
||||||
|
return u.render(api.add_image_tags(image_id, **data).wrapped_dict)
|
||||||
|
|
||||||
|
|
||||||
|
@rest.post('/images/<image_id>/untag')
|
||||||
|
@acl.enforce("data-processing:images:remove_tags")
|
||||||
|
@v.check_exists(api.get_image, id='image_id')
|
||||||
|
@v.validate(v_images.image_tags_schema)
|
||||||
|
def image_tags_delete(image_id, data):
|
||||||
|
return u.render(api.remove_image_tags(image_id, **data).wrapped_dict)
|
123
sahara/api/v2/job_binaries.py
Normal file
123
sahara/api/v2/job_binaries.py
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
# Copyright (c) 2016 Red Hat, 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.api import acl
|
||||||
|
from sahara.service.edp import api
|
||||||
|
from sahara.service import validation as v
|
||||||
|
from sahara.service.validations.edp import job_binary as v_j_b
|
||||||
|
from sahara.service.validations.edp import job_binary_internal as v_j_b_i
|
||||||
|
from sahara.service.validations.edp import job_binary_internal_schema as vjbi_s
|
||||||
|
from sahara.service.validations.edp import job_binary_schema as v_j_b_schema
|
||||||
|
import sahara.utils.api as u
|
||||||
|
|
||||||
|
|
||||||
|
rest = u.RestV2('job-binaries', __name__)
|
||||||
|
|
||||||
|
|
||||||
|
@rest.post('/job-binaries')
|
||||||
|
@acl.enforce("data-processing:job-binaries:create")
|
||||||
|
@v.validate(v_j_b_schema.JOB_BINARY_SCHEMA, v_j_b.check_job_binary)
|
||||||
|
def job_binary_create(data):
|
||||||
|
return u.render(api.create_job_binary(data).to_wrapped_dict())
|
||||||
|
|
||||||
|
|
||||||
|
@rest.get('/job-binaries')
|
||||||
|
@acl.enforce("data-processing:job-binaries:get_all")
|
||||||
|
def job_binary_list():
|
||||||
|
return u.render(binaries=[j.to_dict() for j in api.get_job_binaries(
|
||||||
|
**u.get_request_args().to_dict())])
|
||||||
|
|
||||||
|
|
||||||
|
@rest.get('/job-binaries/<job_binary_id>')
|
||||||
|
@acl.enforce("data-processing:job-binaries:get")
|
||||||
|
@v.check_exists(api.get_job_binary, 'job_binary_id')
|
||||||
|
def job_binary_get(job_binary_id):
|
||||||
|
return u.to_wrapped_dict(api.get_job_binary, job_binary_id)
|
||||||
|
|
||||||
|
|
||||||
|
@rest.delete('/job-binaries/<job_binary_id>')
|
||||||
|
@acl.enforce("data-processing:job-binaries:delete")
|
||||||
|
@v.check_exists(api.get_job_binary, id='job_binary_id')
|
||||||
|
def job_binary_delete(job_binary_id):
|
||||||
|
api.delete_job_binary(job_binary_id)
|
||||||
|
return u.render()
|
||||||
|
|
||||||
|
|
||||||
|
@rest.get('/job-binaries/<job_binary_id>/data')
|
||||||
|
@acl.enforce("data-processing:job-binaries:get_data")
|
||||||
|
@v.check_exists(api.get_job_binary, 'job_binary_id')
|
||||||
|
def job_binary_data(job_binary_id):
|
||||||
|
data = api.get_job_binary_data(job_binary_id)
|
||||||
|
if type(data) == dict:
|
||||||
|
data = u.render(data)
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
@rest.put('/job-binaries/<job_binary_id>')
|
||||||
|
@acl.enforce("data-processing:job-binaries:modify")
|
||||||
|
@v.validate(v_j_b_schema.JOB_BINARY_UPDATE_SCHEMA, v_j_b.check_job_binary)
|
||||||
|
def job_binary_update(job_binary_id, data):
|
||||||
|
return u.render(
|
||||||
|
api.update_job_binary(job_binary_id, data).to_wrapped_dict())
|
||||||
|
|
||||||
|
|
||||||
|
# Job binary internals ops
|
||||||
|
|
||||||
|
@rest.put_file('/job-binary-internals/<name>')
|
||||||
|
@acl.enforce("data-processing:job-binary-internals:create")
|
||||||
|
@v.validate(None, v_j_b_i.check_job_binary_internal)
|
||||||
|
def job_binary_internal_create(**values):
|
||||||
|
return u.render(
|
||||||
|
api.create_job_binary_internal(values).to_wrapped_dict())
|
||||||
|
|
||||||
|
|
||||||
|
@rest.get('/job-binary-internals')
|
||||||
|
@acl.enforce("data-processing:job-binary-internals:get_all")
|
||||||
|
def job_binary_internal_list():
|
||||||
|
return u.render(binaries=[j.to_dict() for j in
|
||||||
|
api.get_job_binary_internals(
|
||||||
|
**u.get_request_args().to_dict())])
|
||||||
|
|
||||||
|
|
||||||
|
@rest.get('/job-binary-internals/<job_binary_internal_id>')
|
||||||
|
@acl.enforce("data-processing:job-binary-internals:get")
|
||||||
|
@v.check_exists(api.get_job_binary_internal, 'job_binary_internal_id')
|
||||||
|
def job_binary_internal_get(job_binary_internal_id):
|
||||||
|
return u.to_wrapped_dict(
|
||||||
|
api.get_job_binary_internal, job_binary_internal_id)
|
||||||
|
|
||||||
|
|
||||||
|
@rest.delete('/job-binary-internals/<job_binary_internal_id>')
|
||||||
|
@acl.enforce("data-processing:job-binary-internals:delete")
|
||||||
|
@v.check_exists(api.get_job_binary_internal, 'job_binary_internal_id')
|
||||||
|
def job_binary_internal_delete(job_binary_internal_id):
|
||||||
|
api.delete_job_binary_internal(job_binary_internal_id)
|
||||||
|
return u.render()
|
||||||
|
|
||||||
|
|
||||||
|
@rest.get('/job-binary-internals/<job_binary_internal_id>/data')
|
||||||
|
@acl.enforce("data-processing:job-binary-internals:get_data")
|
||||||
|
@v.check_exists(api.get_job_binary_internal, 'job_binary_internal_id')
|
||||||
|
def job_binary_internal_data(job_binary_internal_id):
|
||||||
|
return api.get_job_binary_internal_data(job_binary_internal_id)
|
||||||
|
|
||||||
|
|
||||||
|
@rest.patch('/job-binary-internals/<job_binary_internal_id>')
|
||||||
|
@acl.enforce("data-processing:job-binaries:modify")
|
||||||
|
@v.check_exists(api.get_job_binary_internal, 'job_binary_internal_id')
|
||||||
|
@v.validate(vjbi_s.JOB_BINARY_UPDATE_SCHEMA)
|
||||||
|
def job_binary_internal_update(job_binary_internal_id, data):
|
||||||
|
return u.to_wrapped_dict(
|
||||||
|
api.update_job_binary_internal, job_binary_internal_id, data)
|
75
sahara/api/v2/job_executions.py
Normal file
75
sahara/api/v2/job_executions.py
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
# Copyright (c) 2016 Red Hat, 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.api import acl
|
||||||
|
from sahara.service.edp import api
|
||||||
|
from sahara.service import validation as v
|
||||||
|
from sahara.service.validations.edp import job_execution as v_j_e
|
||||||
|
from sahara.service.validations.edp import job_execution_schema as v_j_e_schema
|
||||||
|
import sahara.utils.api as u
|
||||||
|
|
||||||
|
|
||||||
|
rest = u.RestV2('job-executions', __name__)
|
||||||
|
|
||||||
|
|
||||||
|
@rest.get('/job-executions')
|
||||||
|
@acl.enforce("data-processing:job-executions:get_all")
|
||||||
|
def job_executions_list():
|
||||||
|
job_executions = [je.to_dict() for je in api.job_execution_list(
|
||||||
|
**u.get_request_args().to_dict())]
|
||||||
|
return u.render(job_executions=job_executions)
|
||||||
|
|
||||||
|
|
||||||
|
@rest.get('/job-executions/<job_execution_id>')
|
||||||
|
@acl.enforce("data-processing:job-executions:get")
|
||||||
|
@v.check_exists(api.get_job_execution, id='job_execution_id')
|
||||||
|
def job_executions(job_execution_id):
|
||||||
|
return u.to_wrapped_dict(api.get_job_execution, job_execution_id)
|
||||||
|
|
||||||
|
|
||||||
|
@rest.get('/job-executions/<job_execution_id>/refresh-status')
|
||||||
|
@acl.enforce("data-processing:job-executions:refresh_status")
|
||||||
|
@v.check_exists(api.get_job_execution, id='job_execution_id')
|
||||||
|
def job_executions_status(job_execution_id):
|
||||||
|
return u.to_wrapped_dict(
|
||||||
|
api.get_job_execution_status, job_execution_id)
|
||||||
|
|
||||||
|
|
||||||
|
@rest.get('/job-executions/<job_execution_id>/cancel')
|
||||||
|
@acl.enforce("data-processing:job-executions:cancel")
|
||||||
|
@v.check_exists(api.get_job_execution, id='job_execution_id')
|
||||||
|
@v.validate(None, v_j_e.check_job_execution_cancel)
|
||||||
|
def job_executions_cancel(job_execution_id):
|
||||||
|
return u.to_wrapped_dict(api.cancel_job_execution, job_execution_id)
|
||||||
|
|
||||||
|
|
||||||
|
@rest.patch('/job-executions/<job_execution_id>')
|
||||||
|
@acl.enforce("data-processing:job-executions:modify")
|
||||||
|
@v.check_exists(api.get_job_execution, id='job_execution_id')
|
||||||
|
@v.validate(
|
||||||
|
v_j_e_schema.JOB_EXEC_UPDATE_SCHEMA, v_j_e.check_job_execution_update)
|
||||||
|
def job_executions_update(job_execution_id, data):
|
||||||
|
return u.to_wrapped_dict(
|
||||||
|
api.update_job_execution, job_execution_id, data)
|
||||||
|
|
||||||
|
|
||||||
|
@rest.delete('/job-executions/<job_execution_id>')
|
||||||
|
@acl.enforce("data-processing:job-executions:delete")
|
||||||
|
@v.check_exists(api.get_job_execution, id='job_execution_id')
|
||||||
|
@v.validate(None, v_j_e.check_job_execution_delete)
|
||||||
|
def job_executions_delete(job_execution_id):
|
||||||
|
api.delete_job_execution(job_execution_id)
|
||||||
|
return u.render()
|
31
sahara/api/v2/job_types.py
Normal file
31
sahara/api/v2/job_types.py
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
# Copyright (c) 2016 Red Hat, 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.api import acl
|
||||||
|
from sahara.service.edp import api
|
||||||
|
import sahara.utils.api as u
|
||||||
|
|
||||||
|
|
||||||
|
rest = u.RestV2('job-types', __name__)
|
||||||
|
|
||||||
|
|
||||||
|
@rest.get('/job-types')
|
||||||
|
@acl.enforce("data-processing:job-types:get_all")
|
||||||
|
def job_types_get():
|
||||||
|
# We want to use flat=False with to_dict() so that
|
||||||
|
# the value of each arg is given as a list. This supports
|
||||||
|
# filters of the form ?type=Pig&type=Java, etc.
|
||||||
|
return u.render(job_types=api.get_job_types(
|
||||||
|
**u.get_request_args().to_dict(flat=False)))
|
78
sahara/api/v2/jobs.py
Normal file
78
sahara/api/v2/jobs.py
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
# Copyright (c) 2016 Red Hat, 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.api import acl
|
||||||
|
from sahara.service.edp import api
|
||||||
|
from sahara.service import validation as v
|
||||||
|
from sahara.service.validations.edp import job as v_j
|
||||||
|
from sahara.service.validations.edp import job_execution as v_j_e
|
||||||
|
from sahara.service.validations.edp import job_execution_schema as v_j_e_schema
|
||||||
|
from sahara.service.validations.edp import job_schema as v_j_schema
|
||||||
|
import sahara.utils.api as u
|
||||||
|
|
||||||
|
|
||||||
|
rest = u.RestV2('jobs', __name__)
|
||||||
|
|
||||||
|
|
||||||
|
@rest.get('/jobs')
|
||||||
|
@acl.enforce("data-processing:jobs:get_all")
|
||||||
|
def job_list():
|
||||||
|
return u.render(jobs=[j.to_dict() for j in api.get_jobs(
|
||||||
|
**u.get_request_args().to_dict())])
|
||||||
|
|
||||||
|
|
||||||
|
@rest.post('/jobs')
|
||||||
|
@acl.enforce("data-processing:jobs:create")
|
||||||
|
@v.validate(v_j_schema.JOB_SCHEMA, v_j.check_mains_libs, v_j.check_interface)
|
||||||
|
def job_create(data):
|
||||||
|
return u.render(api.create_job(data).to_wrapped_dict())
|
||||||
|
|
||||||
|
|
||||||
|
@rest.get('/jobs/<job_id>')
|
||||||
|
@acl.enforce("data-processing:jobs:get")
|
||||||
|
@v.check_exists(api.get_job, id='job_id')
|
||||||
|
def job_get(job_id):
|
||||||
|
return u.to_wrapped_dict(api.get_job, job_id)
|
||||||
|
|
||||||
|
|
||||||
|
@rest.patch('/jobs/<job_id>')
|
||||||
|
@acl.enforce("data-processing:jobs:modify")
|
||||||
|
@v.check_exists(api.get_job, id='job_id')
|
||||||
|
@v.validate(v_j_schema.JOB_UPDATE_SCHEMA)
|
||||||
|
def job_update(job_id, data):
|
||||||
|
return u.to_wrapped_dict(api.update_job, job_id, data)
|
||||||
|
|
||||||
|
|
||||||
|
@rest.delete('/jobs/<job_id>')
|
||||||
|
@acl.enforce("data-processing:jobs:delete")
|
||||||
|
@v.check_exists(api.get_job, id='job_id')
|
||||||
|
def job_delete(job_id):
|
||||||
|
api.delete_job(job_id)
|
||||||
|
return u.render()
|
||||||
|
|
||||||
|
|
||||||
|
@rest.post('/jobs/<job_id>/execute')
|
||||||
|
@acl.enforce("data-processing:jobs:execute")
|
||||||
|
@v.check_exists(api.get_job, id='job_id')
|
||||||
|
@v.validate(v_j_e_schema.JOB_EXEC_SCHEMA, v_j_e.check_job_execution)
|
||||||
|
def job_execute(job_id, data):
|
||||||
|
return u.render(job_execution=api.execute_job(job_id, data).to_dict())
|
||||||
|
|
||||||
|
|
||||||
|
@rest.get('/jobs/config-hints/<job_type>')
|
||||||
|
@acl.enforce("data-processing:jobs:get_config_hints")
|
||||||
|
@v.check_exists(api.get_job_config_hints, job_type='job_type')
|
||||||
|
def job_config_hints_get(job_type):
|
||||||
|
return u.render(api.get_job_config_hints(job_type))
|
69
sahara/api/v2/node_group_templates.py
Normal file
69
sahara/api/v2/node_group_templates.py
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
# Copyright (c) 2016 Red Hat, 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.api import acl
|
||||||
|
from sahara.service import api
|
||||||
|
from sahara.service import validation as v
|
||||||
|
from sahara.service.validations import node_group_template_schema as ngt_schema
|
||||||
|
from sahara.service.validations import node_group_templates as v_ngt
|
||||||
|
import sahara.utils.api as u
|
||||||
|
|
||||||
|
|
||||||
|
rest = u.RestV2('node-group-templates', __name__)
|
||||||
|
|
||||||
|
|
||||||
|
@rest.get('/node-group-templates')
|
||||||
|
@acl.enforce("data-processing:node-group-templates:get_all")
|
||||||
|
def node_group_templates_list():
|
||||||
|
return u.render(
|
||||||
|
node_group_templates=[t.to_dict()
|
||||||
|
for t in api.get_node_group_templates(
|
||||||
|
**u.get_request_args().to_dict())])
|
||||||
|
|
||||||
|
|
||||||
|
@rest.post('/node-group-templates')
|
||||||
|
@acl.enforce("data-processing:node-group-templates:create")
|
||||||
|
@v.validate(ngt_schema.NODE_GROUP_TEMPLATE_SCHEMA,
|
||||||
|
v_ngt.check_node_group_template_create)
|
||||||
|
def node_group_templates_create(data):
|
||||||
|
return u.render(api.create_node_group_template(data).to_wrapped_dict())
|
||||||
|
|
||||||
|
|
||||||
|
@rest.get('/node-group-templates/<node_group_template_id>')
|
||||||
|
@acl.enforce("data-processing:node-group-templates:get")
|
||||||
|
@v.check_exists(api.get_node_group_template, 'node_group_template_id')
|
||||||
|
def node_group_templates_get(node_group_template_id):
|
||||||
|
return u.to_wrapped_dict(
|
||||||
|
api.get_node_group_template, node_group_template_id)
|
||||||
|
|
||||||
|
|
||||||
|
@rest.put('/node-group-templates/<node_group_template_id>')
|
||||||
|
@acl.enforce("data-processing:node-group-templates:modify")
|
||||||
|
@v.check_exists(api.get_node_group_template, 'node_group_template_id')
|
||||||
|
@v.validate(ngt_schema.NODE_GROUP_TEMPLATE_UPDATE_SCHEMA,
|
||||||
|
v_ngt.check_node_group_template_update)
|
||||||
|
def node_group_templates_update(node_group_template_id, data):
|
||||||
|
return u.to_wrapped_dict(
|
||||||
|
api.update_node_group_template, node_group_template_id, data)
|
||||||
|
|
||||||
|
|
||||||
|
@rest.delete('/node-group-templates/<node_group_template_id>')
|
||||||
|
@acl.enforce("data-processing:node-group-templates:delete")
|
||||||
|
@v.check_exists(api.get_node_group_template, 'node_group_template_id')
|
||||||
|
@v.validate(None, v_ngt.check_node_group_template_usage)
|
||||||
|
def node_group_templates_delete(node_group_template_id):
|
||||||
|
api.terminate_node_group_template(node_group_template_id)
|
||||||
|
return u.render()
|
52
sahara/api/v2/plugins.py
Normal file
52
sahara/api/v2/plugins.py
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
# Copyright (c) 2016 Red Hat, 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.api import acl
|
||||||
|
from sahara.service import api
|
||||||
|
from sahara.service import validation as v
|
||||||
|
from sahara.service.validations import plugins as v_p
|
||||||
|
import sahara.utils.api as u
|
||||||
|
|
||||||
|
|
||||||
|
rest = u.RestV2('plugins', __name__)
|
||||||
|
|
||||||
|
|
||||||
|
@rest.get('/plugins')
|
||||||
|
@acl.enforce("data-processing:plugins:get_all")
|
||||||
|
def plugins_list():
|
||||||
|
return u.render(plugins=[p.dict for p in api.get_plugins()])
|
||||||
|
|
||||||
|
|
||||||
|
@rest.get('/plugins/<plugin_name>')
|
||||||
|
@acl.enforce("data-processing:plugins:get")
|
||||||
|
@v.check_exists(api.get_plugin, plugin_name='plugin_name')
|
||||||
|
def plugins_get(plugin_name):
|
||||||
|
return u.render(api.get_plugin(plugin_name).wrapped_dict)
|
||||||
|
|
||||||
|
|
||||||
|
@rest.get('/plugins/<plugin_name>/<version>')
|
||||||
|
@acl.enforce("data-processing:plugins:get_version")
|
||||||
|
@v.check_exists(api.get_plugin, plugin_name='plugin_name', version='version')
|
||||||
|
def plugins_get_version(plugin_name, version):
|
||||||
|
return u.render(api.get_plugin(plugin_name, version).wrapped_dict)
|
||||||
|
|
||||||
|
|
||||||
|
@rest.post_file('/plugins/<plugin_name>/<version>/convert-config/<name>')
|
||||||
|
@acl.enforce("data-processing:plugins:convert_config")
|
||||||
|
@v.check_exists(api.get_plugin, plugin_name='plugin_name', version='version')
|
||||||
|
@v.validate(v_p.CONVERT_TO_TEMPLATE_SCHEMA, v_p.check_convert_to_template)
|
||||||
|
def plugins_convert_to_cluster_template(plugin_name, version, name, data):
|
||||||
|
return u.to_wrapped_dict(
|
||||||
|
api.convert_to_cluster_template, plugin_name, version, name, data)
|
@ -111,6 +111,59 @@ class Rest(flask.Blueprint):
|
|||||||
return decorator
|
return decorator
|
||||||
|
|
||||||
|
|
||||||
|
class RestV2(Rest):
|
||||||
|
def route(self, rule, **options):
|
||||||
|
status = options.pop('status_code', None)
|
||||||
|
file_upload = options.pop('file_upload', False)
|
||||||
|
|
||||||
|
def decorator(func):
|
||||||
|
endpoint = options.pop('endpoint', func.__name__)
|
||||||
|
|
||||||
|
def handler(**kwargs):
|
||||||
|
context.set_ctx(None)
|
||||||
|
|
||||||
|
LOG.debug("Rest.route.decorator.handler, kwargs={kwargs}"
|
||||||
|
.format(kwargs=kwargs))
|
||||||
|
|
||||||
|
_init_resp_type(file_upload)
|
||||||
|
|
||||||
|
# update status code
|
||||||
|
if status:
|
||||||
|
flask.request.status_code = status
|
||||||
|
|
||||||
|
req_id = flask.request.environ.get(oslo_req_id.ENV_REQUEST_ID)
|
||||||
|
auth_plugin = flask.request.environ.get('keystone.token_auth')
|
||||||
|
ctx = context.Context(
|
||||||
|
flask.request.headers['X-User-Id'],
|
||||||
|
flask.request.headers['X-Tenant-Id'],
|
||||||
|
flask.request.headers['X-Auth-Token'],
|
||||||
|
flask.request.headers['X-Service-Catalog'],
|
||||||
|
flask.request.headers['X-User-Name'],
|
||||||
|
flask.request.headers['X-Tenant-Name'],
|
||||||
|
flask.request.headers['X-Roles'].split(','),
|
||||||
|
auth_plugin=auth_plugin,
|
||||||
|
request_id=req_id)
|
||||||
|
context.set_ctx(ctx)
|
||||||
|
if flask.request.method in ['POST', 'PUT', 'PATCH']:
|
||||||
|
kwargs['data'] = request_data()
|
||||||
|
|
||||||
|
try:
|
||||||
|
return func(**kwargs)
|
||||||
|
except ex.Forbidden as e:
|
||||||
|
return access_denied(e)
|
||||||
|
except ex.SaharaException as e:
|
||||||
|
return bad_request(e)
|
||||||
|
except Exception as e:
|
||||||
|
return internal_error(500, 'Internal Server Error', e)
|
||||||
|
|
||||||
|
self.add_url_rule(rule, endpoint, handler, **options)
|
||||||
|
self.add_url_rule(rule + '.json', endpoint, handler, **options)
|
||||||
|
|
||||||
|
return func
|
||||||
|
|
||||||
|
return decorator
|
||||||
|
|
||||||
|
|
||||||
RT_JSON = datastructures.MIMEAccept([("application/json", 1)])
|
RT_JSON = datastructures.MIMEAccept([("application/json", 1)])
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user