270 lines
7.1 KiB
Python
270 lines
7.1 KiB
Python
# 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.
|
|
|
|
import eventlet
|
|
from oslo.config import cfg
|
|
|
|
from savanna import exceptions as ex
|
|
from savanna.openstack.common import log as logging
|
|
from savanna.service import cluster_ops
|
|
import savanna.storage.storage as storage
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
CONF = cfg.CONF
|
|
CONF.import_opt('allow_cluster_ops', 'savanna.config')
|
|
|
|
|
|
## Node Template ops:
|
|
|
|
def get_node_template(**args):
|
|
return _node_template(storage.get_node_template(**args))
|
|
|
|
|
|
def get_node_templates(**args):
|
|
return [_node_template(tmpl) for tmpl
|
|
in storage.get_node_templates(**args)]
|
|
|
|
|
|
def is_node_template_associated(**args):
|
|
return storage.is_node_template_associated(**args)
|
|
|
|
|
|
def create_node_template(values, headers):
|
|
"""Creates new node template from values dict.
|
|
|
|
:param values: dict
|
|
:return: created node template resource
|
|
"""
|
|
values = values.pop('node_template')
|
|
|
|
name = values.pop('name')
|
|
node_type_id = storage.get_node_type(name=values.pop('node_type')).id
|
|
flavor_id = values.pop('flavor_id')
|
|
|
|
nt = storage.create_node_template(name, node_type_id, flavor_id, values)
|
|
|
|
return get_node_template(id=nt.id)
|
|
|
|
|
|
def terminate_node_template(**args):
|
|
return storage.terminate_node_template(**args)
|
|
|
|
|
|
## Cluster ops:
|
|
|
|
def get_cluster(**args):
|
|
return _cluster(storage.get_cluster(**args))
|
|
|
|
|
|
def get_clusters(**args):
|
|
return [_cluster(cluster) for cluster in
|
|
storage.get_clusters(**args)]
|
|
|
|
|
|
def create_cluster(values, headers):
|
|
values = values.pop('cluster')
|
|
|
|
name = values.pop('name')
|
|
base_image_id = values.pop('base_image_id')
|
|
tenant_id = headers['X-Tenant-Id']
|
|
templates = values.pop('node_templates')
|
|
|
|
# todo(slukjanov): check that we can create objects in the specified tenant
|
|
|
|
cluster = storage.create_cluster(name, base_image_id, tenant_id, templates)
|
|
|
|
eventlet.spawn(_cluster_creation_job, headers, cluster.id)
|
|
|
|
return get_cluster(id=cluster.id)
|
|
|
|
|
|
def _cluster_creation_job(headers, cluster_id):
|
|
cluster = storage.get_cluster(id=cluster_id)
|
|
LOG.debug("Starting cluster '%s' creation: %s", cluster_id,
|
|
_cluster(cluster).dict)
|
|
|
|
if CONF.allow_cluster_ops:
|
|
launched = cluster_ops.launch_cluster(headers, cluster)
|
|
else:
|
|
LOG.info("Cluster ops are disabled, use --allow-cluster-ops flag")
|
|
launched = True
|
|
|
|
if launched:
|
|
storage.update_cluster_status('Active', id=cluster.id)
|
|
|
|
|
|
def terminate_cluster(headers, **args):
|
|
cluster = storage.update_cluster_status('Stopping', **args)
|
|
|
|
eventlet.spawn(_cluster_termination_job, headers, cluster.id)
|
|
|
|
|
|
def _cluster_termination_job(headers, cluster_id):
|
|
cluster = storage.get_cluster(id=cluster_id)
|
|
LOG.debug("Stopping cluster '%s' creation: %s", cluster_id,
|
|
_cluster(cluster).dict)
|
|
|
|
if CONF.allow_cluster_ops:
|
|
cluster_ops.stop_cluster(headers, cluster)
|
|
else:
|
|
LOG.info("Cluster ops are disabled, use --allow-cluster-ops flag")
|
|
|
|
storage.terminate_cluster(id=cluster.id)
|
|
|
|
|
|
## Node Type ops:
|
|
|
|
def get_node_type(**args):
|
|
return _node_type(storage.get_node_type(**args))
|
|
|
|
|
|
def get_node_types(**args):
|
|
return [_node_type(t) for t in storage.get_node_types(**args)]
|
|
|
|
|
|
def get_node_type_required_params(**args):
|
|
result = {}
|
|
for process in storage.get_node_type(**args).processes:
|
|
result[process.name] = []
|
|
for prop in process.node_process_properties:
|
|
if prop.required and not prop.default:
|
|
result[process.name] += [prop.name]
|
|
|
|
return result
|
|
|
|
|
|
def get_node_type_all_params(**args):
|
|
result = {}
|
|
for process in storage.get_node_type(**args).processes:
|
|
result[process.name] = [prop.name
|
|
for prop in process.node_process_properties]
|
|
return result
|
|
|
|
|
|
## Utils and DB object to Resource converters
|
|
|
|
def _clean_nones(obj):
|
|
if not isinstance(obj, dict) and not isinstance(obj, list):
|
|
return obj
|
|
|
|
if isinstance(obj, dict):
|
|
remove = []
|
|
for key, value in obj.iteritems():
|
|
if value is None:
|
|
remove.append(key)
|
|
for key in remove:
|
|
obj.pop(key)
|
|
for value in obj.values():
|
|
_clean_nones(value)
|
|
elif isinstance(obj, list):
|
|
new_list = []
|
|
for elem in obj:
|
|
elem = _clean_nones(elem)
|
|
if elem is not None:
|
|
new_list.append(elem)
|
|
return new_list
|
|
|
|
return obj
|
|
|
|
|
|
class Resource(object):
|
|
def __init__(self, _name, _info):
|
|
self._name = _name
|
|
self._info = _clean_nones(_info)
|
|
|
|
def __getattr__(self, k):
|
|
if k not in self.__dict__:
|
|
return self._info.get(k)
|
|
return self.__dict__[k]
|
|
|
|
def __repr__(self):
|
|
return '<%s %s>' % (self._name, self._info)
|
|
|
|
def __eq__(self, other):
|
|
return self._name == other._name and self._info == other._info
|
|
|
|
@property
|
|
def dict(self):
|
|
return self._info
|
|
|
|
@property
|
|
def wrapped_dict(self):
|
|
return {self._name: self._info}
|
|
|
|
|
|
def _node_template(nt):
|
|
if not nt:
|
|
raise ex.NodeTemplateNotFoundException(nt)
|
|
|
|
d = {
|
|
'id': nt.id,
|
|
'name': nt.name,
|
|
'node_type': {
|
|
'name': nt.node_type.name,
|
|
'processes': [p.name for p in nt.node_type.processes]},
|
|
'flavor_id': nt.flavor_id
|
|
}
|
|
|
|
for conf in nt.node_template_configs:
|
|
c_section = conf.node_process_property.node_process.name
|
|
c_name = conf.node_process_property.name
|
|
c_value = conf.value
|
|
if c_section not in d:
|
|
d[c_section] = dict()
|
|
d[c_section][c_name] = c_value
|
|
|
|
return Resource('node_template', d)
|
|
|
|
|
|
def _cluster(cluster):
|
|
if not cluster:
|
|
raise ex.ClusterNotFoundException(cluster)
|
|
|
|
d = {
|
|
'id': cluster.id,
|
|
'name': cluster.name,
|
|
'base_image_id': cluster.base_image_id,
|
|
'status': cluster.status,
|
|
'service_urls': {},
|
|
'node_templates': {},
|
|
'nodes': [{'vm_id': n.vm_id,
|
|
'node_template': {
|
|
'id': n.node_template.id,
|
|
'name': n.node_template.name
|
|
}}
|
|
for n in cluster.nodes]
|
|
}
|
|
for ntc in cluster.node_counts:
|
|
d['node_templates'][ntc.node_template.name] = ntc.count
|
|
|
|
for service in cluster.service_urls:
|
|
d['service_urls'][service.name] = service.url
|
|
|
|
return Resource('cluster', d)
|
|
|
|
|
|
def _node_type(nt):
|
|
if not nt:
|
|
raise ex.NodeTypeNotFoundException(nt)
|
|
|
|
d = {
|
|
'id': nt.id,
|
|
'name': nt.name,
|
|
'processes': [p.name for p in nt.processes]
|
|
}
|
|
|
|
return Resource('node_type', d)
|