842 lines
35 KiB
Python
Executable File
842 lines
35 KiB
Python
Executable File
# Copyright 2013 OpenStack Foundation
|
|
# All Rights Reserved.
|
|
#
|
|
# 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.
|
|
|
|
"""
|
|
/Templates endpoint for Daisy v1 API
|
|
"""
|
|
|
|
import os
|
|
from oslo_log import log as logging
|
|
from webob.exc import HTTPBadRequest
|
|
from webob.exc import HTTPConflict
|
|
from webob.exc import HTTPForbidden
|
|
from webob.exc import HTTPNotFound
|
|
from webob import Response
|
|
import copy
|
|
import json
|
|
import subprocess
|
|
from oslo_utils import importutils
|
|
from daisy.api import policy
|
|
import daisy.api.v1
|
|
from daisy.api.v1 import controller
|
|
from daisy.api.v1 import filters
|
|
from daisy.common import exception
|
|
from daisy.common import utils
|
|
from daisy.common import wsgi
|
|
from daisy import i18n
|
|
from daisy import notifier
|
|
import daisy.registry.client.v1.api as registry
|
|
from daisy.registry.api.v1 import template
|
|
|
|
import daisy.api.backends.common as daisy_cmn
|
|
|
|
# TODO (huzhj) move it into common sub module
|
|
daisy_path = '/var/lib/daisy/'
|
|
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
_ = i18n._
|
|
_LE = i18n._LE
|
|
_LI = i18n._LI
|
|
_LW = i18n._LW
|
|
SUPPORTED_PARAMS = template.SUPPORTED_PARAMS
|
|
SUPPORTED_FILTERS = template.SUPPORTED_FILTERS
|
|
ACTIVE_IMMUTABLE = daisy.api.v1.ACTIVE_IMMUTABLE
|
|
|
|
|
|
class Controller(controller.BaseController):
|
|
"""
|
|
WSGI controller for Templates resource in Daisy v1 API
|
|
|
|
The Templates resource API is a RESTful web Template for Template data.
|
|
The API is as follows::
|
|
|
|
GET /Templates -- Returns a set of brief metadata about Templates
|
|
GET /Templates/detail -- Returns a set of detailed metadata about
|
|
Templates
|
|
HEAD /Templates/<ID> -- Return metadata about an Template with id <ID>
|
|
GET /Templates/<ID> -- Return Template data for Template with id <ID>
|
|
POST /Templates -- Store Template data and return metadata about the
|
|
newly-stored Template
|
|
PUT /Templates/<ID> -- Update Template metadata and/or upload Template
|
|
data for a previously-reserved Template
|
|
DELETE /Templates/<ID> -- Delete the Template with id <ID>
|
|
"""
|
|
|
|
def __init__(self):
|
|
self.notifier = notifier.Notifier()
|
|
registry.configure_registry_client()
|
|
self.policy = policy.Enforcer()
|
|
|
|
def _enforce(self, req, action, target=None):
|
|
"""Authorize an action against our policies"""
|
|
if target is None:
|
|
target = {}
|
|
try:
|
|
self.policy.enforce(req.context, action, target)
|
|
except exception.Forbidden:
|
|
raise HTTPForbidden()
|
|
|
|
def _get_filters(self, req):
|
|
"""
|
|
Return a dictionary of query param filters from the request
|
|
|
|
:param req: the Request object coming from the wsgi layer
|
|
:retval a dict of key/value filters
|
|
"""
|
|
query_filters = {}
|
|
for param in req.params:
|
|
if param in SUPPORTED_FILTERS:
|
|
query_filters[param] = req.params.get(param)
|
|
if not filters.validate(param, query_filters[param]):
|
|
raise HTTPBadRequest(_('Bad value passed to filter '
|
|
'%(filter)s got %(val)s')
|
|
% {'filter': param,
|
|
'val': query_filters[param]})
|
|
return query_filters
|
|
|
|
def _get_query_params(self, req):
|
|
"""
|
|
Extracts necessary query params from request.
|
|
|
|
:param req: the WSGI Request object
|
|
:retval dict of parameters that can be used by registry client
|
|
"""
|
|
params = {'filters': self._get_filters(req)}
|
|
|
|
for PARAM in SUPPORTED_PARAMS:
|
|
if PARAM in req.params:
|
|
params[PARAM] = req.params.get(PARAM)
|
|
return params
|
|
|
|
def _raise_404_if_cluster_deleted(self, req, cluster_id):
|
|
cluster = self.get_cluster_meta_or_404(req, cluster_id)
|
|
if cluster['deleted']:
|
|
msg = _("Cluster with identifier %s has been deleted.") % \
|
|
cluster_id
|
|
raise HTTPNotFound(msg)
|
|
|
|
@utils.mutating
|
|
def add_template(self, req, template):
|
|
"""
|
|
Adds a new cluster template to Daisy.
|
|
|
|
:param req: The WSGI/Webob Request object
|
|
:param image_meta: Mapping of metadata about Template
|
|
|
|
:raises HTTPBadRequest if x-Template-name is missing
|
|
"""
|
|
self._enforce(req, 'add_template')
|
|
|
|
template = registry.add_template_metadata(req.context, template)
|
|
|
|
return {'template': template}
|
|
|
|
@utils.mutating
|
|
def update_template(self, req, template_id, template):
|
|
"""
|
|
Updates an existing Template with the registry.
|
|
|
|
:param request: The WSGI/Webob Request object
|
|
:param id: The opaque image identifier
|
|
|
|
:retval Returns the updated image information as a mapping
|
|
"""
|
|
self._enforce(req, 'update_template')
|
|
try:
|
|
template = registry.update_template_metadata(req.context,
|
|
template_id,
|
|
template)
|
|
|
|
except exception.Invalid as e:
|
|
msg = (_("Failed to update template metadata. Got error: %s") %
|
|
utils.exception_to_str(e))
|
|
LOG.warn(msg)
|
|
raise HTTPBadRequest(explanation=msg,
|
|
request=req,
|
|
content_type="text/plain")
|
|
except exception.NotFound as e:
|
|
msg = (_("Failed to find template to update: %s") %
|
|
utils.exception_to_str(e))
|
|
LOG.warn(msg)
|
|
raise HTTPNotFound(explanation=msg,
|
|
request=req,
|
|
content_type="text/plain")
|
|
except exception.Forbidden as e:
|
|
msg = (_("Forbidden to update template: %s") %
|
|
utils.exception_to_str(e))
|
|
LOG.warn(msg)
|
|
raise HTTPForbidden(explanation=msg,
|
|
request=req,
|
|
content_type="text/plain")
|
|
except (exception.Conflict, exception.Duplicate) as e:
|
|
LOG.warn(utils.exception_to_str(e))
|
|
raise HTTPConflict(body=_('template operation conflicts'),
|
|
request=req,
|
|
content_type='text/plain')
|
|
else:
|
|
self.notifier.info('template.update', template)
|
|
|
|
return {'template': template}
|
|
|
|
@utils.mutating
|
|
def delete_template(self, req, template_id):
|
|
"""
|
|
delete a existing cluster template with the registry.
|
|
|
|
:param request: The WSGI/Webob Request object
|
|
:param id: The opaque image identifier
|
|
|
|
:retval Returns the updated image information as a mapping
|
|
"""
|
|
self._enforce(req, 'delete_template')
|
|
try:
|
|
registry.delete_template_metadata(req.context, template_id)
|
|
except exception.NotFound as e:
|
|
msg = (_("Failed to find template to delete: %s") %
|
|
utils.exception_to_str(e))
|
|
LOG.error(msg)
|
|
raise HTTPNotFound(explanation=msg,
|
|
request=req,
|
|
content_type="text/plain")
|
|
except exception.Forbidden as e:
|
|
msg = (_("Forbidden to delete template: %s") %
|
|
utils.exception_to_str(e))
|
|
LOG.error(msg)
|
|
raise HTTPForbidden(explanation=msg,
|
|
request=req,
|
|
content_type="text/plain")
|
|
except exception.InUseByStore as e:
|
|
msg = (_("template %(id)s could not be deleted "
|
|
"because it is in use: "
|
|
"%(exc)s") % {"id": template_id,
|
|
"exc": utils.exception_to_str(e)})
|
|
LOG.error(msg)
|
|
raise HTTPConflict(explanation=msg,
|
|
request=req,
|
|
content_type="text/plain")
|
|
else:
|
|
return Response(body='', status=200)
|
|
|
|
def _del_general_params(self, param):
|
|
del param['created_at']
|
|
del param['updated_at']
|
|
del param['deleted']
|
|
del param['deleted_at']
|
|
del param['id']
|
|
|
|
def _del_cluster_params(self, cluster):
|
|
del cluster['networks']
|
|
del cluster['vlan_start']
|
|
del cluster['vlan_end']
|
|
del cluster['vni_start']
|
|
del cluster['vni_end']
|
|
del cluster['gre_id_start']
|
|
del cluster['gre_id_end']
|
|
del cluster['net_l23_provider']
|
|
del cluster['public_vip']
|
|
del cluster['segmentation_type']
|
|
del cluster['base_mac']
|
|
del cluster['name']
|
|
|
|
def _get_cinder_volumes(self, req, role):
|
|
cinder_volume_params = {'filters': {'role_id': role['id']}}
|
|
cinder_volumes = registry.list_cinder_volume_metadata(
|
|
req.context, **cinder_volume_params)
|
|
for cinder_volume in cinder_volumes:
|
|
if cinder_volume.get('role_id', None):
|
|
cinder_volume['role_id'] = role['name']
|
|
self._del_general_params(cinder_volume)
|
|
return cinder_volumes
|
|
|
|
def _get_neutron_backends(self, req, role):
|
|
neutron_backend_params = {'filters': {'role_id': role['id']}}
|
|
neutron_backends = registry.list_neutron_backend_metadata(
|
|
req.context, **neutron_backend_params)
|
|
for neutron_backend in neutron_backends:
|
|
if neutron_backend.get('role_id', None):
|
|
neutron_backend['role_id'] = role['name']
|
|
self._del_general_params(neutron_backend)
|
|
return neutron_backends
|
|
|
|
def _get_optical_switchs(self, req, role):
|
|
optical_switch_params = {'filters': {'role_id': role['id']}}
|
|
optical_switchs = registry.list_optical_switch_metadata(
|
|
req.context, **optical_switch_params)
|
|
for optical_switch in optical_switchs:
|
|
if optical_switch.get('role_id', None):
|
|
optical_switch['role_id'] = role['name']
|
|
self._del_general_params(optical_switch)
|
|
return optical_switchs
|
|
|
|
def _get_services_disk(self, req, role):
|
|
params = {'filters': {'role_id': role['id']}}
|
|
services_disk = registry.list_service_disk_metadata(
|
|
req.context, **params)
|
|
for service_disk in services_disk:
|
|
if service_disk.get('role_id', None):
|
|
service_disk['role_id'] = role['name']
|
|
self._del_general_params(service_disk)
|
|
return services_disk
|
|
|
|
@utils.mutating
|
|
def export_db_to_json(self, req, template):
|
|
"""
|
|
Template TECS to a cluster.
|
|
:param req: The WSGI/Webob Request object
|
|
:raises HTTPBadRequest if x-Template-cluster is missing
|
|
"""
|
|
cluster_name = template.get('cluster_name', None)
|
|
type = template.get('type', None)
|
|
description = template.get('description', None)
|
|
template_name = template.get('template_name', None)
|
|
self._enforce(req, 'export_db_to_json')
|
|
cinder_volume_list = []
|
|
neutron_backend_list = []
|
|
service_disk_list = []
|
|
optical_switch_list = []
|
|
template_content = {}
|
|
template_json = {}
|
|
template_id = ""
|
|
if not type or type == "tecs":
|
|
try:
|
|
params = {'filters': {'name': cluster_name}}
|
|
clusters = registry.get_clusters_detail(req.context, **params)
|
|
if clusters:
|
|
cluster_id = clusters[0]['id']
|
|
else:
|
|
msg = "the cluster %s is not exist" % cluster_name
|
|
LOG.error(msg)
|
|
raise HTTPForbidden(
|
|
explanation=msg,
|
|
request=req,
|
|
content_type="text/plain")
|
|
|
|
params = {'filters': {'cluster_id': cluster_id}}
|
|
cluster = registry.get_cluster_metadata(
|
|
req.context, cluster_id)
|
|
roles = registry.get_roles_detail(req.context, **params)
|
|
networks = registry.get_networks_detail(
|
|
req.context, cluster_id, **params)
|
|
for role in roles:
|
|
cinder_volumes = self._get_cinder_volumes(req, role)
|
|
cinder_volume_list += cinder_volumes
|
|
services_disk = self._get_services_disk(req, role)
|
|
service_disk_list += services_disk
|
|
optical_switchs = self._get_optical_switchs(req, role)
|
|
optical_switch_list += optical_switchs
|
|
neutron_backends = self._get_neutron_backends(req, role)
|
|
neutron_backend_list += neutron_backends
|
|
|
|
if role.get('config_set_id', None):
|
|
config_set = registry.get_config_set_metadata(
|
|
req.context, role['config_set_id'])
|
|
if config_set.get("config", None):
|
|
role['config_set'] = config_set['config']
|
|
del role['cluster_id']
|
|
del role['status']
|
|
del role['progress']
|
|
del role['messages']
|
|
del role['config_set_update_progress']
|
|
self._del_general_params(role)
|
|
for network in networks:
|
|
network_detail = registry.get_network_metadata(
|
|
req.context, network['id'])
|
|
if network_detail.get('ip_ranges', None):
|
|
network['ip_ranges'] = network_detail['ip_ranges']
|
|
del network['cluster_id']
|
|
self._del_general_params(network)
|
|
if cluster.get('routers', None):
|
|
for router in cluster['routers']:
|
|
del router['cluster_id']
|
|
self._del_general_params(router)
|
|
if cluster.get('logic_networks', None):
|
|
for logic_network in cluster['logic_networks']:
|
|
for subnet in logic_network['subnets']:
|
|
del subnet['logic_network_id']
|
|
del subnet['router_id']
|
|
self._del_general_params(subnet)
|
|
del logic_network['cluster_id']
|
|
self._del_general_params(logic_network)
|
|
if cluster.get('nodes', None):
|
|
del cluster['nodes']
|
|
self._del_general_params(cluster)
|
|
self._del_cluster_params(cluster)
|
|
cluster['tecs_version_id'] = ""
|
|
template_content['cluster'] = cluster
|
|
template_content['cluster_name'] = cluster_name
|
|
template_content['roles'] = roles
|
|
template_content['networks'] = networks
|
|
template_content['cinder_volumes'] = cinder_volume_list
|
|
template_content['neutron_backends'] = neutron_backend_list
|
|
template_content['optical_switchs'] = optical_switch_list
|
|
template_content['services_disk'] = service_disk_list
|
|
template_json['content'] = json.dumps(template_content)
|
|
template_json['type'] = 'tecs'
|
|
template_json['name'] = template_name
|
|
template_json['description'] = description
|
|
|
|
template_host_params = {'cluster_name': cluster_name}
|
|
template_hosts = registry.host_template_lists_metadata(
|
|
req.context, **template_host_params)
|
|
if template_hosts:
|
|
template_json['hosts'] = template_hosts[0]['hosts']
|
|
else:
|
|
template_json['hosts'] = "[]"
|
|
|
|
template_params = {'filters': {'name': template_name}}
|
|
template_list = registry.template_lists_metadata(
|
|
req.context, **template_params)
|
|
if template_list:
|
|
update_template = registry.update_template_metadata(
|
|
req.context, template_list[0]['id'], template_json)
|
|
template_id = template_list[0]['id']
|
|
else:
|
|
add_template = registry.add_template_metadata(
|
|
req.context, template_json)
|
|
template_id = add_template['id']
|
|
|
|
if template_id:
|
|
template_detail = registry.template_detail_metadata(
|
|
req.context, template_id)
|
|
self._del_general_params(template_detail)
|
|
template_detail['content'] = json.loads(
|
|
template_detail['content'])
|
|
if template_detail['hosts']:
|
|
template_detail['hosts'] = json.loads(
|
|
template_detail['hosts'])
|
|
|
|
tecs_json = daisy_path + "%s.json" % template_name
|
|
cmd = 'rm -rf %s' % (tecs_json,)
|
|
daisy_cmn.subprocess_call(cmd)
|
|
with open(tecs_json, "w+") as fp:
|
|
json.dump(template_detail, fp, indent=2)
|
|
|
|
except exception.Invalid as e:
|
|
raise HTTPBadRequest(explanation=e.msg, request=req)
|
|
|
|
return {"template": template_detail}
|
|
|
|
@utils.mutating
|
|
def import_json_to_template(self, req, template):
|
|
template_id = ""
|
|
template = json.loads(template.get('template', None))
|
|
template_cluster = copy.deepcopy(template)
|
|
template_name = template_cluster.get('name', None)
|
|
template_params = {'filters': {'name': template_name}}
|
|
try:
|
|
if template_cluster.get('content', None):
|
|
template_cluster['content'] = json.dumps(
|
|
template_cluster['content'])
|
|
if template_cluster.get('hosts', None):
|
|
template_cluster['hosts'] = json.dumps(
|
|
template_cluster['hosts'])
|
|
else:
|
|
template_cluster['hosts'] = "[]"
|
|
|
|
template_list = registry.template_lists_metadata(
|
|
req.context, **template_params)
|
|
if template_list:
|
|
registry.update_template_metadata(
|
|
req.context, template_list[0]['id'], template_cluster)
|
|
template_id = template_list[0]['id']
|
|
else:
|
|
add_template_cluster = registry.add_template_metadata(
|
|
req.context, template_cluster)
|
|
template_id = add_template_cluster['id']
|
|
|
|
if template_id:
|
|
template_detail = registry.template_detail_metadata(
|
|
req.context, template_id)
|
|
del template_detail['deleted']
|
|
del template_detail['deleted_at']
|
|
|
|
except exception.Invalid as e:
|
|
raise HTTPBadRequest(explanation=e.msg, request=req)
|
|
|
|
return {"template": template_detail}
|
|
|
|
def _import_cinder_volumes_to_db(self, req,
|
|
template_cinder_volumes, roles):
|
|
for template_cinder_volume in template_cinder_volumes:
|
|
has_template_role = False
|
|
for role in roles:
|
|
if template_cinder_volume['role_id'] == role['name']:
|
|
has_template_role = True
|
|
template_cinder_volume['role_id'] = role['id']
|
|
break
|
|
if has_template_role:
|
|
registry.add_cinder_volume_metadata(req.context,
|
|
template_cinder_volume)
|
|
else:
|
|
msg = "can't find role %s in new cluster when\
|
|
import cinder_volumes from template"\
|
|
% template_cinder_volume['role_id']
|
|
raise HTTPBadRequest(explanation=msg, request=req)
|
|
|
|
def _import_neutron_backends_to_db(self, req,
|
|
template_neutron_backends, roles):
|
|
for template_neutron_backend in template_neutron_backends:
|
|
has_template_role = False
|
|
for role in roles:
|
|
if template_neutron_backend['role_id'] == role['name']:
|
|
has_template_role = True
|
|
template_neutron_backend['role_id'] = role['id']
|
|
break
|
|
if has_template_role:
|
|
registry.add_neutron_backend_metadata(req.context,
|
|
template_neutron_backend)
|
|
else:
|
|
msg = "can't find role %s in new cluster when\
|
|
import neutron_backends from template"\
|
|
% template_neutron_backend['role_id']
|
|
LOG.error(msg)
|
|
raise HTTPBadRequest(explanation=msg, request=req)
|
|
|
|
def _import_optical_switchs_to_db(self, req,
|
|
template_optical_switchs, roles):
|
|
for template_optical_switch in template_optical_switchs:
|
|
has_template_role = False
|
|
for role in roles:
|
|
if template_optical_switch['role_id'] == role['name']:
|
|
has_template_role = True
|
|
template_optical_switch['role_id'] = role['id']
|
|
break
|
|
if has_template_role:
|
|
registry.add_optical_switch_metadata(req.context,
|
|
template_optical_switch)
|
|
else:
|
|
msg = "can't find role %s in new cluster when\
|
|
import optical_switchs from template"\
|
|
% template_optical_switch['role_id']
|
|
raise HTTPBadRequest(explanation=msg, request=req)
|
|
|
|
def _import_services_disk_to_db(self, req,
|
|
template_services_disk, roles):
|
|
for template_service_disk in template_services_disk:
|
|
has_template_role = False
|
|
for role in roles:
|
|
if template_service_disk['role_id'] == role['name']:
|
|
has_template_role = True
|
|
template_service_disk['role_id'] = role['id']
|
|
break
|
|
if has_template_role:
|
|
registry.add_service_disk_metadata(req.context,
|
|
template_service_disk)
|
|
else:
|
|
msg = "can't find role %s in new cluster when\
|
|
import service_disks from template"\
|
|
% template_service_disk['role_id']
|
|
raise HTTPBadRequest(explanation=msg, request=req)
|
|
|
|
@utils.mutating
|
|
def import_template_to_db(self, req, template):
|
|
"""
|
|
create cluster
|
|
"""
|
|
cluster_id = ""
|
|
template_cluster = {}
|
|
cluster_meta = {}
|
|
template_meta = copy.deepcopy(template)
|
|
template_name = template_meta.get('template_name', None)
|
|
cluster_name = template_meta.get('cluster', None)
|
|
template_params = {'filters': {'name': template_name}}
|
|
template_list = registry.template_lists_metadata(
|
|
req.context, **template_params)
|
|
if template_list:
|
|
template_cluster = template_list[0]
|
|
else:
|
|
msg = "the template %s is not exist" % template_name
|
|
LOG.error(msg)
|
|
raise HTTPForbidden(
|
|
explanation=msg,
|
|
request=req,
|
|
content_type="text/plain")
|
|
|
|
try:
|
|
template_content = json.loads(template_cluster['content'])
|
|
template_content_cluster = template_content['cluster']
|
|
template_content_cluster['name'] = cluster_name
|
|
template_content_cluster['networking_parameters'] = str(
|
|
template_content_cluster['networking_parameters'])
|
|
template_content_cluster['logic_networks'] = str(
|
|
template_content_cluster['logic_networks'])
|
|
template_content_cluster['logic_networks'] = \
|
|
template_content_cluster[
|
|
'logic_networks'].replace("\'true\'", "True")
|
|
template_content_cluster['routers'] = str(
|
|
template_content_cluster['routers'])
|
|
|
|
if template_cluster['hosts']:
|
|
template_hosts = json.loads(template_cluster['hosts'])
|
|
template_host_params = {'cluster_name': cluster_name}
|
|
template_host_list = registry.host_template_lists_metadata(
|
|
req.context, **template_host_params)
|
|
if template_host_list:
|
|
update_template_meta = {
|
|
"cluster_name": cluster_name,
|
|
"hosts": json.dumps(template_hosts)}
|
|
registry.update_host_template_metadata(
|
|
req.context, template_host_list[0]['id'],
|
|
update_template_meta)
|
|
else:
|
|
template_meta = {
|
|
"cluster_name": cluster_name,
|
|
"hosts": json.dumps(template_hosts)}
|
|
registry.add_host_template_metadata(
|
|
req.context, template_meta)
|
|
|
|
cluster_params = {'filters': {'name': cluster_name}}
|
|
clusters = registry.get_clusters_detail(
|
|
req.context, **cluster_params)
|
|
if clusters:
|
|
msg = "the cluster %s is exist" % clusters[0]['name']
|
|
LOG.error(msg)
|
|
raise HTTPForbidden(
|
|
explanation=msg,
|
|
request=req,
|
|
content_type="text/plain")
|
|
else:
|
|
if template_content_cluster.get('auto_scale', None) == 1:
|
|
params = {'filters': ''}
|
|
clusters_list = registry.get_clusters_detail(
|
|
req.context, **params)
|
|
for cluster in clusters_list:
|
|
if cluster.get('auto_scale', None) == 1:
|
|
template_content_cluster['auto_scale'] = 0
|
|
break
|
|
|
|
if template_cluster.get('type') in utils.SUPPORT_BACKENDS:
|
|
template_content['cluster'].setdefault(
|
|
'target_systems', 'os+%s' % template_cluster['type'])
|
|
else:
|
|
msg = 'type in template: "%s" not support' % \
|
|
template_cluster
|
|
LOG.error(msg)
|
|
raise HTTPBadRequest(explanation=msg, request=req)
|
|
|
|
cluster_meta = registry.add_cluster_metadata(
|
|
req.context, template_content['cluster'])
|
|
cluster_id = cluster_meta['id']
|
|
|
|
params = {'filters': {}}
|
|
networks = registry.get_networks_detail(
|
|
req.context, cluster_id, **params)
|
|
template_content_networks = template_content['networks']
|
|
for template_content_network in template_content_networks:
|
|
template_content_network['ip_ranges'] = str(
|
|
template_content_network['ip_ranges'])
|
|
network_exist = 'false'
|
|
for network in networks:
|
|
if template_content_network['name'] == network['name']:
|
|
update_network_meta = registry.update_network_metadata(
|
|
req.context, network['id'],
|
|
template_content_network)
|
|
network_exist = 'true'
|
|
|
|
if network_exist == 'false':
|
|
template_content_network['cluster_id'] = cluster_id
|
|
add_network_meta = registry.add_network_metadata(
|
|
req.context, template_content_network)
|
|
|
|
params = {'filters': {'cluster_id': cluster_id}}
|
|
roles = registry.get_roles_detail(req.context, **params)
|
|
template_content_roles = template_content['roles']
|
|
for template_content_role in template_content_roles:
|
|
role_exist = 'false'
|
|
del template_content_role['config_set_id']
|
|
for role in roles:
|
|
if template_content_role['name'] == role['name']:
|
|
update_role_meta = registry.update_role_metadata(
|
|
req.context, role['id'], template_content_role)
|
|
role_exist = 'true'
|
|
|
|
if role_exist == 'false':
|
|
template_content_role['cluster_id'] = cluster_id
|
|
registry.add_role_metadata(
|
|
req.context, template_content_role)
|
|
|
|
self._import_cinder_volumes_to_db(
|
|
req, template_content['cinder_volumes'], roles)
|
|
if 'neutron_backends' in template_content:
|
|
self._import_neutron_backends_to_db(
|
|
req, template_content['neutron_backends'], roles)
|
|
if 'optical_switchs' in template_content:
|
|
self._import_optical_switchs_to_db(
|
|
req, template_content['optical_switchs'], roles)
|
|
self._import_services_disk_to_db(req,
|
|
template_content['services_disk'],
|
|
roles)
|
|
|
|
# add extension content for cluster_template
|
|
path = os.path.join(os.path.abspath(os.path.dirname(
|
|
os.path.realpath(__file__))), 'ext')
|
|
for root, dirs, names in os.walk(path):
|
|
filename = 'router.py'
|
|
if filename in names:
|
|
ext_name = root.split(path)[1].strip('/')
|
|
ext_func = "%s.api.hosts" % ext_name
|
|
extension = importutils.import_module(
|
|
'daisy.api.v1.ext.%s' % ext_func)
|
|
if 'import_template_to_db_ext' in dir(extension):
|
|
extension.import_template_to_db_ext(req, cluster_id)
|
|
|
|
except exception.Invalid as e:
|
|
raise HTTPBadRequest(explanation=e.msg, request=req)
|
|
return {"template": cluster_meta}
|
|
|
|
@utils.mutating
|
|
def get_template_detail(self, req, template_id):
|
|
"""
|
|
delete a existing cluster template with the registry.
|
|
:param request: The WSGI/Webob Request object
|
|
:param id: The opaque image identifie
|
|
:retval Returns the updated image information as a mapping
|
|
"""
|
|
self._enforce(req, 'get_template_detail')
|
|
try:
|
|
template = registry.template_detail_metadata(
|
|
req.context, template_id)
|
|
if template.get("tecs_version_id", None):
|
|
template['tecs_version_id'] = ""
|
|
obj = subprocess.Popen("which daisy-manage >/dev/null "
|
|
"&& daisy-manage db_version",
|
|
shell=True,
|
|
stdout=subprocess.PIPE,
|
|
stderr=subprocess.PIPE)
|
|
(stdoutput, erroutput) = obj.communicate()
|
|
if stdoutput:
|
|
template['version'] = stdoutput.strip('\n')
|
|
return {'template': template}
|
|
except exception.NotFound as e:
|
|
msg = (_("Failed to find template: %s") %
|
|
utils.exception_to_str(e))
|
|
LOG.error(msg)
|
|
raise HTTPNotFound(explanation=msg,
|
|
request=req,
|
|
content_type="text/plain")
|
|
except exception.Forbidden as e:
|
|
msg = (_("Forbidden to get template: %s") %
|
|
utils.exception_to_str(e))
|
|
LOG.error(msg)
|
|
raise HTTPForbidden(explanation=msg,
|
|
request=req,
|
|
content_type="text/plain")
|
|
except exception.InUseByStore as e:
|
|
msg = (_("template %(id)s could not be get because it is in use: "
|
|
"%(exc)s") % {"id": template_id,
|
|
"exc": utils.exception_to_str(e)})
|
|
LOG.error(msg)
|
|
raise HTTPConflict(explanation=msg,
|
|
request=req,
|
|
content_type="text/plain")
|
|
else:
|
|
return Response(body='', status=200)
|
|
|
|
@utils.mutating
|
|
def get_template_lists(self, req):
|
|
self._enforce(req, 'get_template_lists')
|
|
params = self._get_query_params(req)
|
|
try:
|
|
template_lists = registry.template_lists_metadata(
|
|
req.context, **params)
|
|
except exception.Invalid as e:
|
|
raise HTTPBadRequest(explanation=e.msg, request=req)
|
|
return dict(template=template_lists)
|
|
|
|
|
|
class TemplateDeserializer(wsgi.JSONRequestDeserializer):
|
|
"""Handles deserialization of specific controller method requests."""
|
|
|
|
def _deserialize(self, request):
|
|
result = {}
|
|
result["template"] = utils.get_template_meta(request)
|
|
return result
|
|
|
|
def add_template(self, request):
|
|
return self._deserialize(request)
|
|
|
|
def update_template(self, request):
|
|
return self._deserialize(request)
|
|
|
|
def export_db_to_json(self, request):
|
|
return self._deserialize(request)
|
|
|
|
def import_json_to_template(self, request):
|
|
return self._deserialize(request)
|
|
|
|
def import_template_to_db(self, request):
|
|
return self._deserialize(request)
|
|
|
|
|
|
class TemplateSerializer(wsgi.JSONResponseSerializer):
|
|
"""Handles serialization of specific controller method responses."""
|
|
|
|
def __init__(self):
|
|
self.notifier = notifier.Notifier()
|
|
|
|
def add_template(self, response, result):
|
|
template = result['template']
|
|
response.status = 201
|
|
response.headers['Content-Type'] = 'application/json'
|
|
response.body = self.to_json(dict(template=template))
|
|
return response
|
|
|
|
def delete_template(self, response, result):
|
|
template = result['template']
|
|
response.status = 201
|
|
response.headers['Content-Type'] = 'application/json'
|
|
response.body = self.to_json(dict(template=template))
|
|
return response
|
|
|
|
def get_template_detail(self, response, result):
|
|
template = result['template']
|
|
response.status = 201
|
|
response.headers['Content-Type'] = 'application/json'
|
|
response.body = self.to_json(dict(template=template))
|
|
return response
|
|
|
|
def update_template(self, response, result):
|
|
template = result['template']
|
|
response.status = 201
|
|
response.headers['Content-Type'] = 'application/json'
|
|
response.body = self.to_json(dict(template=template))
|
|
return response
|
|
|
|
def export_db_to_json(self, response, result):
|
|
response.status = 201
|
|
response.headers['Content-Type'] = 'application/json'
|
|
response.body = self.to_json(result)
|
|
return response
|
|
|
|
def import_json_to_template(self, response, result):
|
|
response.status = 201
|
|
response.headers['Content-Type'] = 'application/json'
|
|
response.body = self.to_json(result)
|
|
return response
|
|
|
|
def import_template_to_db(self, response, result):
|
|
response.status = 201
|
|
response.headers['Content-Type'] = 'application/json'
|
|
response.body = self.to_json(result)
|
|
return response
|
|
|
|
|
|
def create_resource():
|
|
"""Templates resource factory method"""
|
|
deserializer = TemplateDeserializer()
|
|
serializer = TemplateSerializer()
|
|
return wsgi.Resource(Controller(), deserializer, serializer)
|