diff --git a/tacker/vm/hosting_device_scheduler.py b/tacker/vm/hosting_device_scheduler.py new file mode 100644 index 000000000..a14398109 --- /dev/null +++ b/tacker/vm/hosting_device_scheduler.py @@ -0,0 +1,130 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 +# +# Copyright 2013, 2014 Intel Corporation. +# Copyright 2013, 2014 Isaku Yamahata +# +# 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. +# +# @author: Isaku Yamahata, Intel Corporation. + +import random + +import sqlalchemy as sa + +from tacker.db.vm import vm_db +from tacker.openstack.common import log as logging +from tacker.plugins.common import constants + + +LOG = logging.getLogger(__name__) + + +class ChanceScheduler(object): + + """Select a Device that can serve a service in a random way.""" + + def schedule(self, plugin, context, + service_type, service_instance_id, name, service_context): + """ + :param service_context: list of DeviceServiceContext + without service_instance_id + [{'network_id': network_id, + 'subnet_id': subnet_id, + 'port_id': port_id, + 'router_id': router_id, + 'role': role, + 'index': index}, + ... ] + They can be missing or None = don't care + """ + with context.session.begin(subtransactions=True): + # Race. prevent from inserting ServiceDeviceBinding + + # select hosting device that is capable of service_type, but + # not yet used for it. + # i.e. + # device.service_type in + # [st.service_types for st in + # device.template.service_types] + # and + # device.sevice_type not in + # [ls.service_type for ls in device.services] + query = ( + context.session.query(vm_db.Device). + filter(vm_db.Device.status == constants.ACTIVE). + filter( + sa.exists(). + where(sa.and_( + vm_db.Device.template_id == vm_db.DeviceTemplate.id, + vm_db.DeviceTemplate.id == + vm_db.ServiceType.template_id, + vm_db.ServiceType.service_type == service_type))). + filter( + ~sa.exists(). + where(sa.and_( + vm_db.Device.id == + vm_db.ServiceDeviceBinding.device_id, + vm_db.ServiceDeviceBinding.service_instance_id == + vm_db.ServiceInstance.id, + vm_db.ServiceInstance.service_type_id == + vm_db.ServiceType.id, + vm_db.ServiceType.service_type == service_type)))) + + for sc_entry in service_context: + network_id = sc_entry.get('network_id') + subnet_id = sc_entry.get('subnet_id') + port_id = sc_entry.get('port_id') + router_id = sc_entry.get('router_id') + role = sc_entry.get('role') + index = sc_entry.get('index') + + expr = [ + vm_db.Device.id == vm_db.DeviceServiceContext.device_id] + if network_id is not None: + expr.append( + vm_db.DeviceServiceContext.network_id == network_id) + if subnet_id is not None: + expr.append( + vm_db.DeviceServiceContext.subnet_id == subnet_id) + if port_id is not None: + expr.append(vm_db.DeviceServiceContext.port_id == port_id) + if router_id is not None: + expr.append( + vm_db.DeviceServiceContext.router_id == router_id) + if role is not None: + expr.append(vm_db.DeviceServiceContext.role == role) + if index is not None: + expr.append(vm_db.DeviceServiceContext.index == index) + query = query.filter(sa.exists().where(sa.and_(*expr))) + + candidates = query.with_lockmode("update").all() + if not candidates: + LOG.debug(_('no hosting device supporing %s'), service_type) + return + device = random.choice(candidates) + + service_type_id = [s.id for s in device.template.service_types + if s.service_type == service_type][0] + + service_instance_param = { + 'name': name, + 'service_table_id': service_instance_id, + 'service_type': service_type, + 'service_type_id': service_type_id, + } + service_instance_dict = plugin._create_service_instance( + context, device.id, service_instance_param, False) + return (plugin._make_device_dict(device), service_instance_dict)