blazar/climate/manager/service.py
Dina Belova f50a50b3fe Implement Manager service.
Implement RPC service to work with plugins and DB.
Base plugin class added.

Implements: blueprint lease-manager
Change-Id: Icbed7fabef6c0673c62f67017e5e9cd8d257b5ee
2013-11-14 17:50:11 +01:00

214 lines
7.5 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 datetime
import eventlet
import six
from oslo.config import cfg
from stevedore import enabled
from climate.db import api as db_api
from climate import exceptions
from climate.openstack.common import log as logging
from climate.openstack.common.rpc import service as rpc_service
from climate.utils import service as service_utils
manager_opts = [
cfg.StrOpt('rpc_topic',
default='climate.manager',
help='The topic Climate uses for climate-manager messages.'),
cfg.ListOpt('plugins',
default=['dummy.vm.plugin'],
help='All plugins to use (one for every resource type to '
'support.)'),
]
CONF = cfg.CONF
CONF.register_opts(manager_opts, 'manager')
LOG = logging.getLogger(__name__)
class ManagerService(rpc_service.Service):
"""Service class for the climate-manager service.
Responsible for working with Climate DB, scheduling logic, running events,
working with plugins, etc.
"""
RPC_API_VERSION = '1.0'
def __init__(self, host):
super(ManagerService, self).__init__(host, CONF.manager.rpc_topic)
self.plugins = self._get_plugins()
self.resource_actions = self._setup_actions()
def start(self):
super(ManagerService, self).start()
self.tg.add_timer(10, self._event)
def _get_plugins(self):
"""Return dict of resource-plugin class pairs."""
config_plugins = CONF.manager.plugins
plugins = {}
extension_manager = enabled.EnabledExtensionManager(
check_func=lambda ext: ext.name in config_plugins,
namespace='climate.resource.plugins',
invoke_on_load=True
)
for ext in extension_manager.extensions:
if ext.obj.resource_type in plugins:
raise exceptions.ClimateException(
'You have provided several plugins for one resource type '
'in configuration file. '
'Please set one plugin per resource type.'
)
plugins[ext.obj.resource_type] = ext.obj
if len(plugins) < len(config_plugins):
raise exceptions.ClimateException('Not all requested plugins are '
'loaded.')
return plugins
def _setup_actions(self):
"""Setup actions for each resource type supported.
BasePlugin interface provides only on_start and on_end behaviour now.
If there are some configs needed by plugin, they should be returned
from get_plugin_opts method. These flags are registered in
[resource_type] group of configuration file.
"""
actions = {}
for resource_type, plugin in six.iteritems(self.plugins):
plugin = self.plugins[resource_type]
CONF.register_opts(plugin.get_plugin_opts(), group=resource_type)
actions[resource_type] = {}
actions[resource_type]['on_start'] = plugin.on_start
actions[resource_type]['on_end'] = plugin.on_end
return actions
@service_utils.with_empty_context
def _event(self):
"""Tries to commit event.
If there is an event in Climate DB to be done, do it and change its
status to 'DONE'.
"""
LOG.debug('Trying to get event from DB.')
events = db_api.event_get_all_sorted_by_filters(
sort_key='time',
sort_dir='asc',
filters={'status': 'UNDONE'}
)
if not events:
return
event = events[0]
if event['time'] < datetime.datetime.utcnow():
db_api.event_update(event['id'], {'status': 'IN_PROGRESS'})
event_type = event['event_type']
event_fn = getattr(self, event_type, None)
if event_fn is None:
raise exceptions.ClimateException('Event type %s is not '
'supported' % event_type)
try:
eventlet.spawn_n(service_utils.with_empty_context(event_fn),
event['lease_id'], event['id'])
except Exception:
db_api.event_update(event['id'], {'status': 'ERROR'})
LOG.exception('Error occurred while event handling.')
@service_utils.export_context
def get_lease(self, lease_id):
return db_api.lease_get(lease_id)
@service_utils.export_context
def list_leases(self):
return db_api.lease_list()
@service_utils.export_context
def create_lease(self, lease_values):
start_date = lease_values['start_date']
end_date = lease_values['end_date']
if start_date == 'now':
start_date = datetime.datetime.utcnow()
else:
start_date = datetime.datetime.strptime(start_date,
"%Y-%m-%d %H:%M")
end_date = datetime.datetime.strptime(end_date, "%Y-%m-%d %H:%M")
lease_values['start_date'] = start_date
lease_values['end_date'] = end_date
if not lease_values.get('events'):
lease_values['events'] = []
lease_values['events'].append({'event_type': 'start_lease',
'time': start_date,
'status': 'UNDONE'})
lease_values['events'].append({'event_type': 'end_lease',
'time': end_date,
'status': 'UNDONE'})
lease = db_api.lease_create(lease_values)
return db_api.lease_get(lease['id'])
@service_utils.export_context
def update_lease(self, lease_id, values):
if values:
db_api.lease_update(lease_id, values)
return db_api.lease_get(lease_id)
@service_utils.export_context
def delete_lease(self, lease_id):
lease = self.get_lease(lease_id)
for reservation in lease['reservations']:
self.plugins[reservation['resource_type']]\
.on_end(reservation['resource_id'])
db_api.lease_destroy(lease_id)
def start_lease(self, lease_id, event_id):
self._basic_action(lease_id, event_id, 'on_start', 'active')
def end_lease(self, lease_id, event_id):
self._basic_action(lease_id, event_id, 'on_end', 'deleted')
def _basic_action(self, lease_id, event_id, action_time,
reservation_status=None):
"""Commits basic lease actions such as starting and ending."""
lease = self.get_lease(lease_id)
for reservation in lease['reservations']:
resource_type = reservation['resource_type']
self.resource_actions[resource_type][action_time](
reservation['resource_id']
)
if reservation_status is not None:
db_api.reservation_update(reservation['id'],
{'status': reservation_status})
db_api.event_update(event_id, {'status': 'DONE'})