murano/murano/db/services/environments.py

286 lines
10 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.
from keystoneclient import exceptions as ks_exceptions
from oslo_config import cfg
from oslo_log import log as logging
import six
import yaml
from murano.common import auth_utils
from murano.common import uuidutils
from murano.db import models
from murano.db.services import sessions
from murano.db import session as db_session
from murano.services import states
CONF = cfg.CONF
LOG = logging.getLogger(__name__)
DEFAULT_NETWORK_TYPES = {
"nova": 'io.murano.resources.NovaNetwork',
"neutron": 'io.murano.resources.NeutronNetwork'
}
class EnvironmentServices(object):
@staticmethod
def get_environments_by(filters):
"""Returns list of environments
:param filters: property filters
:return: Returns list of environments
"""
unit = db_session.get_session()
environments = unit.query(models.Environment). \
filter_by(**filters).all()
for env in environments:
env['status'] = EnvironmentServices.get_status(env['id'])
return environments
@staticmethod
def get_status(environment_id):
"""Environment can have one of the following statuses:
- deploying: there is ongoing deployment for environment
- deleting: environment is currently being deleted
- deploy failure: last deployment session has failed
- delete failure: last delete session has failed
- pending: there is at least one session with status `open` and no
errors in previous sessions
- ready: there are no sessions for environment
:param environment_id: Id of environment for which we checking status.
:return: Environment status
"""
# Deploying: there is at least one valid session with status deploying
session_list = sessions.SessionServices.get_sessions(environment_id)
has_opened = False
for session in session_list:
if session.state == states.SessionState.DEPLOYING:
return states.EnvironmentStatus.DEPLOYING
elif session.state == states.SessionState.DELETING:
return states.EnvironmentStatus.DELETING
elif session.state == states.SessionState.DEPLOY_FAILURE:
return states.EnvironmentStatus.DEPLOY_FAILURE
elif session.state == states.SessionState.DELETE_FAILURE:
return states.EnvironmentStatus.DELETE_FAILURE
elif session.state == states.SessionState.OPENED:
has_opened = True
elif session.state == states.SessionState.DEPLOYED:
break
if has_opened:
return states.EnvironmentStatus.PENDING
return states.EnvironmentStatus.READY
@staticmethod
def create(environment_params, context):
# tagging environment by tenant_id for later checks
"""Creates environment with specified params, in particular - name
:param environment_params: Dict, e.g. {'name': 'env-name'}
:param context: request context to get the tenant id and the token
:return: Created Environment
"""
objects = {'?': {
'id': uuidutils.generate_uuid(),
}}
network_driver = EnvironmentServices.get_network_driver(context)
objects.update(environment_params)
if not objects.get('defaultNetworks'):
objects['defaultNetworks'] = \
EnvironmentServices.generate_default_networks(objects['name'],
network_driver)
objects['?']['type'] = 'io.murano.Environment'
objects['?']['metadata'] = {}
data = {
'Objects': objects,
'Attributes': [],
'project_id': context.tenant,
'user_id': context.user
}
environment_params['tenant_id'] = context.tenant
environment = models.Environment()
environment.update(environment_params)
unit = db_session.get_session()
with unit.begin():
unit.add(environment)
# saving environment as Json to itself
environment.update({'description': data})
environment.save(unit)
return environment
@staticmethod
def delete(environment_id, session_id):
"""Deletes environment and notify orchestration engine about deletion
:param environment_id: Environment that is going to be deleted
:param session_id: Session Id
"""
env_description = EnvironmentServices.get_environment_description(
environment_id, session_id, False)
env_description['Objects'] = None
EnvironmentServices.save_environment_description(
session_id, env_description, False)
@staticmethod
def remove(environment_id):
unit = db_session.get_session()
environment = unit.query(models.Environment).get(environment_id)
if environment:
with unit.begin():
unit.delete(environment)
@staticmethod
def get_environment_description(environment_id, session_id=None,
inner=True):
"""Returns environment description for specified environment.
If session is specified and not in deploying state function
returns modified environment description,
otherwise returns actual environment desc.
:param environment_id: Environment Id
:param session_id: Session Id
:param inner: return contents of environment rather than whole
Object Model structure
:return: Environment Description Object
"""
unit = db_session.get_session()
if session_id:
session = unit.query(models.Session).get(session_id)
if sessions.SessionServices.validate(session):
if session.state != states.SessionState.DEPLOYED:
env_description = session.description
else:
env = unit.query(models.Environment) \
.get(session.environment_id)
env_description = env.description
else:
env = unit.query(models.Environment) \
.get(session.environment_id)
env_description = env.description
else:
env = (unit.query(models.Environment).get(environment_id))
env_description = env.description
if not inner:
return env_description
else:
return env_description['Objects']
@staticmethod
def save_environment_description(session_id, environment, inner=True):
"""Saves environment description to specified session.
:param session_id: Session Id
:param environment: Environment Description
:param inner: save modifications to only content of environment
rather than whole Object Model structure
"""
unit = db_session.get_session()
session = unit.query(models.Session).get(session_id)
if inner:
data = session.description.copy()
data['Objects'] = environment
session.description = data
else:
session.description = environment
session.save(unit)
@staticmethod
def generate_default_networks(env_name, network_driver):
net_config = CONF.find_file(
CONF.networking.network_config_file)
if net_config:
LOG.debug("Loading network configuration from file")
with open(net_config) as f:
data = yaml.safe_load(f)
return EnvironmentServices._objectify(data, {
'ENV': env_name
})
network_type = DEFAULT_NETWORK_TYPES[network_driver]
LOG.debug("Setting '{net_type}' as environment's "
"default network".format(net_type=network_type))
return {
'environment': {
'?': {
'id': uuidutils.generate_uuid(),
'type': network_type
},
'name': env_name + '-network'
},
'flat': None
}
@staticmethod
def deploy(session, unit, context):
environment = unit.query(models.Environment).get(
session.environment_id)
if (session.description['Objects'] is None and
'ObjectsCopy' not in session.description):
EnvironmentServices.remove(session.environment_id)
else:
sessions.SessionServices.deploy(
session, environment, unit, context)
@staticmethod
def _objectify(data, replacements):
if isinstance(data, dict):
if isinstance(data.get('?'), dict):
data['?']['id'] = uuidutils.generate_uuid()
result = {}
for key, value in six.iteritems(data):
result[key] = EnvironmentServices._objectify(
value, replacements)
return result
elif isinstance(data, list):
return [EnvironmentServices._objectify(v, replacements)
for v in data]
elif isinstance(data, six.string_types):
for key, value in six.iteritems(replacements):
data = data.replace('%' + key + '%', value)
return data
@staticmethod
def get_network_driver(context):
driver = CONF.networking.driver
if driver:
LOG.debug("Will use {} as a network driver".format(driver))
return driver
session = auth_utils.get_token_client_session(
context.auth_token, context.tenant)
try:
session.get_endpoint(service_type='network')
except ks_exceptions.EndpointNotFound:
LOG.debug("Will use NovaNetwork as a network driver")
return "nova"
else:
LOG.debug("Will use Neutron as a network driver")
return "neutron"