tacker/tacker/vm/infra_drivers/nova/nova.py

278 lines
10 KiB
Python

# Copyright 2013, 2014 Intel Corporation.
# 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.
import time
from keystoneclient import auth as ks_auth
from keystoneclient.auth.identity import v2 as v2_auth
from keystoneclient import session as ks_session
from oslo_config import cfg
from oslo_log import log as logging
from six import iteritems
from tacker.api.v1 import attributes
from tacker.i18n import _LE, _LW
from tacker.openstack.common import versionutils
from tacker.vm.infra_drivers import abstract_driver
LOG = logging.getLogger(__name__)
CONF = cfg.CONF
NOVA_API_VERSION = "2"
TACKER_NOVA_CONF_SECTION = 'tacker_nova'
ks_session.Session.register_conf_options(cfg.CONF, TACKER_NOVA_CONF_SECTION)
ks_auth.register_conf_options(cfg.CONF, TACKER_NOVA_CONF_SECTION)
OPTS = [
cfg.StrOpt('region_name',
help=_('Name of nova region to use. Useful if keystone manages'
' more than one region.')),
]
CONF.register_opts(OPTS, group=TACKER_NOVA_CONF_SECTION)
_NICS = 'nics' # converted by novaclient => 'networks'
_NET_ID = 'net-id' # converted by novaclient => 'uuid'
_PORT_ID = 'port-id' # converted by novaclient => 'port'
_FILES = 'files'
class DefaultAuthPlugin(v2_auth.Password):
"""A wrapper around standard v2 user/pass to handle bypass url.
This is only necessary because novaclient doesn't support endpoint_override
yet - bug #1403329.
When this bug is fixed we can pass the endpoint_override to the client
instead and remove this class.
"""
def __init__(self, **kwargs):
self._endpoint_override = kwargs.pop('endpoint_override', None)
super(DefaultAuthPlugin, self).__init__(**kwargs)
def get_endpoint(self, session, **kwargs):
if self._endpoint_override:
return self._endpoint_override
return super(DefaultAuthPlugin, self).get_endpoint(session, **kwargs)
@versionutils.deprecated(
versionutils.deprecated.MITAKA,
what='infra_driver nova',
in_favor_of='infra_driver heat',
remove_in=+1)
class DeviceNova(abstract_driver.DeviceAbstractDriver):
"""Nova driver of hosting device."""
def __init__(self):
super(DeviceNova, self).__init__()
# avoid circular import
from novaclient import client
self._novaclient = client
def _nova_client(self, token=None):
auth = ks_auth.load_from_conf_options(cfg.CONF,
TACKER_NOVA_CONF_SECTION)
endpoint_override = None
if not auth:
LOG.warning(_LW('Authenticating to nova using nova_admin_* options'
' is deprecated. This should be done using'
' an auth plugin, like password'))
if cfg.CONF.nova_admin_tenant_id:
endpoint_override = "%s/%s" % (cfg.CONF.nova_url,
cfg.CONF.nova_admin_tenant_id)
auth = DefaultAuthPlugin(
auth_url=cfg.CONF.nova_admin_auth_url,
username=cfg.CONF.nova_admin_username,
password=cfg.CONF.nova_admin_password,
tenant_id=cfg.CONF.nova_admin_tenant_id,
tenant_name=cfg.CONF.nova_admin_tenant_name,
endpoint_override=endpoint_override)
session = ks_session.Session.load_from_conf_options(
cfg.CONF, TACKER_NOVA_CONF_SECTION, auth=auth)
novaclient_cls = self._novaclient.get_client_class(NOVA_API_VERSION)
return novaclient_cls(session=session,
region_name=cfg.CONF.tacker_nova.region_name)
def get_type(self):
return 'nova'
def get_name(self):
return 'nova'
def get_description(self):
return 'Nuetron Device Nova driver'
@staticmethod
def _safe_pop(d, name_list):
res = None
for name in name_list:
if name in d:
res = d.pop(name)
break
return res
def _create_port(self, plugin, context, tenant_id,
network_id=None, subnet_id=None):
# resolve subnet and create port
LOG.debug(_('network_id %(network_id)s subnet_id %(subnet_id)s)'),
{'network_id': network_id, 'subnet_id': subnet_id})
if subnet_id:
subnet = plugin._core_plugin.get_subnet(context, subnet_id)
network_id = subnet['network_id']
port_data = {
'tenant_id': tenant_id,
'network_id': network_id,
'admin_state_up': True,
'fixed_ips': attributes.ATTR_NOT_SPECIFIED,
}
if subnet_id:
port_data['fixed_ips'] = [{'subnet_id': subnet_id}]
# See api.v2.base.prepare_request_body()
for attr, attr_vals in iteritems(attributes.RESOURCE_ATTRIBUTE_MAP[
attributes.PORTS]):
if not attr_vals.get('allow_post', False):
continue
if attr in port_data:
continue
port_data[attr] = attr_vals['default']
LOG.debug(_('port_data %s'), port_data)
port = plugin._core_plugin.create_port(context, {'port': port_data})
LOG.debug(_('port %s'), port)
return port['id']
def create(self, plugin, context, device):
# typical required arguments are
# 'name': name string
# 'image': uuid
# 'flavir': uuid
#
# for details, see the signature of
# novaclient.v<version>.servers.SeverManager.create()
LOG.debug(_('device %s'), device)
# flavor and image are specially treated by novaclient
attributes = device['device_template']['attributes'].copy()
attributes.update(device['kwargs'])
name = self._safe_pop(attributes, ('name', ))
if name is None:
# TODO(yamahata): appropreate way to generate instance name
name = (__name__ + ':' + self.__class__.__name__ + '-' +
device['id'])
image = self._safe_pop(attributes, ('image', 'imageRef'))
flavor = self._safe_pop(attributes, ('flavor', 'flavorRef'))
files = plugin.mgmt_get_config(context, device)
if files:
attributes[_FILES] = files
LOG.debug(_('service_context: %s'), device.get('service_context', []))
tenant_id = device['tenant_id']
nics = []
for sc_entry in device.get('service_context', []):
LOG.debug(_('sc_entry: %s'), sc_entry)
# nova API doesn't return tacker port_id.
# so create port if necessary by hand, and use it explicitly.
if sc_entry['port_id']:
LOG.debug(_('port_id %s specified'), sc_entry['port_id'])
port_id = sc_entry['port_id']
elif sc_entry['subnet_id']:
LOG.debug(_('subnet_id %s specified'), sc_entry['subnet_id'])
port_id = self._create_port(plugin, context, tenant_id,
subnet_id=sc_entry['subnet_id'])
elif sc_entry['network_id']:
LOG.debug(_('network_id %s specified'), sc_entry['network_id'])
port_id = self._create_port(plugin, context, tenant_id,
network_id=sc_entry['network_id'])
else:
LOG.debug(_('skipping sc_entry %s'), sc_entry)
continue
LOG.debug(_('port_id %s'), port_id)
port = plugin._core_plugin.get_port(context, port_id)
sc_entry['network_id'] = port['network_id']
if not sc_entry['subnet_id'] and port['fixed_ips']:
sc_entry['subnet_id'] = port['fixed_ips'][0]['subnet_id']
sc_entry['port_id'] = port_id
nics.append({_PORT_ID: port_id})
if nics:
attributes[_NICS] = nics
LOG.debug(_('nics %(nics)s attributes %(attributes)s'),
{'nics': nics, 'attributes': attributes})
nova = self._nova_client()
instance = nova.servers.create(name, image, flavor, **attributes)
return instance.id
def create_wait(self, plugin, context, device_dict, device_id):
nova = self._nova_client()
instance = nova.servers.get(device_id)
status = instance.status
# TODO(yamahata): timeout and error
while status == 'BUILD':
time.sleep(5)
instance = nova.servers.get(instance.id)
status = instance.status
LOG.debug(_('status: %s'), status)
LOG.debug(_('status: %s'), status)
if status == 'ERROR':
raise RuntimeError(_("creation of server %s faild") % device_id)
def update(self, plugin, context, device_id, device_dict, device):
# do nothing but checking if the instance exists at the moment
nova = self._nova_client()
nova.servers.get(device_id)
def update_wait(self, plugin, context, device_id):
# do nothing but checking if the instance exists at the moment
nova = self._nova_client()
nova.servers.get(device_id)
def delete(self, plugin, context, device_id):
nova = self._nova_client()
try:
instance = nova.servers.get(device_id)
except self._novaclient.exceptions.NotFound:
LOG.error(_LE("server %s is not found") %
device_id)
return
instance.delete()
def delete_wait(self, plugin, context, device_id):
nova = self._nova_client()
# TODO(yamahata): timeout and error
while True:
try:
instance = nova.servers.get(device_id)
LOG.debug(_('instance status %s'), instance.status)
except self._novaclient.exceptions.NotFound:
break
if instance.status == 'ERROR':
raise RuntimeError(_("deletion of server %s faild") %
device_id)
time.sleep(5)