No longer require a volume.
* Added a VolumeCreationFailure exception, which is raised on Instance creation if the volume doesn't prov. * Casted the flavorid in the flavor view code to a string to ensure it works in fake mode. * Changed the Guest code to save the volume info in fstab. * Split the VolumeHelper into VolumeDevice and VolumeMountPoint classes. * Changed fake mode to work when no volume is given.
This commit is contained in:
parent
3103efbb43
commit
532633cd4b
@ -108,3 +108,8 @@ class VolumeAttachmentsNotFound(NotFound):
|
||||
|
||||
message = _("Cannot find the volumes attached to compute "
|
||||
"instance %(server_id)")
|
||||
|
||||
|
||||
class VolumeCreationFailure(ReddwarfError):
|
||||
|
||||
message = _("Failed to create a volume in Nova.")
|
||||
|
@ -40,7 +40,8 @@ instances = Table('instances', meta,
|
||||
Column('compute_instance_id', String(36)),
|
||||
Column('task_id', Integer()),
|
||||
Column('task_description', String(32)),
|
||||
Column('task_start_time', DateTime()))
|
||||
Column('task_start_time', DateTime()),
|
||||
Column('volume_id', String(36)))
|
||||
|
||||
|
||||
def upgrade(migrate_engine):
|
||||
|
@ -40,7 +40,7 @@ class FlavorView(object):
|
||||
detailed = '/detail'
|
||||
splitpath.pop(-1)
|
||||
flavorid = self.flavor.id
|
||||
if splitpath[-1] == flavorid:
|
||||
if str(splitpath[-1]) == str(flavorid):
|
||||
splitpath.pop(-1)
|
||||
href_template = "%(scheme)s://%(endpoint)s%(path)s/%(flavorid)s"
|
||||
for link in self.flavor.links:
|
||||
|
@ -46,7 +46,7 @@ from reddwarf.common.exception import ProcessExecutionError
|
||||
from reddwarf.common import config
|
||||
from reddwarf.common import utils
|
||||
from reddwarf.guestagent.db import models
|
||||
from reddwarf.guestagent.volume import VolumeHelper
|
||||
from reddwarf.guestagent.volume import VolumeDevice
|
||||
from reddwarf.instance import models as rd_models
|
||||
|
||||
|
||||
@ -503,17 +503,16 @@ class DBaaSAgent(object):
|
||||
app = MySqlApp(self.status)
|
||||
restart_mysql = False
|
||||
if device_path:
|
||||
VolumeHelper.format(device_path)
|
||||
device = VolumeDevice(device_path)
|
||||
device.format()
|
||||
if app.is_installed(pkg):
|
||||
#stop and do not update database
|
||||
app.stop_mysql()
|
||||
restart_mysql = True
|
||||
#rsync exiting data
|
||||
VolumeHelper.migrate_data(device_path, MYSQL_BASE_DIR)
|
||||
device.migrate_data(MYSQL_BASE_DIR)
|
||||
#mount the volume
|
||||
VolumeHelper.mount(device_path, mount_point)
|
||||
#TODO(cp16net) need to update the fstab here so that on a
|
||||
# restart the volume will be mounted automatically again
|
||||
device.mount(mount_point)
|
||||
LOG.debug(_("Mounted the volume."))
|
||||
#check mysql was installed and stopped
|
||||
if restart_mysql:
|
||||
|
@ -30,26 +30,24 @@ LOG = logging.getLogger(__name__)
|
||||
CONFIG = config.Config
|
||||
|
||||
|
||||
class VolumeHelper(object):
|
||||
class VolumeDevice(object):
|
||||
|
||||
@staticmethod
|
||||
def _has_volume_device(device_path):
|
||||
return not device_path is None
|
||||
def __init__(self, device_path):
|
||||
self.device_path = device_path
|
||||
|
||||
@staticmethod
|
||||
def migrate_data(device_path, mysql_base):
|
||||
def migrate_data(self, mysql_base):
|
||||
""" Synchronize the data from the mysql directory to the new volume """
|
||||
# Use sudo to have access to this spot.
|
||||
utils.execute("sudo", "mkdir", "-p", TMP_MOUNT_POINT)
|
||||
VolumeHelper.mount(device_path, TMP_MOUNT_POINT)
|
||||
self._tmp_mount(TMP_MOUNT_POINT)
|
||||
if not mysql_base[-1] == '/':
|
||||
mysql_base = "%s/" % mysql_base
|
||||
utils.execute("sudo", "rsync", "--safe-links", "--perms",
|
||||
"--recursive", "--owner", "--group", "--xattrs",
|
||||
"--sparse", mysql_base, TMP_MOUNT_POINT)
|
||||
VolumeHelper.unmount(device_path)
|
||||
self.unmount()
|
||||
|
||||
@staticmethod
|
||||
def _check_device_exists(device_path):
|
||||
def _check_device_exists(self):
|
||||
"""Check that the device path exists.
|
||||
|
||||
Verify that the device path has actually been created and can report
|
||||
@ -58,72 +56,100 @@ class VolumeHelper(object):
|
||||
"""
|
||||
try:
|
||||
num_tries = CONFIG.get('num_tries', 3)
|
||||
utils.execute('sudo', 'blockdev', '--getsize64', device_path,
|
||||
utils.execute('sudo', 'blockdev', '--getsize64', self.device_path,
|
||||
attempts=num_tries)
|
||||
except ProcessExecutionError:
|
||||
raise GuestError("InvalidDevicePath(path=%s)" % device_path)
|
||||
raise GuestError("InvalidDevicePath(path=%s)" % self.device_path)
|
||||
|
||||
@staticmethod
|
||||
def _check_format(device_path):
|
||||
def _check_format(self):
|
||||
"""Checks that an unmounted volume is formatted."""
|
||||
child = pexpect.spawn("sudo dumpe2fs %s" % device_path)
|
||||
child = pexpect.spawn("sudo dumpe2fs %s" % self.device_path)
|
||||
try:
|
||||
i = child.expect(['has_journal', 'Wrong magic number'])
|
||||
if i == 0:
|
||||
return
|
||||
volume_fstype = CONFIG.get('volume_fstype', 'ext3')
|
||||
raise IOError('Device path at %s did not seem to be %s.' %
|
||||
(device_path, volume_fstype))
|
||||
(self.device_path, volume_fstype))
|
||||
except pexpect.EOF:
|
||||
raise IOError("Volume was not formatted.")
|
||||
child.expect(pexpect.EOF)
|
||||
|
||||
@staticmethod
|
||||
def _format(device_path):
|
||||
def _format(self):
|
||||
"""Calls mkfs to format the device at device_path."""
|
||||
volume_fstype = CONFIG.get('volume_fstype', 'ext3')
|
||||
format_options = CONFIG.get('format_options', '-m 5')
|
||||
cmd = "sudo mkfs -t %s %s %s" % (volume_fstype,
|
||||
format_options, device_path)
|
||||
format_options, self.device_path)
|
||||
volume_format_timeout = CONFIG.get('volume_format_timeout', 120)
|
||||
child = pexpect.spawn(cmd, timeout=volume_format_timeout)
|
||||
# child.expect("(y,n)")
|
||||
# child.sendline('y')
|
||||
child.expect(pexpect.EOF)
|
||||
|
||||
@staticmethod
|
||||
def format(device_path):
|
||||
def format(self):
|
||||
"""Formats the device at device_path and checks the filesystem."""
|
||||
VolumeHelper._check_device_exists(device_path)
|
||||
VolumeHelper._format(device_path)
|
||||
VolumeHelper._check_format(device_path)
|
||||
self._check_device_exists()
|
||||
self._format()
|
||||
self._check_format()
|
||||
|
||||
@staticmethod
|
||||
def mount(device_path, mount_point):
|
||||
if not os.path.exists(mount_point):
|
||||
os.makedirs(mount_point)
|
||||
volume_fstype = CONFIG.get('volume_fstype', 'ext3')
|
||||
mount_options = CONFIG.get('mount_options', 'noatime')
|
||||
cmd = "sudo mount -t %s -o %s %s %s" % (volume_fstype,
|
||||
mount_options,
|
||||
device_path, mount_point)
|
||||
child = pexpect.spawn(cmd)
|
||||
child.expect(pexpect.EOF)
|
||||
def mount(self, mount_point):
|
||||
"""Mounts, and writes to fstab."""
|
||||
mount_point = VolumeMountPoint(self.device_path, mount_point)
|
||||
mount_point.mount()
|
||||
mount_point.write_to_fstab()
|
||||
|
||||
@staticmethod
|
||||
def unmount(mount_point):
|
||||
if os.path.exists(mount_point):
|
||||
cmd = "sudo umount %s" % mount_point
|
||||
child = pexpect.spawn(cmd)
|
||||
child.expect(pexpect.EOF)
|
||||
|
||||
@staticmethod
|
||||
def resize_fs(device_path):
|
||||
#TODO(tim.simpson): Are we using this?
|
||||
def resize_fs(self):
|
||||
"""Resize the filesystem on the specified device"""
|
||||
VolumeHelper._check_device_exists(device_path)
|
||||
self._check_device_exists()
|
||||
try:
|
||||
utils.execute("sudo", "resize2fs", device_path)
|
||||
utils.execute("sudo", "resize2fs", self.device_path)
|
||||
except ProcessExecutionError as err:
|
||||
LOG.error(err)
|
||||
raise GuestError("Error resizing the filesystem: %s"
|
||||
% device_path)
|
||||
% self.device_path)
|
||||
|
||||
def _tmp_mount(self, mount_point):
|
||||
"""Mounts, but doesn't save to fstab."""
|
||||
mount_point = VolumeMountPoint(self.device_path, mount_point)
|
||||
mount_point.mount() # Don't save to fstab.
|
||||
|
||||
def unmount(self):
|
||||
if os.path.exists(self.device_path):
|
||||
cmd = "sudo umount %s" % self.device_path
|
||||
child = pexpect.spawn(cmd)
|
||||
child.expect(pexpect.EOF)
|
||||
|
||||
|
||||
class VolumeMountPoint(object):
|
||||
|
||||
def __init__(self, device_path, mount_point):
|
||||
self.device_path = device_path
|
||||
self.mount_point = mount_point
|
||||
self.volume_fstype = CONFIG.get('volume_fstype', 'ext3')
|
||||
self.mount_options = CONFIG.get('mount_options', 'defaults,noatime')
|
||||
|
||||
def mount(self):
|
||||
if not os.path.exists(self.mount_point):
|
||||
os.makedirs(self.mount_point)
|
||||
LOG.debug("Adding volume. Device path:%s, mount_point:%s, "
|
||||
"volume_type:%s, mount options:%s" %
|
||||
(self.device_path, self.mount_point, self.volume_fstype,
|
||||
self.mount_options))
|
||||
cmd = "sudo mount -t %s -o %s %s %s" % (self.volume_fstype,
|
||||
self.mount_options, self.device_path, self.mount_point)
|
||||
child = pexpect.spawn(cmd)
|
||||
child.expect(pexpect.EOF)
|
||||
|
||||
def write_to_fstab(self):
|
||||
fstab_line = "%s\t%s\t%s\t%s\t0\t0" % (self.device_path,
|
||||
self.mount_point, self.volume_fstype, self.mount_options)
|
||||
LOG.debug("Writing new line to fstab:%s" % fstab_line)
|
||||
utils.execute("sudo", "cp", "/etc/fstab", "/etc/fstab.orig")
|
||||
utils.execute("sudo", "cp", "/etc/fstab", "/tmp/newfstab")
|
||||
utils.execute("sudo", "chmod", "666", "/tmp/newfstab")
|
||||
with open("/tmp/newfstab", 'a') as new_fstab:
|
||||
new_fstab.write("\n" + fstab_line)
|
||||
utils.execute("sudo", "chmod", "640", "/tmp/newfstab")
|
||||
utils.execute("sudo", "mv", "/tmp/newfstab", "/etc/fstab")
|
||||
|
@ -43,7 +43,7 @@ CONFIG = config.Config
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def load_server(context, instance_id, server_id):
|
||||
def load_server_with_volumes(context, instance_id, server_id):
|
||||
"""Loads a server or raises an exception."""
|
||||
client = create_nova_client(context)
|
||||
try:
|
||||
@ -168,8 +168,8 @@ class Instance(object):
|
||||
db_info = DBInstance.find_by(id=id)
|
||||
except rd_exceptions.NotFound:
|
||||
raise rd_exceptions.NotFound(uuid=id)
|
||||
server, volumes = load_server(context, db_info.id,
|
||||
db_info.compute_instance_id)
|
||||
server, volumes = load_server_with_volumes(context, db_info.id,
|
||||
db_info.compute_instance_id)
|
||||
task_status = db_info.task_status
|
||||
service_status = InstanceServiceStatus.find_by(instance_id=id)
|
||||
LOG.info("service status=%s" % service_status)
|
||||
@ -208,6 +208,9 @@ class Instance(object):
|
||||
volume_size,
|
||||
display_name="mysql-%s" % db_info.id,
|
||||
display_description=volume_desc)
|
||||
# Record the volume ID in case something goes wrong.
|
||||
db_info.volume_id = volume_ref.id
|
||||
db_info.save()
|
||||
#TODO(cp16net) this is bad to wait here for the volume create
|
||||
# before returning but this was a quick way to get it working
|
||||
# for now we need this to go into the task manager
|
||||
@ -219,8 +222,7 @@ class Instance(object):
|
||||
v_ref = volume_client.volumes.get(volume_ref.id)
|
||||
|
||||
if v_ref.status in ['error']:
|
||||
raise rd_exceptions.ReddwarfError(
|
||||
_("Could not create volume"))
|
||||
raise rd_exceptions.VolumeCreationFailure()
|
||||
LOG.debug(_("Created volume %s") % v_ref)
|
||||
# The mapping is in the format:
|
||||
# <id>:[<type>]:[<size(GB)>]:[<delete_on_terminate>]
|
||||
@ -232,10 +234,10 @@ class Instance(object):
|
||||
# guest. Also in cases for ovz where this is mounted on
|
||||
# the host, that's not going to work for us.
|
||||
block_device = {'vdb': mapping}
|
||||
volume = [{'id': v_ref.id,
|
||||
volumes = [{'id': v_ref.id,
|
||||
'size': v_ref.size}]
|
||||
LOG.debug("block_device = %s" % block_device)
|
||||
LOG.debug("volume = %s" % volume)
|
||||
LOG.debug("volume = %s" % volumes)
|
||||
|
||||
device_path = CONFIG.get('device_path')
|
||||
mount_point = CONFIG.get('mount_point')
|
||||
@ -246,12 +248,16 @@ class Instance(object):
|
||||
block_device = None
|
||||
device_path = None
|
||||
mount_point = None
|
||||
volume = None
|
||||
volumes = None
|
||||
#end volume_support
|
||||
#block_device = ""
|
||||
#device_path = /dev/vdb
|
||||
#mount_point = /var/lib/mysql
|
||||
volume_info = {'block_device': block_device,
|
||||
'device_path': device_path,
|
||||
'mount_point': mount_point}
|
||||
return volume, volume_info
|
||||
'mount_point': mount_point,
|
||||
'volumes': volumes}
|
||||
return volume_info
|
||||
|
||||
@classmethod
|
||||
def create(cls, context, name, flavor_ref, image_id,
|
||||
@ -259,15 +265,25 @@ class Instance(object):
|
||||
db_info = DBInstance.create(name=name,
|
||||
task_status=InstanceTasks.NONE)
|
||||
LOG.debug(_("Created new Reddwarf instance %s...") % db_info.id)
|
||||
volume, volume_info = cls._create_volume(context,
|
||||
db_info,
|
||||
volume_size)
|
||||
|
||||
if volume_size:
|
||||
volume_info = cls._create_volume(context, db_info, volume_size)
|
||||
block_device_mapping = volume_info['block_device']
|
||||
device_path=volume_info['device_path']
|
||||
mount_point=volume_info['mount_point']
|
||||
volumes = volume_info['volumes']
|
||||
else:
|
||||
block_device_mapping = None
|
||||
device_path=None
|
||||
mount_point=None
|
||||
volumes = []
|
||||
|
||||
client = create_nova_client(context)
|
||||
files = {"/etc/guest_info": "guest_id=%s\nservice_type=%s\n" %
|
||||
(db_info.id, service_type)}
|
||||
server = client.servers.create(name, image_id, flavor_ref,
|
||||
files=files,
|
||||
block_device_mapping=volume_info['block_device'])
|
||||
block_device_mapping=block_device_mapping)
|
||||
LOG.debug(_("Created new compute instance %s.") % server.id)
|
||||
|
||||
db_info.compute_instance_id = server.id
|
||||
@ -281,9 +297,9 @@ class Instance(object):
|
||||
# populate the databases
|
||||
model_schemas = populate_databases(databases)
|
||||
guest.prepare(512, model_schemas, users=[],
|
||||
device_path=volume_info['device_path'],
|
||||
mount_point=volume_info['mount_point'])
|
||||
return Instance(context, db_info, server, service_status, volume)
|
||||
device_path=device_path,
|
||||
mount_point=mount_point)
|
||||
return Instance(context, db_info, server, service_status, volumes)
|
||||
|
||||
def get_guest(self):
|
||||
return create_guest_client(self.context, self.db_info.id)
|
||||
@ -377,9 +393,10 @@ class Instance(object):
|
||||
|
||||
def _refresh_compute_server_info(self):
|
||||
"""Refreshes the compute server field."""
|
||||
server = load_server(self.context, self.db_info.id,
|
||||
self.db_info.compute_instance_id)
|
||||
server, volumes = load_server_with_volumes(self.context,
|
||||
self.db_info.id, self.db_info.compute_instance_id)
|
||||
self.server = server
|
||||
self.volumes = volumes
|
||||
return server
|
||||
|
||||
def resize_flavor(self, new_flavor_id):
|
||||
@ -631,7 +648,8 @@ class DBInstance(DatabaseModelBase):
|
||||
#TODO(tim.simpson): Add start time.
|
||||
|
||||
_data_fields = ['name', 'created', 'compute_instance_id',
|
||||
'task_id', 'task_description', 'task_start_time']
|
||||
'task_id', 'task_description', 'task_start_time',
|
||||
'volume_id']
|
||||
|
||||
def __init__(self, task_status=None, **kwargs):
|
||||
kwargs["task_id"] = task_status.code
|
||||
|
@ -262,7 +262,10 @@ class InstanceController(BaseController):
|
||||
databases = body['instance'].get('databases')
|
||||
if databases is None:
|
||||
databases = []
|
||||
volume_size = body['instance']['volume']['size']
|
||||
if body['instance'].get('volume', None) is not None:
|
||||
volume_size = body['instance']['volume']['size']
|
||||
else:
|
||||
volume_size = None
|
||||
instance = models.Instance.create(context, name, flavor_ref,
|
||||
image_id, databases,
|
||||
service_type, volume_size)
|
||||
@ -284,8 +287,8 @@ class InstanceController(BaseController):
|
||||
volume_size = float(size)
|
||||
except (ValueError, TypeError) as err:
|
||||
LOG.error(err)
|
||||
msg = ("Required element/key - instance volume"
|
||||
"'size' was not specified as a number")
|
||||
msg = ("Required element/key - instance volume 'size' was not "
|
||||
"specified as a number (value was %s)." % size)
|
||||
raise rd_exceptions.ReddwarfError(msg)
|
||||
if int(volume_size) != volume_size or int(volume_size) < 1:
|
||||
msg = ("Volume 'size' needs to be a positive "
|
||||
@ -308,12 +311,13 @@ class InstanceController(BaseController):
|
||||
body['instance']
|
||||
body['instance']['flavorRef']
|
||||
# TODO(cp16net) add in volume to the mix
|
||||
volume_size = body['instance']['volume']['size']
|
||||
if 'volume' in body['instance'] and \
|
||||
body['instance']['volume'] is not None:
|
||||
volume_size = body['instance']['volume']['size']
|
||||
except KeyError as e:
|
||||
LOG.error(_("Create Instance Required field(s) - %s") % e)
|
||||
raise rd_exceptions.ReddwarfError("Required element/key - %s "
|
||||
"was not specified" % e)
|
||||
InstanceController._validate_volume_size(volume_size)
|
||||
|
||||
@staticmethod
|
||||
def _validate_resize_instance(body):
|
||||
|
@ -59,6 +59,7 @@ class FakeFlavors(object):
|
||||
self._add(3, 10, "m1.medium", 4096)
|
||||
self._add(4, 10, "m1.large", 8192)
|
||||
self._add(5, 10, "m1.xlarge", 16384)
|
||||
self._add(6, 0, "tinier", 506)
|
||||
|
||||
def _add(self, *args, **kwargs):
|
||||
new_flavor = FakeFlavor(*args, **kwargs)
|
||||
@ -86,7 +87,7 @@ class FakeFlavors(object):
|
||||
class FakeServer(object):
|
||||
|
||||
def __init__(self, parent, owner, id, name, image_id, flavor_ref,
|
||||
block_device_mapping):
|
||||
block_device_mapping, volumes):
|
||||
self.owner = owner # This is a context.
|
||||
self.id = id
|
||||
self.parent = parent
|
||||
@ -95,8 +96,7 @@ class FakeServer(object):
|
||||
self.flavor_ref = flavor_ref
|
||||
self.events = EventSimulator()
|
||||
self.schedule_status("BUILD", 0.0)
|
||||
LOG.debug("block_device_mapping = %s" % block_device_mapping)
|
||||
self.block_device_mapping = block_device_mapping
|
||||
self.volumes = volumes
|
||||
|
||||
@property
|
||||
def addresses(self):
|
||||
@ -176,13 +176,32 @@ class FakeServers(object):
|
||||
def create(self, name, image_id, flavor_ref, files, block_device_mapping):
|
||||
id = "FAKE_%d" % self.next_id
|
||||
self.next_id += 1
|
||||
volumes = self._get_volumes_from_bdm(block_device_mapping)
|
||||
server = FakeServer(self, self.context, id, name, image_id, flavor_ref,
|
||||
block_device_mapping)
|
||||
block_device_mapping, volumes)
|
||||
self.db[id] = server
|
||||
server.schedule_status("ACTIVE", 1)
|
||||
LOG.info("FAKE_SERVERS_DB : %s" % str(FAKE_SERVERS_DB))
|
||||
return server
|
||||
|
||||
def _get_volumes_from_bdm(self, block_device_mapping):
|
||||
volumes = []
|
||||
if block_device_mapping is not None:
|
||||
# block_device_mapping is a dictionary, where the key is the
|
||||
# device name on the compute instance and the mapping info is a
|
||||
# set of fields in a string, seperated by colons.
|
||||
# For each device, find the volume, and record the mapping info
|
||||
# to another fake object and attach it to the volume
|
||||
# so that the fake API can later retrieve this.
|
||||
for device in block_device_mapping:
|
||||
mapping = block_device_mapping[device]
|
||||
(id, _type, size, delete_on_terminate) = mapping.split(":")
|
||||
volume = self.volumes.get(id)
|
||||
volume.mapping = FakeBlockDeviceMappingInfo(id, device,
|
||||
_type, size, delete_on_terminate)
|
||||
volumes.append(volume)
|
||||
return volumes
|
||||
|
||||
def get(self, id):
|
||||
if id not in self.db:
|
||||
LOG.error("Couldn't find server id %s, collection=%s" % (id,
|
||||
@ -194,6 +213,11 @@ class FakeServers(object):
|
||||
else:
|
||||
raise nova_exceptions.NotFound(404, "Bad permissions")
|
||||
|
||||
def get_server_volumes(self, server_id):
|
||||
return [volume.mapping
|
||||
for volume in self.get(server_id).volumes
|
||||
if volume.mapping is not None]
|
||||
|
||||
def list(self):
|
||||
return [v for (k, v) in self.db.items() if self.can_see(v.id)]
|
||||
|
||||
@ -223,20 +247,8 @@ class FakeServerVolumes(object):
|
||||
return [ServerVolumes(server.block_device_mapping)]
|
||||
|
||||
|
||||
FLAVORS = FakeFlavors()
|
||||
|
||||
|
||||
class FakeClient(object):
|
||||
|
||||
def __init__(self, context):
|
||||
self.context = context
|
||||
self.flavors = FLAVORS
|
||||
self.servers = FakeServers(context, self.flavors)
|
||||
self.volumes = FakeServerVolumes(context)
|
||||
|
||||
|
||||
def fake_create_nova_client(context):
|
||||
return FakeClient(context)
|
||||
|
||||
|
||||
class FakeVolume(object):
|
||||
@ -268,6 +280,16 @@ class FakeVolume(object):
|
||||
return self._current_status
|
||||
|
||||
|
||||
class FakeBlockDeviceMappingInfo(object):
|
||||
|
||||
def __init__(self, id, device, _type, size, delete_on_terminate):
|
||||
self.volumeId = id
|
||||
self.device = device
|
||||
self.type = _type
|
||||
self.size = size
|
||||
self.delete_on_terminate = delete_on_terminate
|
||||
|
||||
|
||||
FAKE_VOLUMES_DB = {}
|
||||
|
||||
|
||||
@ -307,12 +329,40 @@ class FakeVolumes(object):
|
||||
return volume
|
||||
|
||||
|
||||
class FakeVolumeClient(object):
|
||||
FLAVORS = FakeFlavors()
|
||||
|
||||
class FakeClient(object):
|
||||
|
||||
def __init__(self, context):
|
||||
self.context = context
|
||||
self.flavors = FLAVORS
|
||||
self.servers = FakeServers(context, self.flavors)
|
||||
self.volumes = FakeVolumes(context)
|
||||
self.servers.volumes = self.volumes
|
||||
|
||||
def get_server_volumes(self, server_id):
|
||||
return self.servers.get_server_volumes(server_id)
|
||||
|
||||
|
||||
CLIENT_DATA = {}
|
||||
|
||||
|
||||
def get_client_data(context):
|
||||
if context not in CLIENT_DATA:
|
||||
nova_client = FakeClient(context)
|
||||
volume_client = FakeClient(context)
|
||||
nova_client.volumes = volume_client
|
||||
volume_client.servers = nova_client
|
||||
CLIENT_DATA[context] = {
|
||||
'nova': nova_client,
|
||||
'volume': volume_client
|
||||
}
|
||||
return CLIENT_DATA[context]
|
||||
|
||||
|
||||
def fake_create_nova_client(context):
|
||||
return get_client_data(context)['nova']
|
||||
|
||||
|
||||
def fake_create_nova_volume_client(context):
|
||||
return FakeVolumeClient(context)
|
||||
return get_client_data(context)['volume']
|
||||
|
Loading…
x
Reference in New Issue
Block a user