def _fake_add_urls(self):
def check(cmd_string):
- return '> /sys/class/srb/add_urls' in cmd_string
+ return 'tee, /sys/class/srb/add_urls' in cmd_string
def act(cmd):
- self._urls.append(cmd[2].split()[1])
+ self._urls.append(cmd[2])
return check, act
def _fake_create(self):
def check(cmd_string):
- return '> /sys/class/srb/create' in cmd_string
+ return 'tee, /sys/class/srb/create' in cmd_string
def act(cmd):
- volname = cmd[2].split()[1]
- volsize = cmd[2].split()[2]
+ volname = cmd[2].split()[0]
+ volsize = cmd[2].split()[1]
self._volumes[volname] = {
"name": volname,
"size": self._convert_size(volsize),
def _fake_destroy(self):
def check(cmd_string):
- return '> /sys/class/srb/destroy' in cmd_string
+ return 'tee, /sys/class/srb/destroy' in cmd_string
def act(cmd):
- volname = cmd[2].split()[1]
+ volname = cmd[2]
del self._volumes[volname]
return check, act
def _fake_extend(self):
def check(cmd_string):
- return '> /sys/class/srb/extend' in cmd_string
+ return 'tee, /sys/class/srb/extend' in cmd_string
def act(cmd):
- volname = cmd[2].split()[1]
- volsize = cmd[2].split()[2]
+ volname = cmd[2].split()[0]
+ volsize = cmd[2].split()[1]
self._volumes[volname]["size"] = self._convert_size(volsize)
return check, act
def _fake_attach(self):
def check(cmd_string):
- return '> /sys/class/srb/attach' in cmd_string
+ return 'tee, /sys/class/srb/attach' in cmd_string
def act(_):
pass
def _fake_detach(self):
def check(cmd_string):
- return '> /sys/class/srb/detach' in cmd_string
+ return 'tee, /sys/class/srb/detach' in cmd_string
def act(_):
pass
return check, act
def _fake_execute(self, *cmd, **kwargs):
+ # Initial version of this driver used to perform commands this way :
+ # sh echo $cmd > /sys/class/srb
+ # As noted in LP #1414531 this is wrong, it should be
+ # tee /sys/class/srb < $cmd
+ # To avoid having to rewrite every unit tests, we insert the STDIN
+ # as part of the original command
+ if 'process_input' in kwargs:
+ cmd = cmd + (kwargs['process_input'],)
cmd_string = ', '.join(cmd)
##
# To test behavior, we need to stub part of the brick/local_dev/lvm
self.fail('Unexpected command: %s' % cmd_string)
def _configure_driver(self):
- srb.CONF.srb_base_urls = "http://localhost/volumes"
+ srb.CONF.srb_base_urls = "http://127.0.0.1/volumes"
def setUp(self):
super(SRBDriverTestCase, self).setUp()
def test_setup(self):
"""The url shall be added automatically"""
self._driver.do_setup(None)
- self.assertEqual('http://localhost/volumes', self._urls[0])
+ self.assertEqual('http://127.0.0.1/volumes',
+ self._urls[0])
self._driver.check_for_setup_error()
+ @mock.patch.object(srb.CONF, 'srb_base_urls', "http://; evil")
+ def test_setup_malformated_url(self):
+ self.assertRaises(exception.VolumeBackendAPIException,
+ self._driver.do_setup, None)
+
def test_setup_no_config(self):
"""The driver shall not start without any url configured"""
srb.CONF.srb_base_urls = None
import contextlib
import functools
+import re
import sys
import time
srb_opts = [
cfg.StrOpt('srb_base_urls',
default=None,
- help='Comma-separated list of REST servers to connect to'),
+ help='Comma-separated list of REST servers IP to connect to. '
+ '(eg http://IP1/,http://IP2:81/path'),
]
CONF = cfg.CONF
CONF.register_opts(srb_opts)
+ACCEPTED_REST_SERVER = re.compile(r'^http://'
+ '(\d{1,3}\.){3}\d{1,3}'
+ '(:\d+)?/[a-zA-Z0-9\-_\/]*$')
+
class retry:
SLEEP_NONE = 'none'
CDMI).
"""
- VERSION = '1.0.0'
+ VERSION = '1.1.0'
# Over-allocation ratio (multiplied with requested size) for thin
# provisioning
self.urls_setup = False
self.backend_name = None
self.base_urls = None
+ self.root_helper = utils.get_root_helper()
self._attached_devices = {}
def _setup_urls(self):
message=_LE('Cound not setup urls on the Block Driver.'),
info_message=_LI('Error creating Volume'),
reraise=False):
- cmd = 'echo ' + self.base_urls + ' > /sys/class/srb/add_urls'
- putils.execute('sh', '-c', cmd,
- root_helper='sudo', run_as_root=True)
+ cmd = self.base_urls
+ path = '/sys/class/srb/add_urls'
+ putils.execute('tee', path, process_input=cmd,
+ root_helper=self.root_helper, run_as_root=True)
self.urls_setup = True
def do_setup(self, context):
self.backend_name = self.configuration.safe_get('volume_backend_name')
base_urls = self.configuration.safe_get('srb_base_urls')
+ sane_urls = []
if base_urls:
- base_urls = ','.join(s.strip() for s in base_urls.split(','))
-
- self.base_urls = base_urls
-
+ for url in base_urls.split(','):
+ stripped_url = url.strip()
+ if ACCEPTED_REST_SERVER.match(stripped_url):
+ sane_urls.append(stripped_url)
+ else:
+ LOG.warning(_LW("%s is not an accepted REST server "
+ "IP address"), stripped_url)
+
+ self.base_urls = ','.join(sane_urls)
self._setup_urls()
def check_for_setup_error(self):
reraise=exception.VolumeBackendAPIException(data=message)):
size = self._size_int(volume['size']) * self.OVER_ALLOC_RATIO
- cmd = 'echo ' + volume['name'] + ' '
- cmd += '%dG' % size
- cmd += ' > /sys/class/srb/create'
- putils.execute('/bin/sh', '-c', cmd,
- root_helper='sudo', run_as_root=True)
+ cmd = volume['name']
+ cmd += ' %dG' % size
+ path = '/sys/class/srb/create'
+ putils.execute('tee', path, process_input=cmd,
+ root_helper=self.root_helper, run_as_root=True)
return self._set_device_path(volume)
reraise=exception.VolumeBackendAPIException(data=message)):
size = self._size_int(new_size) * self.OVER_ALLOC_RATIO
- cmd = 'echo ' + volume['name'] + ' '
- cmd += '%dG' % size
- cmd += ' > /sys/class/srb/extend'
- putils.execute('/bin/sh', '-c', cmd,
- root_helper='sudo', run_as_root=True)
+ cmd = volume['name']
+ cmd += ' %dG' % size
+ path = '/sys/class/srb/extend'
+ putils.execute('tee', path, process_input=cmd,
+ root_helper=self.root_helper, run_as_root=True)
@staticmethod
def _destroy_file(volume):
message=message,
info_message=_LI('Error destroying Volume %s.') % volname,
reraise=exception.VolumeBackendAPIException(data=message)):
- cmd = 'echo ' + volume['name'] + ' > /sys/class/srb/destroy'
- putils.execute('/bin/sh', '-c', cmd,
- root_helper='sudo', run_as_root=True)
+ cmd = volume['name']
+ path = '/sys/class/srb/destroy'
+ putils.execute('tee', path, process_input=cmd,
+ root_helper=utils.get_root_helper(),
+ run_as_root=True)
# NOTE(joachim): Must only be called within a function decorated by:
# @lockutils.synchronized('devices', 'cinder-srb-')
message=message,
info_message=_LI('Error attaching Volume'),
reraise=exception.VolumeBackendAPIException(data=message)):
- cmd = 'echo ' + name + ' ' + devname
- cmd += ' > /sys/class/srb/attach'
- putils.execute('/bin/sh', '-c', cmd,
- root_helper='sudo', run_as_root=True)
+ cmd = name + ' ' + devname
+ path = '/sys/class/srb/attach'
+ putils.execute('tee', path, process_input=cmd,
+ root_helper=self.root_helper, run_as_root=True)
else:
LOG.debug('Volume %s already attached', name)
def _do_detach(self, volume, vg):
devname = self._device_name(volume)
volname = self._get_volname(volume)
- cmd = 'echo ' + devname + ' > /sys/class/srb/detach'
+ cmd = devname
+ path = '/sys/class/srb/detach'
try:
- putils.execute('/bin/sh', '-c', cmd,
- root_helper='sudo', run_as_root=True)
+ putils.execute('tee', path, process_input=cmd,
+ root_helper=self.root_helper, run_as_root=True)
except putils.ProcessExecutionError:
with excutils.save_and_reraise_exception(reraise=True):
try:
raise exception.VolumeIsBusy(volume_name=volume['name'])
vg.destroy_vg()
# NOTE(joachim) Force lvm vg flush through a vgs command
- vgs = vg.get_all_volume_groups(root_helper='sudo', vg_name=vg.vg_name)
+ vgs = vg.get_all_volume_groups(root_helper=self.root_helper,
+ vg_name=vg.vg_name)
if len(vgs) != 0:
LOG.warning(_LW('Removed volume group %s still appears in vgs.'),
vg.vg_name)