**** CubicPower OpenStack Study ****
# coding=utf-8
# Copyright (c) 2012 NTT DOCOMO, INC.
# 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 re
from oslo.config import cfg
from nova import context as nova_context
from nova import exception
from nova import network
from nova.openstack.common.gettextutils import _
from nova.openstack.common import importutils
from nova.openstack.common import log as logging
from nova.openstack.common import processutils
from nova import utils
from nova.virt.baremetal import db as bmdb
from nova.virt import volumeutils
opts = [
cfg.BoolOpt('use_unsafe_iscsi',
default=False,
help='Do not set this out of dev/test environments. '
'If a node does not have a fixed PXE IP address, '
'volumes are exported with globally opened ACL'),
cfg.StrOpt('iscsi_iqn_prefix',
default='iqn.2010-10.org.openstack.baremetal',
help='The iSCSI IQN prefix used in baremetal volume '
'connections.'),
]
baremetal_group = cfg.OptGroup(name='baremetal',
title='Baremetal Options')
CONF = cfg.CONF
CONF.register_group(baremetal_group)
CONF.register_opts(opts, baremetal_group)
CONF.import_opt('host', 'nova.netconf')
CONF.import_opt('use_ipv6', 'nova.netconf')
CONF.import_opt('volume_drivers', 'nova.virt.libvirt.driver', group='libvirt')
LOG = logging.getLogger(__name__)
**** CubicPower OpenStack Study ****
def _get_baremetal_node_by_instance_uuid(instance_uuid):
context = nova_context.get_admin_context()
return bmdb.bm_node_get_by_instance_uuid(context, instance_uuid)
**** CubicPower OpenStack Study ****
def _create_iscsi_export_tgtadm(path, tid, iqn):
utils.execute('tgtadm', '--lld', 'iscsi',
'--mode', 'target',
'--op', 'new',
'--tid', tid,
'--targetname', iqn,
run_as_root=True)
utils.execute('tgtadm', '--lld', 'iscsi',
'--mode', 'logicalunit',
'--op', 'new',
'--tid', tid,
'--lun', '1',
'--backing-store', path,
run_as_root=True)
**** CubicPower OpenStack Study ****
def _allow_iscsi_tgtadm(tid, address):
utils.execute('tgtadm', '--lld', 'iscsi',
'--mode', 'target',
'--op', 'bind',
'--tid', tid,
'--initiator-address', address,
run_as_root=True)
**** CubicPower OpenStack Study ****
def _delete_iscsi_export_tgtadm(tid):
try:
utils.execute('tgtadm', '--lld', 'iscsi',
'--mode', 'logicalunit',
'--op', 'delete',
'--tid', tid,
'--lun', '1',
run_as_root=True)
except processutils.ProcessExecutionError:
pass
try:
utils.execute('tgtadm', '--lld', 'iscsi',
'--mode', 'target',
'--op', 'delete',
'--tid', tid,
run_as_root=True)
except processutils.ProcessExecutionError:
pass
# Check if the tid is deleted, that is, check the tid no longer exists.
# If the tid dose not exist, tgtadm returns with exit_code 22.
# utils.execute() can check the exit_code if check_exit_code parameter is
# passed. But, regardless of whether check_exit_code contains 0 or not,
# if the exit_code is 0, the function dose not report errors. So we have to
# catch a ProcessExecutionError and test its exit_code is 22.
try:
utils.execute('tgtadm', '--lld', 'iscsi',
'--mode', 'target',
'--op', 'show',
'--tid', tid,
run_as_root=True)
except processutils.ProcessExecutionError as e:
if e.exit_code == 22:
# OK, the tid is deleted
return
raise
raise exception.NovaException(_(
'baremetal driver was unable to delete tid %s') % tid)
**** CubicPower OpenStack Study ****
def _show_tgtadm():
out, _ = utils.execute('tgtadm', '--lld', 'iscsi',
'--mode', 'target',
'--op', 'show',
run_as_root=True)
return out
**** CubicPower OpenStack Study ****
def _list_backingstore_path():
out = _show_tgtadm()
l = []
for line in out.split('\n'):
m = re.search(r'Backing store path: (.*)$', line)
if m:
if '/' in m.group(1):
l.append(m.group(1))
return l
**** CubicPower OpenStack Study ****
def _get_next_tid():
out = _show_tgtadm()
last_tid = 0
for line in out.split('\n'):
m = re.search(r'^Target (\d+):', line)
if m:
tid = int(m.group(1))
if last_tid < tid:
last_tid = tid
return last_tid + 1
**** CubicPower OpenStack Study ****
def _find_tid(iqn):
out = _show_tgtadm()
pattern = r'^Target (\d+): *' + re.escape(iqn)
for line in out.split('\n'):
m = re.search(pattern, line)
if m:
return int(m.group(1))
return None
**** CubicPower OpenStack Study ****
def _get_iqn(instance_name, mountpoint):
mp = mountpoint.replace('/', '-').strip('-')
iqn = '%s:%s-%s' % (CONF.baremetal.iscsi_iqn_prefix,
instance_name,
mp)
return iqn
**** CubicPower OpenStack Study ****
def _get_fixed_ips(instance):
context = nova_context.get_admin_context()
nw_info = network.API().get_instance_nw_info(context, instance)
ips = nw_info.fixed_ips()
return ips
**** CubicPower OpenStack Study ****
class VolumeDriver(object):
**** CubicPower OpenStack Study ****
def __init__(self, virtapi):
super(VolumeDriver, self).__init__()
self.virtapi = virtapi
self._initiator = None
**** CubicPower OpenStack Study ****
def get_volume_connector(self, instance):
if not self._initiator:
self._initiator = volumeutils.get_iscsi_initiator()
if not self._initiator:
LOG.warn(_('Could not determine iscsi initiator name'),
instance=instance)
return {
'ip': CONF.my_ip,
'initiator': self._initiator,
'host': CONF.host,
}
**** CubicPower OpenStack Study ****
def attach_volume(self, connection_info, instance, mountpoint):
raise NotImplementedError()
**** CubicPower OpenStack Study ****
def detach_volume(self, connection_info, instance, mountpoint):
raise NotImplementedError()
**** CubicPower OpenStack Study ****
class LibvirtVolumeDriver(VolumeDriver):
"""The VolumeDriver delegates to nova.virt.libvirt.volume."""
**** CubicPower OpenStack Study ****
def __init__(self, virtapi):
super(LibvirtVolumeDriver, self).__init__(virtapi)
self.volume_drivers = {}
for driver_str in CONF.libvirt.volume_drivers:
driver_type, _sep, driver = driver_str.partition('=')
driver_class = importutils.import_class(driver)
self.volume_drivers[driver_type] = driver_class(self)
**** CubicPower OpenStack Study ****
def _volume_driver_method(self, method_name, connection_info,
*args, **kwargs):
driver_type = connection_info.get('driver_volume_type')
if driver_type not in self.volume_drivers:
raise exception.VolumeDriverNotFound(driver_type=driver_type)
driver = self.volume_drivers[driver_type]
method = getattr(driver, method_name)
return method(connection_info, *args, **kwargs)
**** CubicPower OpenStack Study ****
def attach_volume(self, connection_info, instance, mountpoint):
fixed_ips = _get_fixed_ips(instance)
if not fixed_ips:
if not CONF.baremetal.use_unsafe_iscsi:
raise exception.NovaException(_(
'No fixed PXE IP is associated to %s') % instance['uuid'])
mount_device = mountpoint.rpartition("/")[2]
disk_info = {
'dev': mount_device,
'bus': 'baremetal',
'type': 'baremetal',
}
conf = self._connect_volume(connection_info, disk_info)
self._publish_iscsi(instance, mountpoint, fixed_ips,
conf.source_path)
**** CubicPower OpenStack Study ****
def _connect_volume(self, connection_info, disk_info):
return self._volume_driver_method('connect_volume',
connection_info,
disk_info)
**** CubicPower OpenStack Study ****
def _publish_iscsi(self, instance, mountpoint, fixed_ips, device_path):
iqn = _get_iqn(instance['name'], mountpoint)
tid = _get_next_tid()
_create_iscsi_export_tgtadm(device_path, tid, iqn)
if fixed_ips:
for ip in fixed_ips:
_allow_iscsi_tgtadm(tid, ip['address'])
else:
# NOTE(NTTdocomo): Since nova-compute does not know the
# instance's initiator ip, it allows any initiators
# to connect to the volume. This means other bare-metal
# instances that are not attached the volume can connect
# to the volume. Do not set CONF.baremetal.use_unsafe_iscsi
# out of dev/test environments.
# TODO(NTTdocomo): support CHAP
_allow_iscsi_tgtadm(tid, 'ALL')
**** CubicPower OpenStack Study ****
def detach_volume(self, connection_info, instance, mountpoint):
mount_device = mountpoint.rpartition("/")[2]
try:
self._depublish_iscsi(instance, mountpoint)
finally:
self._disconnect_volume(connection_info, mount_device)
**** CubicPower OpenStack Study ****
def _disconnect_volume(self, connection_info, disk_dev):
return self._volume_driver_method('disconnect_volume',
connection_info,
disk_dev)
**** CubicPower OpenStack Study ****
def _depublish_iscsi(self, instance, mountpoint):
iqn = _get_iqn(instance['name'], mountpoint)
tid = _find_tid(iqn)
if tid is not None:
_delete_iscsi_export_tgtadm(tid)
else:
LOG.warn(_('detach volume could not find tid for %s'), iqn,
instance=instance)
**** CubicPower OpenStack Study ****
def get_all_block_devices(self):
"""Return all block devices in use on this node."""
return _list_backingstore_path()
**** CubicPower OpenStack Study ****
def get_hypervisor_version(self):
"""A dummy method for LibvirtBaseVolumeDriver.connect_volume."""
return 1