deleted brick
This commit is contained in:
parent
3daf82a5d9
commit
65d8167a0f
manila/brick
@ -1,16 +0,0 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2013 OpenStack Foundation.
|
||||
# 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.
|
@ -1,16 +0,0 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2013 OpenStack Foundation.
|
||||
# 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.
|
@ -1,468 +0,0 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2010 United States Government as represented by the
|
||||
# Administrator of the National Aeronautics and Space Administration.
|
||||
# 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.
|
||||
"""
|
||||
Helper code for the iSCSI volume driver.
|
||||
|
||||
"""
|
||||
import os
|
||||
import re
|
||||
|
||||
from oslo.config import cfg
|
||||
|
||||
from manila import exception
|
||||
from manila import flags
|
||||
from manila.openstack.common import log as logging
|
||||
from manila import utils
|
||||
from manila.volume import utils as volume_utils
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
iscsi_helper_opt = [cfg.StrOpt('iscsi_helper',
|
||||
default='tgtadm',
|
||||
help='iscsi target user-land tool to use'),
|
||||
cfg.StrOpt('volumes_dir',
|
||||
default='$state_path/volumes',
|
||||
help='Volume configuration file storage '
|
||||
'directory'),
|
||||
cfg.StrOpt('iet_conf',
|
||||
default='/etc/iet/ietd.conf',
|
||||
help='IET configuration file'),
|
||||
cfg.StrOpt('lio_initiator_iqns',
|
||||
default='',
|
||||
help=('Comma-separatd list of initiator IQNs '
|
||||
'allowed to connect to the '
|
||||
'iSCSI target. (From Nova compute nodes.)'
|
||||
)
|
||||
),
|
||||
cfg.StrOpt('iscsi_iotype',
|
||||
default='fileio',
|
||||
help=('Sets the behavior of the iSCSI target '
|
||||
'to either perform blockio or fileio '
|
||||
'optionally, auto can be set and Manila '
|
||||
'will autodetect type of backing device')
|
||||
)
|
||||
]
|
||||
|
||||
FLAGS = flags.FLAGS
|
||||
FLAGS.register_opts(iscsi_helper_opt)
|
||||
FLAGS.import_opt('volume_name_template', 'manila.db')
|
||||
|
||||
|
||||
class TargetAdmin(object):
|
||||
"""iSCSI target administration.
|
||||
|
||||
Base class for iSCSI target admin helpers.
|
||||
"""
|
||||
|
||||
def __init__(self, cmd, execute):
|
||||
self._cmd = cmd
|
||||
self.set_execute(execute)
|
||||
|
||||
def set_execute(self, execute):
|
||||
"""Set the function to be used to execute commands."""
|
||||
self._execute = execute
|
||||
|
||||
def _run(self, *args, **kwargs):
|
||||
self._execute(self._cmd, *args, run_as_root=True, **kwargs)
|
||||
|
||||
def create_iscsi_target(self, name, tid, lun, path,
|
||||
chap_auth=None, **kwargs):
|
||||
"""Create a iSCSI target and logical unit"""
|
||||
raise NotImplementedError()
|
||||
|
||||
def remove_iscsi_target(self, tid, lun, vol_id, **kwargs):
|
||||
"""Remove a iSCSI target and logical unit"""
|
||||
raise NotImplementedError()
|
||||
|
||||
def _new_target(self, name, tid, **kwargs):
|
||||
"""Create a new iSCSI target."""
|
||||
raise NotImplementedError()
|
||||
|
||||
def _delete_target(self, tid, **kwargs):
|
||||
"""Delete a target."""
|
||||
raise NotImplementedError()
|
||||
|
||||
def show_target(self, tid, iqn=None, **kwargs):
|
||||
"""Query the given target ID."""
|
||||
raise NotImplementedError()
|
||||
|
||||
def _new_logicalunit(self, tid, lun, path, **kwargs):
|
||||
"""Create a new LUN on a target using the supplied path."""
|
||||
raise NotImplementedError()
|
||||
|
||||
def _delete_logicalunit(self, tid, lun, **kwargs):
|
||||
"""Delete a logical unit from a target."""
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
class TgtAdm(TargetAdmin):
|
||||
"""iSCSI target administration using tgtadm."""
|
||||
|
||||
def __init__(self, execute=utils.execute):
|
||||
super(TgtAdm, self).__init__('tgtadm', execute)
|
||||
|
||||
def _get_target(self, iqn):
|
||||
(out, err) = self._execute('tgt-admin', '--show', run_as_root=True)
|
||||
lines = out.split('\n')
|
||||
for line in lines:
|
||||
if iqn in line:
|
||||
parsed = line.split()
|
||||
tid = parsed[1]
|
||||
return tid[:-1]
|
||||
|
||||
return None
|
||||
|
||||
def create_iscsi_target(self, name, tid, lun, path,
|
||||
chap_auth=None, **kwargs):
|
||||
# Note(jdg) tid and lun aren't used by TgtAdm but remain for
|
||||
# compatibility
|
||||
|
||||
utils.ensure_tree(FLAGS.volumes_dir)
|
||||
|
||||
vol_id = name.split(':')[1]
|
||||
if chap_auth is None:
|
||||
volume_conf = """
|
||||
<target %s>
|
||||
backing-store %s
|
||||
</target>
|
||||
""" % (name, path)
|
||||
else:
|
||||
volume_conf = """
|
||||
<target %s>
|
||||
backing-store %s
|
||||
%s
|
||||
</target>
|
||||
""" % (name, path, chap_auth)
|
||||
|
||||
LOG.info(_('Creating iscsi_target for: %s') % vol_id)
|
||||
volumes_dir = FLAGS.volumes_dir
|
||||
volume_path = os.path.join(volumes_dir, vol_id)
|
||||
|
||||
f = open(volume_path, 'w+')
|
||||
f.write(volume_conf)
|
||||
f.close()
|
||||
|
||||
old_persist_file = None
|
||||
old_name = kwargs.get('old_name', None)
|
||||
if old_name is not None:
|
||||
old_persist_file = os.path.join(volumes_dir, old_name)
|
||||
|
||||
try:
|
||||
(out, err) = self._execute('tgt-admin',
|
||||
'--update',
|
||||
name,
|
||||
run_as_root=True)
|
||||
except exception.ProcessExecutionError, e:
|
||||
LOG.error(_("Failed to create iscsi target for volume "
|
||||
"id:%(vol_id)s.") % locals())
|
||||
|
||||
#Don't forget to remove the persistent file we created
|
||||
os.unlink(volume_path)
|
||||
raise exception.ISCSITargetCreateFailed(volume_id=vol_id)
|
||||
|
||||
iqn = '%s%s' % (FLAGS.iscsi_target_prefix, vol_id)
|
||||
tid = self._get_target(iqn)
|
||||
if tid is None:
|
||||
LOG.error(_("Failed to create iscsi target for volume "
|
||||
"id:%(vol_id)s. Please ensure your tgtd config file "
|
||||
"contains 'include %(volumes_dir)s/*'") % locals())
|
||||
raise exception.NotFound()
|
||||
|
||||
if old_persist_file is not None and os.path.exists(old_persist_file):
|
||||
os.unlink(old_persist_file)
|
||||
|
||||
return tid
|
||||
|
||||
def remove_iscsi_target(self, tid, lun, vol_id, **kwargs):
|
||||
LOG.info(_('Removing iscsi_target for: %s') % vol_id)
|
||||
vol_uuid_file = FLAGS.volume_name_template % vol_id
|
||||
volume_path = os.path.join(FLAGS.volumes_dir, vol_uuid_file)
|
||||
if os.path.isfile(volume_path):
|
||||
iqn = '%s%s' % (FLAGS.iscsi_target_prefix,
|
||||
vol_uuid_file)
|
||||
else:
|
||||
raise exception.ISCSITargetRemoveFailed(volume_id=vol_id)
|
||||
try:
|
||||
# NOTE(vish): --force is a workaround for bug:
|
||||
# https://bugs.launchpad.net/manila/+bug/1159948
|
||||
self._execute('tgt-admin',
|
||||
'--force',
|
||||
'--delete',
|
||||
iqn,
|
||||
run_as_root=True)
|
||||
except exception.ProcessExecutionError, e:
|
||||
LOG.error(_("Failed to remove iscsi target for volume "
|
||||
"id:%(vol_id)s.") % locals())
|
||||
raise exception.ISCSITargetRemoveFailed(volume_id=vol_id)
|
||||
|
||||
os.unlink(volume_path)
|
||||
|
||||
def show_target(self, tid, iqn=None, **kwargs):
|
||||
if iqn is None:
|
||||
raise exception.InvalidParameterValue(
|
||||
err=_('valid iqn needed for show_target'))
|
||||
|
||||
tid = self._get_target(iqn)
|
||||
if tid is None:
|
||||
raise exception.NotFound()
|
||||
|
||||
|
||||
class IetAdm(TargetAdmin):
|
||||
"""iSCSI target administration using ietadm."""
|
||||
|
||||
def __init__(self, execute=utils.execute):
|
||||
super(IetAdm, self).__init__('ietadm', execute)
|
||||
|
||||
def _iotype(self, path):
|
||||
if FLAGS.iscsi_iotype == 'auto':
|
||||
return 'blockio' if volume_utils.is_block(path) else 'fileio'
|
||||
else:
|
||||
return FLAGS.iscsi_iotype
|
||||
|
||||
def create_iscsi_target(self, name, tid, lun, path,
|
||||
chap_auth=None, **kwargs):
|
||||
|
||||
# NOTE (jdg): Address bug: 1175207
|
||||
kwargs.pop('old_name', None)
|
||||
|
||||
self._new_target(name, tid, **kwargs)
|
||||
self._new_logicalunit(tid, lun, path, **kwargs)
|
||||
if chap_auth is not None:
|
||||
(type, username, password) = chap_auth.split()
|
||||
self._new_auth(tid, type, username, password, **kwargs)
|
||||
|
||||
conf_file = FLAGS.iet_conf
|
||||
if os.path.exists(conf_file):
|
||||
try:
|
||||
volume_conf = """
|
||||
Target %s
|
||||
%s
|
||||
Lun 0 Path=%s,Type=%s
|
||||
""" % (name, chap_auth, path, self._iotype(path))
|
||||
|
||||
with utils.temporary_chown(conf_file):
|
||||
f = open(conf_file, 'a+')
|
||||
f.write(volume_conf)
|
||||
f.close()
|
||||
except exception.ProcessExecutionError, e:
|
||||
vol_id = name.split(':')[1]
|
||||
LOG.error(_("Failed to create iscsi target for volume "
|
||||
"id:%(vol_id)s.") % locals())
|
||||
raise exception.ISCSITargetCreateFailed(volume_id=vol_id)
|
||||
return tid
|
||||
|
||||
def remove_iscsi_target(self, tid, lun, vol_id, **kwargs):
|
||||
LOG.info(_('Removing iscsi_target for volume: %s') % vol_id)
|
||||
self._delete_logicalunit(tid, lun, **kwargs)
|
||||
self._delete_target(tid, **kwargs)
|
||||
vol_uuid_file = FLAGS.volume_name_template % vol_id
|
||||
conf_file = FLAGS.iet_conf
|
||||
if os.path.exists(conf_file):
|
||||
with utils.temporary_chown(conf_file):
|
||||
try:
|
||||
iet_conf_text = open(conf_file, 'r+')
|
||||
full_txt = iet_conf_text.readlines()
|
||||
new_iet_conf_txt = []
|
||||
count = 0
|
||||
for line in full_txt:
|
||||
if count > 0:
|
||||
count -= 1
|
||||
continue
|
||||
elif re.search(vol_uuid_file, line):
|
||||
count = 2
|
||||
continue
|
||||
else:
|
||||
new_iet_conf_txt.append(line)
|
||||
|
||||
iet_conf_text.seek(0)
|
||||
iet_conf_text.truncate(0)
|
||||
iet_conf_text.writelines(new_iet_conf_txt)
|
||||
finally:
|
||||
iet_conf_text.close()
|
||||
|
||||
def _new_target(self, name, tid, **kwargs):
|
||||
self._run('--op', 'new',
|
||||
'--tid=%s' % tid,
|
||||
'--params', 'Name=%s' % name,
|
||||
**kwargs)
|
||||
|
||||
def _delete_target(self, tid, **kwargs):
|
||||
self._run('--op', 'delete',
|
||||
'--tid=%s' % tid,
|
||||
**kwargs)
|
||||
|
||||
def show_target(self, tid, iqn=None, **kwargs):
|
||||
self._run('--op', 'show',
|
||||
'--tid=%s' % tid,
|
||||
**kwargs)
|
||||
|
||||
def _new_logicalunit(self, tid, lun, path, **kwargs):
|
||||
self._run('--op', 'new',
|
||||
'--tid=%s' % tid,
|
||||
'--lun=%d' % lun,
|
||||
'--params', 'Path=%s,Type=%s' % (path, self._iotype(path)),
|
||||
**kwargs)
|
||||
|
||||
def _delete_logicalunit(self, tid, lun, **kwargs):
|
||||
self._run('--op', 'delete',
|
||||
'--tid=%s' % tid,
|
||||
'--lun=%d' % lun,
|
||||
**kwargs)
|
||||
|
||||
def _new_auth(self, tid, type, username, password, **kwargs):
|
||||
self._run('--op', 'new',
|
||||
'--tid=%s' % tid,
|
||||
'--user',
|
||||
'--params=%s=%s,Password=%s' % (type, username, password),
|
||||
**kwargs)
|
||||
|
||||
|
||||
class FakeIscsiHelper(object):
|
||||
|
||||
def __init__(self):
|
||||
self.tid = 1
|
||||
|
||||
def set_execute(self, execute):
|
||||
self._execute = execute
|
||||
|
||||
def create_iscsi_target(self, *args, **kwargs):
|
||||
self.tid += 1
|
||||
return self.tid
|
||||
|
||||
|
||||
class LioAdm(TargetAdmin):
|
||||
"""iSCSI target administration for LIO using python-rtslib."""
|
||||
def __init__(self, execute=utils.execute):
|
||||
super(LioAdm, self).__init__('rtstool', execute)
|
||||
|
||||
try:
|
||||
self._execute('rtstool', 'verify')
|
||||
except (OSError, exception.ProcessExecutionError):
|
||||
LOG.error(_('rtstool is not installed correctly'))
|
||||
raise
|
||||
|
||||
def _get_target(self, iqn):
|
||||
(out, err) = self._execute('rtstool',
|
||||
'get-targets',
|
||||
run_as_root=True)
|
||||
lines = out.split('\n')
|
||||
for line in lines:
|
||||
if iqn in line:
|
||||
return line
|
||||
|
||||
return None
|
||||
|
||||
def create_iscsi_target(self, name, tid, lun, path,
|
||||
chap_auth=None, **kwargs):
|
||||
# tid and lun are not used
|
||||
|
||||
vol_id = name.split(':')[1]
|
||||
|
||||
LOG.info(_('Creating iscsi_target for volume: %s') % vol_id)
|
||||
|
||||
# rtstool requires chap_auth, but unit tests don't provide it
|
||||
chap_auth_userid = 'test_id'
|
||||
chap_auth_password = 'test_pass'
|
||||
|
||||
if chap_auth != None:
|
||||
(chap_auth_userid, chap_auth_password) = chap_auth.split(' ')[1:]
|
||||
|
||||
extra_args = []
|
||||
if FLAGS.lio_initiator_iqns:
|
||||
extra_args.append(FLAGS.lio_initiator_iqns)
|
||||
|
||||
try:
|
||||
command_args = ['rtstool',
|
||||
'create',
|
||||
path,
|
||||
name,
|
||||
chap_auth_userid,
|
||||
chap_auth_password]
|
||||
if extra_args != []:
|
||||
command_args += extra_args
|
||||
self._execute(*command_args, run_as_root=True)
|
||||
except exception.ProcessExecutionError as e:
|
||||
LOG.error(_("Failed to create iscsi target for volume "
|
||||
"id:%(vol_id)s.") % locals())
|
||||
LOG.error("%s" % str(e))
|
||||
|
||||
raise exception.ISCSITargetCreateFailed(volume_id=vol_id)
|
||||
|
||||
iqn = '%s%s' % (FLAGS.iscsi_target_prefix, vol_id)
|
||||
tid = self._get_target(iqn)
|
||||
if tid is None:
|
||||
LOG.error(_("Failed to create iscsi target for volume "
|
||||
"id:%(vol_id)s.") % locals())
|
||||
raise exception.NotFound()
|
||||
|
||||
return tid
|
||||
|
||||
def remove_iscsi_target(self, tid, lun, vol_id, **kwargs):
|
||||
LOG.info(_('Removing iscsi_target: %s') % vol_id)
|
||||
vol_uuid_name = 'volume-%s' % vol_id
|
||||
iqn = '%s%s' % (FLAGS.iscsi_target_prefix, vol_uuid_name)
|
||||
|
||||
try:
|
||||
self._execute('rtstool',
|
||||
'delete',
|
||||
iqn,
|
||||
run_as_root=True)
|
||||
except exception.ProcessExecutionError as e:
|
||||
LOG.error(_("Failed to remove iscsi target for volume "
|
||||
"id:%(vol_id)s.") % locals())
|
||||
LOG.error("%s" % str(e))
|
||||
raise exception.ISCSITargetRemoveFailed(volume_id=vol_id)
|
||||
|
||||
def show_target(self, tid, iqn=None, **kwargs):
|
||||
if iqn is None:
|
||||
raise exception.InvalidParameterValue(
|
||||
err=_('valid iqn needed for show_target'))
|
||||
|
||||
tid = self._get_target(iqn)
|
||||
if tid is None:
|
||||
raise exception.NotFound()
|
||||
|
||||
def initialize_connection(self, volume, connector):
|
||||
volume_iqn = volume['provider_location'].split(' ')[1]
|
||||
|
||||
(auth_method, auth_user, auth_pass) = \
|
||||
volume['provider_auth'].split(' ', 3)
|
||||
|
||||
# Add initiator iqns to target ACL
|
||||
try:
|
||||
self._execute('rtstool', 'add-initiator',
|
||||
volume_iqn,
|
||||
auth_user,
|
||||
auth_pass,
|
||||
connector['initiator'],
|
||||
run_as_root=True)
|
||||
except exception.ProcessExecutionError as e:
|
||||
LOG.error(_("Failed to add initiator iqn %s to target") %
|
||||
connector['initiator'])
|
||||
raise exception.ISCSITargetAttachFailed(volume_id=volume['id'])
|
||||
|
||||
|
||||
def get_target_admin():
|
||||
if FLAGS.iscsi_helper == 'tgtadm':
|
||||
return TgtAdm()
|
||||
elif FLAGS.iscsi_helper == 'fake':
|
||||
return FakeIscsiHelper()
|
||||
elif FLAGS.iscsi_helper == 'lioadm':
|
||||
return LioAdm()
|
||||
else:
|
||||
return IetAdm()
|
@ -1,16 +0,0 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2013 OpenStack Foundation.
|
||||
# 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.
|
@ -1,368 +0,0 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2013 OpenStack Foundation.
|
||||
# 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.
|
||||
|
||||
"""
|
||||
LVM class for performing LVM operations.
|
||||
"""
|
||||
|
||||
import math
|
||||
|
||||
from itertools import izip
|
||||
|
||||
from manila.openstack.common.gettextutils import _
|
||||
from manila.openstack.common import log as logging
|
||||
from manila.openstack.common import processutils as putils
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class VolumeGroupNotFound(Exception):
|
||||
def __init__(self, vg_name):
|
||||
message = (_('Unable to find Volume Group: %s') % vg_name)
|
||||
super(VolumeGroupNotFound, self).__init__(message)
|
||||
|
||||
|
||||
class VolumeGroupCreationFailed(Exception):
|
||||
def __init__(self, vg_name):
|
||||
message = (_('Failed to create Volume Group: %s') % vg_name)
|
||||
super(VolumeGroupCreationFailed, self).__init__(message)
|
||||
|
||||
|
||||
class LVM(object):
|
||||
"""LVM object to enable various LVM related operations."""
|
||||
|
||||
def __init__(self, vg_name, create_vg=False,
|
||||
physical_volumes=None):
|
||||
"""Initialize the LVM object.
|
||||
|
||||
The LVM object is based on an LVM VolumeGroup, one instantiation
|
||||
for each VolumeGroup you have/use.
|
||||
|
||||
:param vg_name: Name of existing VG or VG to create
|
||||
:param create_vg: Indicates the VG doesn't exist
|
||||
and we want to create it
|
||||
:param physical_volumes: List of PVs to build VG on
|
||||
|
||||
"""
|
||||
self.vg_name = vg_name
|
||||
self.pv_list = []
|
||||
self.lv_list = []
|
||||
self.vg_size = 0
|
||||
self.vg_available_space = 0
|
||||
self.vg_lv_count = 0
|
||||
self.vg_uuid = None
|
||||
|
||||
if create_vg and physical_volumes is not None:
|
||||
self.pv_list = physical_volumes
|
||||
|
||||
try:
|
||||
self._create_vg(physical_volumes)
|
||||
except putils.ProcessExecutionError as err:
|
||||
LOG.exception(_('Error creating Volume Group'))
|
||||
LOG.error(_('Cmd :%s') % err.cmd)
|
||||
LOG.error(_('StdOut :%s') % err.stdout)
|
||||
LOG.error(_('StdErr :%s') % err.stderr)
|
||||
raise VolumeGroupCreationFailed(vg_name=self.vg_name)
|
||||
|
||||
if self._vg_exists() is False:
|
||||
LOG.error(_('Unable to locate Volume Group %s') % vg_name)
|
||||
raise VolumeGroupNotFound(vg_name=vg_name)
|
||||
|
||||
def _size_str(self, size_in_g):
|
||||
if '.00' in size_in_g:
|
||||
size_in_g = size_in_g.replace('.00', '')
|
||||
|
||||
if int(size_in_g) == 0:
|
||||
return '100M'
|
||||
|
||||
return '%sG' % size_in_g
|
||||
|
||||
def _vg_exists(self):
|
||||
"""Simple check to see if VG exists.
|
||||
|
||||
:returns: True if vg specified in object exists, else False
|
||||
|
||||
"""
|
||||
exists = False
|
||||
cmd = ['vgs', '--noheadings', '-o', 'name']
|
||||
(out, err) = putils.execute(*cmd, root_helper='sudo', run_as_root=True)
|
||||
|
||||
if out is not None:
|
||||
volume_groups = out.split()
|
||||
if self.vg_name in volume_groups:
|
||||
exists = True
|
||||
|
||||
return exists
|
||||
|
||||
def _create_vg(self, pv_list):
|
||||
cmd = ['vgcreate', self.vg_name, ','.join(pv_list)]
|
||||
putils.execute(*cmd, root_helper='sudo', run_as_root=True)
|
||||
|
||||
def _get_vg_uuid(self):
|
||||
(out, err) = putils.execute('vgs', '--noheadings',
|
||||
'-o uuid', self.vg_name)
|
||||
if out is not None:
|
||||
return out.split()
|
||||
else:
|
||||
return []
|
||||
|
||||
@staticmethod
|
||||
def supports_thin_provisioning():
|
||||
"""Static method to check for thin LVM support on a system.
|
||||
|
||||
:returns: True if supported, False otherwise
|
||||
|
||||
"""
|
||||
cmd = ['vgs', '--version']
|
||||
(out, err) = putils.execute(*cmd, root_helper='sudo', run_as_root=True)
|
||||
lines = out.split('\n')
|
||||
|
||||
for line in lines:
|
||||
if 'LVM version' in line:
|
||||
version_list = line.split()
|
||||
version = version_list[2]
|
||||
if '(2)' in version:
|
||||
version = version.replace('(2)', '')
|
||||
version_tuple = tuple(map(int, version.split('.')))
|
||||
if version_tuple >= (2, 2, 95):
|
||||
return True
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def get_all_volumes(vg_name=None):
|
||||
"""Static method to get all LV's on a system.
|
||||
|
||||
:param vg_name: optional, gathers info for only the specified VG
|
||||
:returns: List of Dictionaries with LV info
|
||||
|
||||
"""
|
||||
cmd = ['lvs', '--noheadings', '-o', 'vg_name,name,size']
|
||||
if vg_name is not None:
|
||||
cmd += [vg_name]
|
||||
|
||||
(out, err) = putils.execute(*cmd, root_helper='sudo', run_as_root=True)
|
||||
|
||||
lv_list = []
|
||||
if out is not None:
|
||||
volumes = out.split()
|
||||
for vg, name, size in izip(*[iter(volumes)] * 3):
|
||||
lv_list.append({"vg": vg, "name": name, "size": size})
|
||||
|
||||
return lv_list
|
||||
|
||||
def get_volumes(self):
|
||||
"""Get all LV's associated with this instantiation (VG).
|
||||
|
||||
:returns: List of Dictionaries with LV info
|
||||
|
||||
"""
|
||||
self.lv_list = self.get_all_volumes(self.vg_name)
|
||||
return self.lv_list
|
||||
|
||||
def get_volume(self, name):
|
||||
"""Get reference object of volume specified by name.
|
||||
|
||||
:returns: dict representation of Logical Volume if exists
|
||||
|
||||
"""
|
||||
ref_list = self.get_volumes()
|
||||
for r in ref_list:
|
||||
if r['name'] == name:
|
||||
return r
|
||||
|
||||
@staticmethod
|
||||
def get_all_physical_volumes(vg_name=None):
|
||||
"""Static method to get all PVs on a system.
|
||||
|
||||
:param vg_name: optional, gathers info for only the specified VG
|
||||
:returns: List of Dictionaries with PV info
|
||||
|
||||
"""
|
||||
cmd = ['pvs', '--noheadings',
|
||||
'-o', 'vg_name,name,size,free',
|
||||
'--separator', ':']
|
||||
if vg_name is not None:
|
||||
cmd += [vg_name]
|
||||
|
||||
(out, err) = putils.execute(*cmd, root_helper='sudo', run_as_root=True)
|
||||
|
||||
pv_list = []
|
||||
if out is not None:
|
||||
pvs = out.split()
|
||||
for pv in pvs:
|
||||
fields = pv.split(':')
|
||||
pv_list.append({'vg': fields[0],
|
||||
'name': fields[1],
|
||||
'size': fields[2],
|
||||
'available': fields[3]})
|
||||
|
||||
return pv_list
|
||||
|
||||
def get_physical_volumes(self):
|
||||
"""Get all PVs associated with this instantiation (VG).
|
||||
|
||||
:returns: List of Dictionaries with PV info
|
||||
|
||||
"""
|
||||
self.pv_list = self.get_all_physical_volumes(self.vg_name)
|
||||
return self.pv_list
|
||||
|
||||
@staticmethod
|
||||
def get_all_volume_groups(vg_name=None):
|
||||
"""Static method to get all VGs on a system.
|
||||
|
||||
:param vg_name: optional, gathers info for only the specified VG
|
||||
:returns: List of Dictionaries with VG info
|
||||
|
||||
"""
|
||||
cmd = ['vgs', '--noheadings',
|
||||
'-o', 'name,size,free,lv_count,uuid',
|
||||
'--separator', ':']
|
||||
if vg_name is not None:
|
||||
cmd += [vg_name]
|
||||
|
||||
(out, err) = putils.execute(*cmd, root_helper='sudo', run_as_root=True)
|
||||
|
||||
vg_list = []
|
||||
if out is not None:
|
||||
vgs = out.split()
|
||||
for vg in vgs:
|
||||
fields = vg.split(':')
|
||||
vg_list.append({'name': fields[0],
|
||||
'size': fields[1],
|
||||
'available': fields[2],
|
||||
'lv_count': fields[3],
|
||||
'uuid': fields[4]})
|
||||
|
||||
return vg_list
|
||||
|
||||
def update_volume_group_info(self):
|
||||
"""Update VG info for this instantiation.
|
||||
|
||||
Used to update member fields of object and
|
||||
provide a dict of info for caller.
|
||||
|
||||
:returns: Dictionaries of VG info
|
||||
|
||||
"""
|
||||
vg_list = self.get_all_volume_groups(self.vg_name)
|
||||
|
||||
if len(vg_list) != 1:
|
||||
LOG.error(_('Unable to find VG: %s') % self.vg_name)
|
||||
raise VolumeGroupNotFound(vg_name=self.vg_name)
|
||||
|
||||
self.vg_size = vg_list[0]['size']
|
||||
self.vg_available_space = vg_list[0]['available']
|
||||
self.vg_lv_count = vg_list[0]['lv_count']
|
||||
self.vg_uuid = vg_list[0]['uuid']
|
||||
|
||||
return vg_list[0]
|
||||
|
||||
def create_thin_pool(self, name=None, size_str=0):
|
||||
"""Creates a thin provisioning pool for this VG.
|
||||
|
||||
:param name: Name to use for pool, default is "<vg-name>-pool"
|
||||
:param size_str: Size to allocate for pool, default is entire VG
|
||||
|
||||
"""
|
||||
|
||||
if not self.supports_thin_provisioning():
|
||||
LOG.error(_('Requested to setup thin provisioning, '
|
||||
'however current LVM version does not '
|
||||
'support it.'))
|
||||
return None
|
||||
|
||||
if name is None:
|
||||
name = '%s-pool' % self.vg_name
|
||||
|
||||
if size_str == 0:
|
||||
self.update_volume_group_info()
|
||||
size_str = self.vg_size
|
||||
|
||||
self.create_volume(name, size_str, 'thin')
|
||||
|
||||
def create_volume(self, name, size_str, lv_type='default', mirror_count=0):
|
||||
"""Creates a logical volume on the object's VG.
|
||||
|
||||
:param name: Name to use when creating Logical Volume
|
||||
:param size_str: Size to use when creating Logical Volume
|
||||
:param lv_type: Type of Volume (default or thin)
|
||||
:param mirror_count: Use LVM mirroring with specified count
|
||||
|
||||
"""
|
||||
size = self._size_str(size_str)
|
||||
cmd = ['lvcreate', '-n', name, self.vg_name]
|
||||
if lv_type == 'thin':
|
||||
cmd += ['-T', '-V', size]
|
||||
else:
|
||||
cmd += ['-L', size]
|
||||
|
||||
if mirror_count > 0:
|
||||
cmd += ['-m', mirror_count, '--nosync']
|
||||
terras = int(size[:-1]) / 1024.0
|
||||
if terras >= 1.5:
|
||||
rsize = int(2 ** math.ceil(math.log(terras) / math.log(2)))
|
||||
# NOTE(vish): Next power of two for region size. See:
|
||||
# http://red.ht/U2BPOD
|
||||
cmd += ['-R', str(rsize)]
|
||||
|
||||
putils.execute(*cmd,
|
||||
root_helper='sudo',
|
||||
run_as_root=True)
|
||||
|
||||
def create_lv_snapshot(self, name, source_lv_name, lv_type='default'):
|
||||
"""Creates a snapshot of a logical volume.
|
||||
|
||||
:param name: Name to assign to new snapshot
|
||||
:param source_lv_name: Name of Logical Volume to snapshot
|
||||
:param lv_type: Type of LV (default or thin)
|
||||
|
||||
"""
|
||||
source_lvref = self.get_volume(source_lv_name)
|
||||
if source_lvref is None:
|
||||
LOG.error(_("Unable to find LV: %s") % source_lv_name)
|
||||
return False
|
||||
cmd = ['lvcreate', '--name', name,
|
||||
'--snapshot', '%s/%s' % (self.vg_name, source_lv_name)]
|
||||
if lv_type != 'thin':
|
||||
size = source_lvref['size']
|
||||
cmd += ['-L', size]
|
||||
|
||||
putils.execute(*cmd,
|
||||
root_helper='sudo',
|
||||
run_as_root=True)
|
||||
|
||||
def delete(self, name):
|
||||
"""Delete logical volume or snapshot.
|
||||
|
||||
:param name: Name of LV to delete
|
||||
|
||||
"""
|
||||
putils.execute('lvremove',
|
||||
'-f',
|
||||
'%s/%s' % (self.vg_name, name),
|
||||
root_helper='sudo', run_as_root=True)
|
||||
|
||||
def revert(self, snapshot_name):
|
||||
"""Revert an LV from snapshot.
|
||||
|
||||
:param snapshot_name: Name of snapshot to revert
|
||||
|
||||
"""
|
||||
putils.execute('lvconvert', '--merge',
|
||||
snapshot_name, root_helper='sudo',
|
||||
run_as_root=True)
|
Loading…
x
Reference in New Issue
Block a user