diff options
author | Vishvananda Ishaya <vishvananda@yahoo.com> | 2010-08-27 23:10:57 -0700 |
---|---|---|
committer | Vishvananda Ishaya <vishvananda@yahoo.com> | 2010-08-27 23:10:57 -0700 |
commit | 8d0f96432b7b07fa608cae433459645880f4a44c (patch) | |
tree | e35990a7ab6a41dd8cc6bebd52174346e3b7ddf2 /nova | |
parent | ff72e7baff179bb814e3b9df9fc50659a48249f3 (diff) | |
download | nova-8d0f96432b7b07fa608cae433459645880f4a44c.tar.gz nova-8d0f96432b7b07fa608cae433459645880f4a44c.tar.xz nova-8d0f96432b7b07fa608cae433459645880f4a44c.zip |
split volume into service/manager/driver
Diffstat (limited to 'nova')
-rw-r--r-- | nova/db/api.py | 25 | ||||
-rw-r--r-- | nova/db/sqlalchemy/api.py | 32 | ||||
-rw-r--r-- | nova/endpoint/cloud.py | 10 | ||||
-rw-r--r-- | nova/manager.py | 38 | ||||
-rw-r--r-- | nova/service.py | 32 | ||||
-rw-r--r-- | nova/utils.py | 23 | ||||
-rw-r--r-- | nova/volume/driver.py | 120 | ||||
-rw-r--r-- | nova/volume/manager.py | 122 | ||||
-rw-r--r-- | nova/volume/service.py | 155 |
9 files changed, 359 insertions, 198 deletions
diff --git a/nova/db/api.py b/nova/db/api.py index 536ef1e25..5e04ee998 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -41,13 +41,6 @@ flags.DEFINE_string('public_range', '4.4.4.0/24', 'Public IP address block') flags.DEFINE_string('private_range', '10.0.0.0/8', 'Private IP address block') flags.DEFINE_integer('cnt_vpn_clients', 5, 'Number of addresses reserved for vpn clients') -flags.DEFINE_integer('num_shelves', - 100, - 'Number of vblade shelves') -flags.DEFINE_integer('blades_per_shelf', - 16, - 'Number of vblade blades per shelf') - _impl = utils.LazyPluggable(FLAGS['db_backend'], @@ -376,6 +369,19 @@ def queue_get_for(context, topic, physical_node_id): ################### +def export_device_count(context): + """Return count of export devices.""" + return _impl.export_device_count(context) + + +def export_device_create(context, values): + """Create an export_device from the values dictionary.""" + return _impl.export_device_create(context, values) + + +################### + + def volume_allocate_shelf_and_blade(context, volume_id): """Atomically allocate a free shelf and blade from the pool.""" return _impl.volume_allocate_shelf_and_blade(context, volume_id) @@ -391,11 +397,6 @@ def volume_create(context, values): return _impl.volume_create(context, values) -def volume_ensure_blades(context, num_shelves, blades_per_shelf): - """Ensure shelves and blades have been created in the datastore.""" - return _impl.volume_ensure_blades(context, num_shelves, blades_per_shelf) - - def volume_destroy(context, volume_id): """Destroy the volume or raise if it does not exist.""" return _impl.volume_destroy(context, volume_id) diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index cba85ccb7..1e688495a 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -446,10 +446,22 @@ def queue_get_for(context, topic, physical_node_id): ################### +def export_device_count(context): + return models.ExportDevice.count() + + +def export_device_create(context, values): + export_device_ref = models.ExportDevice() + for (key, value) in values.iteritems(): + export_device_ref[key] = value + export_device_ref.save() + return export_device_ref + + +################### + + def volume_allocate_shelf_and_blade(context, volume_id): - db.volume_ensure_blades(context, - FLAGS.num_shelves, - FLAGS.blades_per_shelf) session = models.NovaBase.get_session() query = session.query(models.ExportDevice).filter_by(volume=None) export_device = query.with_lockmode("update").first() @@ -477,7 +489,7 @@ def volume_create(context, values): for (key, value) in values.iteritems(): volume_ref[key] = value volume_ref.save() - return volume_ref.id + return volume_ref def volume_destroy(context, volume_id): @@ -494,18 +506,6 @@ def volume_detached(context, volume_id): volume_ref.save() -# NOTE(vish): should this code go up a layer? -def volume_ensure_blades(context, num_shelves, blades_per_shelf): - if models.ExportDevice.count() >= num_shelves * blades_per_shelf: - return - for shelf_id in xrange(num_shelves): - for blade_id in xrange(blades_per_shelf): - export_device = models.ExportDevice() - export_device.shelf_id = shelf_id - export_device.blade_id = blade_id - export_device.save() - - def volume_get(context, volume_id): return models.Volume.find(volume_id) diff --git a/nova/endpoint/cloud.py b/nova/endpoint/cloud.py index ffe3d3cc7..6d59c8225 100644 --- a/nova/endpoint/cloud.py +++ b/nova/endpoint/cloud.py @@ -271,7 +271,6 @@ class CloudController(object): return v @rbac.allow('projectmanager', 'sysadmin') - @defer.inlineCallbacks def create_volume(self, context, size, **kwargs): vol = {} vol['size'] = size @@ -280,13 +279,12 @@ class CloudController(object): vol['availability_zone'] = FLAGS.storage_availability_zone vol['status'] = "creating" vol['attach_status'] = "detached" - volume_id = db.volume_create(context, vol) + volume_ref = db.volume_create(context, vol) - yield rpc.cast(FLAGS.volume_topic, {"method": "create_volume", - "args": {"volume_id": volume_id}}) + rpc.cast(FLAGS.volume_topic, {"method": "create_volume", + "args": {"volume_id": volume_ref['id']}}) - volume = db.volume_get(context, volume_id) - defer.returnValue({'volumeSet': [self._format_volume(context, volume)]}) + return {'volumeSet': [self._format_volume(context, volume_ref)]} @rbac.allow('projectmanager', 'sysadmin') diff --git a/nova/manager.py b/nova/manager.py new file mode 100644 index 000000000..4f212a41b --- /dev/null +++ b/nova/manager.py @@ -0,0 +1,38 @@ +# 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. + +""" +Base class for managers of different parts of the system +""" + +from nova import utils +from nova import flags + + +FLAGS = flags.FLAGS +flags.DEFINE_string('db_driver', 'nova.db.api' + 'driver to use for volume creation') + + +class Manager(object): + """DB driver is injected in the init method""" + def __init__(self, db_driver=None): + if not db_driver: + db_driver=FLAGS.db_driver + self.db = utils.import_object(db_driver) + diff --git a/nova/service.py b/nova/service.py index 9c536c354..59da6f04e 100644 --- a/nova/service.py +++ b/nova/service.py @@ -32,6 +32,7 @@ from nova import db from nova import exception from nova import flags from nova import rpc +from nova import utils FLAGS = flags.FLAGS @@ -43,15 +44,29 @@ flags.DEFINE_integer('report_interval', 10, class Service(object, service.Service): """Base class for workers that run on hosts.""" + def __init__(self, manager, *args, **kwargs): + self.manager = manager + super(self, Service).__init__(*args, **kwargs) + + def __getattr__(self, key): + try: + super(Service, self).__getattr__(key) + except AttributeError: + self.manager.__getattr__(key) + @classmethod - def create(cls, report_interval=None, bin_name=None, topic=None): + def create(cls, + report_interval=None, + bin_name=None, + topic=None, + manager=None): """Instantiates class and passes back application object. Args: report_interval, defaults to flag bin_name, defaults to basename of executable topic, defaults to basename - "nova-" part - + manager, defaults to FLAGS.<topic>_manager """ if not report_interval: report_interval = FLAGS.report_interval @@ -61,21 +76,24 @@ class Service(object, service.Service): bin_name = os.path.basename(inspect.stack()[-1][1]) if not topic: topic = bin_name.rpartition("nova-")[2] + if not manager: + manager = FLAGS.get('%s_manager' % topic) + manager_ref = utils.import_object(manager) logging.warn("Starting %s node" % topic) - node_instance = cls() + service_ref = cls(manager_ref) conn = rpc.Connection.instance() consumer_all = rpc.AdapterConsumer( connection=conn, topic='%s' % topic, - proxy=node_instance) + proxy=service_ref) consumer_node = rpc.AdapterConsumer( connection=conn, topic='%s.%s' % (topic, FLAGS.node_name), - proxy=node_instance) + proxy=service_ref) - pulse = task.LoopingCall(node_instance.report_state, + pulse = task.LoopingCall(service_ref.report_state, FLAGS.node_name, bin_name) pulse.start(interval=report_interval, now=False) @@ -86,7 +104,7 @@ class Service(object, service.Service): # This is the parent service that twistd will be looking for when it # parses this file, return it so that we can get it into globals. application = service.Application(bin_name) - node_instance.setServiceParent(application) + service_ref.setServiceParent(application) return application @defer.inlineCallbacks diff --git a/nova/utils.py b/nova/utils.py index c4a8f17e9..392fa8c46 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -46,6 +46,13 @@ def import_class(import_str): except (ImportError, ValueError, AttributeError): raise exception.NotFound('Class %s cannot be found' % class_str) +def import_object(import_str): + """Returns an object including a module or module and class""" + cls = import_class(import_str) + try: + return cls() + except TypeError: + return cls def fetchfile(url, target): logging.debug("Fetching %s" % url) @@ -73,7 +80,7 @@ def execute(cmd, process_input=None, addl_env=None, check_exit_code=True): if obj.returncode: logging.debug("Result was %s" % (obj.returncode)) if check_exit_code and obj.returncode <> 0: - raise Exception( "Unexpected exit code: %s. result=%s" + raise Exception( "Unexpected exit code: %s. result=%s" % (obj.returncode, result)) return result @@ -105,7 +112,7 @@ def runthis(prompt, cmd, check_exit_code = True): exit_code = subprocess.call(cmd.split(" ")) logging.debug(prompt % (exit_code)) if check_exit_code and exit_code <> 0: - raise Exception( "Unexpected exit code: %s from cmd: %s" + raise Exception( "Unexpected exit code: %s from cmd: %s" % (exit_code, cmd)) @@ -150,21 +157,21 @@ def parse_isotime(timestr): return datetime.datetime.strptime(timestr, TIME_FORMAT) - + class LazyPluggable(object): """A pluggable backend loaded lazily based on some value.""" - + def __init__(self, pivot, **backends): self.__backends = backends self.__pivot = pivot self.__backend = None - + def __get_backend(self): if not self.__backend: backend_name = self.__pivot.value if backend_name not in self.__backends: raise exception.Error('Invalid backend: %s' % backend_name) - + backend = self.__backends[backend_name] if type(backend) == type(tuple()): name = backend[0] @@ -172,11 +179,11 @@ class LazyPluggable(object): else: name = backend fromlist = backend - + self.__backend = __import__(name, None, None, fromlist) logging.error('backend %s', self.__backend) return self.__backend - + def __getattr__(self, key): backend = self.__get_backend() return getattr(backend, key) diff --git a/nova/volume/driver.py b/nova/volume/driver.py new file mode 100644 index 000000000..579472047 --- /dev/null +++ b/nova/volume/driver.py @@ -0,0 +1,120 @@ +# 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. + +""" +Drivers for volumes +""" + +import logging + +from twisted.internet import defer + +from nova import flags +from nova import process +from nova import utils + + +FLAGS = flags.FLAGS +flags.DEFINE_string('storage_dev', '/dev/sdb', + 'Physical device to use for volumes') +flags.DEFINE_string('volume_group', 'nova-volumes', + 'Name for the VG that will contain exported volumes') +flags.DEFINE_string('aoe_eth_dev', 'eth0', + 'Which device to export the volumes on') + + +class FakeAOEDriver(object): + @defer.inlineCallbacks + def create_volume(self, volume_id, size): + logging.debug("Fake AOE: create_volume %s, %s", volume_id, size) + + @defer.inlineCallbacks + def delete_volume(self, volume_id): + logging.debug("Fake AOE: delete_volume %s", volume_id) + + @defer.inlineCallbacks + def create_export(self, volume_id, shelf_id, blade_id): + logging.debug("Fake AOE: create_export %s, %s, %s", + volume_id, shelf_id, blade_id) + + @defer.inlineCallbacks + def remove_export(self, volume_id, shelf_id, blade_id): + logging.debug("Fake AOE: remove_export %s, %s, %s", + volume_id, shelf_id, blade_id) + + @defer.inlineCallbacks + def ensure_exports(self): + logging.debug("Fake AOE: ensure_export") + + +class AOEDriver(object): + def __init__(self, *args, **kwargs): + super(AOEDriver, self).__init__(*args, **kwargs) + # NOTE(vish): no need for thise to be async, but it may be + # best to explicitly do them at some other time + utils.execute("sudo pvcreate %s" % (FLAGS.storage_dev)) + utils.execute("sudo vgcreate %s %s" % (FLAGS.volume_group, + FLAGS.storage_dev)) + @defer.inlineCallbacks + def create_volume(self, volume_id, size): + if int(size) == 0: + sizestr = '100M' + else: + sizestr = '%sG' % size + yield process.simple_execute( + "sudo lvcreate -L %s -n %s %s" % (sizestr, + volume_id, + FLAGS.volume_group), + terminate_on_stderr=False) + + @defer.inlineCallbacks + def delete_volume(self, volume_id): + yield process.simple_execute( + "sudo lvremove -f %s/%s" % (FLAGS.volume_group, + volume_id), + terminate_on_stderr=False) + + @defer.inlineCallbacks + def create_export(self, volume_id, shelf_id, blade_id): + yield process.simple_execute( + "sudo vblade-persist setup %s %s %s /dev/%s/%s" % + (shelf_id, + blade_id, + FLAGS.aoe_eth_dev, + FLAGS.volume_group, + volume_id), + terminate_on_stderr=False) + + @defer.inlineCallbacks + def remove_export(self, _volume_id, shelf_id, blade_id): + yield process.simple_execute( + "sudo vblade-persist stop %s %s" % (shelf_id, blade_id), + terminate_on_stderr=False) + yield process.simple_execute( + "sudo vblade-persist destroy %s %s" % (shelf_id, blade_id), + terminate_on_stderr=False) + + @defer.inlineCallbacks + def ensure_exports(self): + # NOTE(ja): wait for blades to appear + yield process.simple_execute("sleep 5") + yield process.simple_execute("sudo vblade-persist auto all", + check_exit_code=False) + yield process.simple_execute("sudo vblade-persist start all", + check_exit_code=False) + diff --git a/nova/volume/manager.py b/nova/volume/manager.py new file mode 100644 index 000000000..c4686a75c --- /dev/null +++ b/nova/volume/manager.py @@ -0,0 +1,122 @@ +# 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. + +""" +Volume manager manages creating, attaching, detaching, and +destroying persistent storage volumes, ala EBS. +""" + +import logging + +from twisted.internet import defer + +from nova import db +from nova import exception +from nova import flags +from nova import manager +from nova import process +from nova import service +from nova import utils +from nova.volume import driver + + +FLAGS = flags.FLAGS +flags.DEFINE_string('storage_availability_zone', + 'nova', + 'availability zone of this service') +flags.DEFINE_boolean('fake_storage', False, + 'Should we make real storage volumes to attach?') +flags.DEFINE_string('volume_driver', 'nova.volume.driver.AOEDriver' + 'Driver to use for volume creation') +flags.DEFINE_integer('num_shelves', + 100, + 'Number of vblade shelves') +flags.DEFINE_integer('blades_per_shelf', + 16, + 'Number of vblade blades per shelf') + + +class AOEManager(manager.Manager): + def __init__(self, volume_driver=None, *args, **kwargs): + if not volume_driver: + # NOTE(vish): support the legacy fake storage flag + if FLAGS.fake_storage: + volume_driver='nova.volume.driver.FakeAOEDriver' + else: + volume_driver=FLAGS.volume_driver + self.driver = utils.import_object(volume_driver) + super(AOEManager, self).__init__(*args, **kwargs) + + def _ensure_blades(self, context): + total_blades = FLAGS.num_shelves, FLAGS.blades_per_shelf + if self.db.export_device_count(context) >= total_blades: + return + for shelf_id in xrange(FLAGS.num_shelves): + for blade_id in xrange(FLAGS.blades_per_shelf): + dev = {'shelf_id': shelf_id, 'blade_id': blade_id} + self.db.export_device_create(context, dev) + + @defer.inlineCallbacks + def create_volume(self, volume_id, context=None): + """Creates and exports the volume.""" + logging.info("volume %s: creating" % (volume_id)) + + volume_ref = self.db.volume_get(context, volume_id) + + self.db.volume_update(context, + volume_id, + {'node_name': FLAGS.node_name}) + + size = volume_ref['size'] + logging.debug("volume %s: creating lv of size %sG" % (volume_id, size)) + yield self.driver.create_volume(volume_id, size) + + logging.debug("volume %s: allocating shelf & blade" % (volume_id)) + rval = self.db.volume_allocate_shelf_and_blade(context, volume_id) + (shelf_id, blade_id) = rval + + logging.debug("volume %s: exporting shelf %s & blade %s" % (volume_id, + shelf_id, blade_id)) + + yield self.driver.create_export(volume_id, shelf_id, blade_id) + # TODO(joshua): We need to trigger a fanout message + # for aoe-discover on all the nodes + + self.db.volume_update(context, volume_id, {'status': 'available'}) + + logging.debug("volume %s: re-exporting all values" % (volume_id)) + yield self.driver.ensure_exports() + + logging.debug("volume %s: created successfully" % (volume_id)) + defer.returnValue(volume_id) + + @defer.inlineCallbacks + def delete_volume(self, volume_id, context=None): + logging.debug("Deleting volume with id of: %s" % (volume_id)) + volume_ref = self.db.volume_get(context, volume_id) + if volume_ref['attach_status'] == "attached": + raise exception.Error("Volume is still attached") + if volume_ref['node_name'] != FLAGS.node_name: + raise exception.Error("Volume is not local to this node") + shelf_id, blade_id = self.db.volume_get_shelf_and_blade(context, + volume_id) + yield self.driver.remove_export(volume_id, shelf_id, blade_id) + yield self.driver.delete_volume(volume_id) + self.db.volume_destroy(context, volume_id) + defer.returnValue(True) + diff --git a/nova/volume/service.py b/nova/volume/service.py index 7f6747577..423359007 100644 --- a/nova/volume/service.py +++ b/nova/volume/service.py @@ -17,164 +17,21 @@ # under the License. """ -Nova Storage manages creating, attaching, detaching, and -destroying persistent storage volumes, ala EBS. -Currently uses Ata-over-Ethernet. +Volume service allows rpc calls to the volume manager and reports state +to the database. """ -import logging - -from twisted.internet import defer - -from nova import db -from nova import exception from nova import flags -from nova import process from nova import service FLAGS = flags.FLAGS -flags.DEFINE_string('storage_dev', '/dev/sdb', - 'Physical device to use for volumes') -flags.DEFINE_string('volume_group', 'nova-volumes', - 'Name for the VG that will contain exported volumes') -flags.DEFINE_string('aoe_eth_dev', 'eth0', - 'Which device to export the volumes on') -flags.DEFINE_string('aoe_export_dir', - '/var/lib/vblade-persist/vblades', - 'AoE directory where exports are created') -flags.DEFINE_string('storage_availability_zone', - 'nova', - 'availability zone of this service') -flags.DEFINE_boolean('fake_storage', False, - 'Should we make real storage volumes to attach?') +flags.DEFINE_string('volume_manager', 'nova.volume.manager.AOEManager', + 'Manager for volumes') class VolumeService(service.Service): """ - There is one VolumeNode running on each host. - However, each VolumeNode can report on the state of - *all* volumes in the cluster. + Volume Service automatically passes commands on to the Volume Manager """ - def __init__(self): - super(VolumeService, self).__init__() - self._exec_init_volumes() - - @defer.inlineCallbacks - def create_volume(self, volume_id, context=None): - """ - Creates an exported volume (fake or real), - restarts exports to make it available. - Volume at this point has size, owner, and zone. - """ - logging.info("volume %s: creating" % (volume_id)) - - volume_ref = db.volume_get(context, volume_id) - - db.volume_update(context, volume_id, {'node_name': FLAGS.node_name}) - - size = volume_ref['size'] - logging.debug("volume %s: creating lv of size %sG" % (volume_id, size)) - yield self._exec_create_volume(volume_id, size) - - logging.debug("volume %s: allocating shelf & blade" % (volume_id)) - (shelf_id, blade_id) = db.volume_allocate_shelf_and_blade(context, - volume_id) - - logging.debug("volume %s: exporting shelf %s & blade %s" % (volume_id, - shelf_id, blade_id)) - - yield self._exec_create_export(volume_id, shelf_id, blade_id) - # TODO(joshua): We need to trigger a fanout message - # for aoe-discover on all the nodes - - db.volume_update(context, volume_id, {'status': 'available'}) - - logging.debug("volume %s: re-exporting all values" % (volume_id)) - yield self._exec_ensure_exports() - - logging.debug("volume %s: created successfully" % (volume_id)) - defer.returnValue(volume_id) - - @defer.inlineCallbacks - def delete_volume(self, volume_id, context=None): - logging.debug("Deleting volume with id of: %s" % (volume_id)) - volume_ref = db.volume_get(context, volume_id) - if volume_ref['attach_status'] == "attached": - raise exception.Error("Volume is still attached") - if volume_ref['node_name'] != FLAGS.node_name: - raise exception.Error("Volume is not local to this node") - shelf_id, blade_id = db.volume_get_shelf_and_blade(context, - volume_id) - yield self._exec_remove_export(volume_id, shelf_id, blade_id) - yield self._exec_delete_volume(volume_id) - db.volume_destroy(context, volume_id) - defer.returnValue(True) - - @defer.inlineCallbacks - def _exec_create_volume(self, volume_id, size): - if FLAGS.fake_storage: - defer.returnValue(None) - if int(size) == 0: - sizestr = '100M' - else: - sizestr = '%sG' % size - yield process.simple_execute( - "sudo lvcreate -L %s -n %s %s" % (sizestr, - volume_id, - FLAGS.volume_group), - terminate_on_stderr=False) - - @defer.inlineCallbacks - def _exec_delete_volume(self, volume_id): - if FLAGS.fake_storage: - defer.returnValue(None) - yield process.simple_execute( - "sudo lvremove -f %s/%s" % (FLAGS.volume_group, - volume_id), - terminate_on_stderr=False) - - @defer.inlineCallbacks - def _exec_create_export(self, volume_id, shelf_id, blade_id): - if FLAGS.fake_storage: - defer.returnValue(None) - yield process.simple_execute( - "sudo vblade-persist setup %s %s %s /dev/%s/%s" % - (shelf_id, - blade_id, - FLAGS.aoe_eth_dev, - FLAGS.volume_group, - volume_id), - terminate_on_stderr=False) - - @defer.inlineCallbacks - def _exec_remove_export(self, _volume_id, shelf_id, blade_id): - if FLAGS.fake_storage: - defer.returnValue(None) - yield process.simple_execute( - "sudo vblade-persist stop %s %s" % (shelf_id, blade_id), - terminate_on_stderr=False) - yield process.simple_execute( - "sudo vblade-persist destroy %s %s" % (shelf_id, blade_id), - terminate_on_stderr=False) - - @defer.inlineCallbacks - def _exec_ensure_exports(self): - if FLAGS.fake_storage: - defer.returnValue(None) - - yield process.simple_execute("sleep 5") # wait for blades to appear - yield process.simple_execute("sudo vblade-persist auto all", - check_exit_code=False) - yield process.simple_execute("sudo vblade-persist start all", - check_exit_code=False) - - @defer.inlineCallbacks - def _exec_init_volumes(self): - if FLAGS.fake_storage: - defer.returnValue(None) - yield process.simple_execute( - "sudo pvcreate %s" % (FLAGS.storage_dev)) - yield process.simple_execute( - "sudo vgcreate %s %s" % (FLAGS.volume_group, - FLAGS.storage_dev)) + pass |