Adding support for Coraid AoE SANs Appliances.
This driver provide support for Coraid hardware storage appliances using AoE (ATA Over Ethernet) protocol. Implements blueprint coraid-volume-driver Reference to Nova patch libvirt-aoe : https://review.openstack.org/21101 The following operations are supported : -- Volume Creation with Volume Types -- Volume Deletion -- Volume Attach -- Volume Detach -- Snapshot Creation -- Snapshot Deletion -- Create Volume from Snapshot -- Volume Stats The driver only work when operating on EtherCloud ESM, Coraid VSX and Coraid SRX Appliances. Change-Id: I7c8dde0c99698b52c151a4db0fb1bb94d516db61
This commit is contained in:
parent
835fb61442
commit
695e3a848a
|
@ -0,0 +1,214 @@
|
|||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2012 OpenStack LLC.
|
||||
# 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.
|
||||
|
||||
from cinder import exception
|
||||
from cinder.openstack.common import log as logging
|
||||
from cinder import test
|
||||
from cinder.volume.drivers import coraid
|
||||
from cinder.volume.drivers.coraid import CoraidDriver
|
||||
from cinder.volume.drivers.coraid import CoraidRESTClient
|
||||
|
||||
import cookielib
|
||||
import urllib2
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
fake_esm_ipaddress = "192.168.0.1"
|
||||
fake_esm_username = "admin"
|
||||
fake_esm_password = "12345678"
|
||||
|
||||
fake_volume_name = "volume-12345678-1234-1234-1234-1234567890ab"
|
||||
fake_volume_size = "10"
|
||||
fake_repository_name = "A-B:C:D"
|
||||
fake_pool_name = "FakePool"
|
||||
fake_aoetarget = 4081
|
||||
fake_shelf = 16
|
||||
fake_lun = 241
|
||||
|
||||
fake_str_aoetarget = str(fake_aoetarget)
|
||||
fake_lun_addr = {"shelf": fake_shelf, "lun": fake_lun}
|
||||
|
||||
fake_volume = {"name": fake_volume_name,
|
||||
"size": fake_volume_size,
|
||||
"volume_type": {"id": 1}}
|
||||
|
||||
fake_volume_info = {"pool": fake_pool_name,
|
||||
"repo": fake_repository_name,
|
||||
"vsxidx": fake_aoetarget,
|
||||
"index": fake_lun,
|
||||
"shelf": fake_shelf}
|
||||
|
||||
fake_lun_info = {"shelf": fake_shelf, "lun": fake_lun}
|
||||
|
||||
fake_snapshot_name = "snapshot-12345678-8888-8888-1234-1234567890ab"
|
||||
fake_snapshot_id = "12345678-8888-8888-1234-1234567890ab"
|
||||
fake_volume_id = "12345678-1234-1234-1234-1234567890ab"
|
||||
fake_snapshot = {"id": fake_snapshot_id,
|
||||
"volume_id": fake_volume_id}
|
||||
|
||||
fake_configure_data = [{"addr": "cms", "data": "FAKE"}]
|
||||
|
||||
fake_esm_fetch = [[
|
||||
{"command": "super_fake_command_of_death"},
|
||||
{"reply": [
|
||||
{"lv":
|
||||
{"containingPool": fake_pool_name,
|
||||
"lunIndex": fake_aoetarget,
|
||||
"name": fake_volume_name,
|
||||
"lvStatus":
|
||||
{"exportedLun":
|
||||
{"lun": fake_lun,
|
||||
"shelf": fake_shelf}}
|
||||
},
|
||||
"repoName": fake_repository_name}]}]]
|
||||
|
||||
fake_esm_success = {"category": "provider",
|
||||
"tracking": False,
|
||||
"configState": "completedSuccessfully",
|
||||
"heldPending": False,
|
||||
"metaCROp": "noAction",
|
||||
"message": None}
|
||||
|
||||
|
||||
class TestCoraidDriver(test.TestCase):
|
||||
def setUp(self):
|
||||
super(TestCoraidDriver, self).setUp()
|
||||
self.esm_mock = self.mox.CreateMockAnything()
|
||||
self.stubs.Set(coraid, 'CoraidRESTClient',
|
||||
lambda *_, **__: self.esm_mock)
|
||||
self.drv = CoraidDriver()
|
||||
self.drv.do_setup({})
|
||||
|
||||
def test_create_volume(self):
|
||||
setattr(self.esm_mock, 'create_lun', lambda *_: True)
|
||||
self.stubs.Set(CoraidDriver, '_get_repository',
|
||||
lambda *_: fake_repository_name)
|
||||
self.drv.create_volume(fake_volume)
|
||||
|
||||
def test_delete_volume(self):
|
||||
setattr(self.esm_mock, 'delete_lun',
|
||||
lambda *_: True)
|
||||
self.drv.delete_volume(fake_volume)
|
||||
|
||||
def test_initialize_connection(self):
|
||||
setattr(self.esm_mock, '_get_lun_address',
|
||||
lambda *_: fake_lun_addr)
|
||||
self.drv.initialize_connection(fake_volume, '')
|
||||
|
||||
def test_create_snapshot(self):
|
||||
setattr(self.esm_mock, 'create_snapshot',
|
||||
lambda *_: True)
|
||||
self.drv.create_snapshot(fake_snapshot)
|
||||
|
||||
def test_delete_snapshot(self):
|
||||
setattr(self.esm_mock, 'delete_snapshot',
|
||||
lambda *_: True)
|
||||
self.drv.delete_snapshot(fake_snapshot)
|
||||
|
||||
def test_create_volume_from_snapshot(self):
|
||||
setattr(self.esm_mock, 'create_volume_from_snapshot',
|
||||
lambda *_: True)
|
||||
self.stubs.Set(CoraidDriver, '_get_repository',
|
||||
lambda *_: fake_repository_name)
|
||||
self.drv.create_volume_from_snapshot(fake_volume, fake_snapshot)
|
||||
|
||||
|
||||
class TestCoraidRESTClient(test.TestCase):
|
||||
def setUp(self):
|
||||
super(TestCoraidRESTClient, self).setUp()
|
||||
self.stubs.Set(cookielib, 'CookieJar', lambda *_: True)
|
||||
self.stubs.Set(urllib2, 'build_opener', lambda *_: True)
|
||||
self.stubs.Set(urllib2, 'HTTPCookieProcessor', lambda *_: True)
|
||||
self.stubs.Set(CoraidRESTClient, '_login', lambda *_: True)
|
||||
self.rest_mock = self.mox.CreateMockAnything()
|
||||
self.stubs.Set(coraid, 'CoraidRESTClient',
|
||||
lambda *_, **__: self.rest_mock)
|
||||
self.drv = CoraidRESTClient(fake_esm_ipaddress,
|
||||
fake_esm_username,
|
||||
fake_esm_password)
|
||||
|
||||
def test__configure(self):
|
||||
setattr(self.rest_mock, '_configure',
|
||||
lambda *_: True)
|
||||
self.stubs.Set(CoraidRESTClient, '_esm',
|
||||
lambda *_: fake_esm_success)
|
||||
self.drv._configure(fake_configure_data)
|
||||
|
||||
def test__get_volume_info(self):
|
||||
setattr(self.rest_mock, '_get_volume_info',
|
||||
lambda *_: True)
|
||||
self.stubs.Set(CoraidRESTClient, '_esm',
|
||||
lambda *_: fake_esm_fetch)
|
||||
self.drv._get_volume_info(fake_volume_name)
|
||||
|
||||
def test__get_lun_address(self):
|
||||
setattr(self.rest_mock, '_get_lun_address',
|
||||
lambda *_: fake_lun_info)
|
||||
self.stubs.Set(CoraidRESTClient, '_get_volume_info',
|
||||
lambda *_: fake_volume_info)
|
||||
self.drv._get_lun_address(fake_volume_name)
|
||||
|
||||
def test_create_lun(self):
|
||||
setattr(self.rest_mock, 'create_lun',
|
||||
lambda *_: True)
|
||||
self.stubs.Set(CoraidRESTClient, '_configure',
|
||||
lambda *_: fake_esm_success)
|
||||
self.rest_mock.create_lun(fake_volume_name, '10',
|
||||
fake_repository_name)
|
||||
self.drv.create_lun(fake_volume_name, '10',
|
||||
fake_repository_name)
|
||||
|
||||
def test_delete_lun(self):
|
||||
setattr(self.rest_mock, 'delete_lun',
|
||||
lambda *_: True)
|
||||
self.stubs.Set(CoraidRESTClient, '_get_volume_info',
|
||||
lambda *_: fake_volume_info)
|
||||
self.stubs.Set(CoraidRESTClient, '_configure',
|
||||
lambda *_: fake_esm_success)
|
||||
self.rest_mock.delete_lun(fake_volume_name)
|
||||
self.drv.delete_lun(fake_volume_name)
|
||||
|
||||
def test_create_snapshot(self):
|
||||
setattr(self.rest_mock, 'create_snapshot',
|
||||
lambda *_: True)
|
||||
self.stubs.Set(CoraidRESTClient, '_get_volume_info',
|
||||
lambda *_: fake_volume_info)
|
||||
self.stubs.Set(CoraidRESTClient, '_configure',
|
||||
lambda *_: fake_esm_success)
|
||||
self.drv.create_snapshot(fake_volume_name,
|
||||
fake_volume_name)
|
||||
|
||||
def test_delete_snapshot(self):
|
||||
setattr(self.rest_mock, 'delete_snapshot',
|
||||
lambda *_: True)
|
||||
self.stubs.Set(CoraidRESTClient, '_get_volume_info',
|
||||
lambda *_: fake_volume_info)
|
||||
self.stubs.Set(CoraidRESTClient, '_configure',
|
||||
lambda *_: fake_esm_success)
|
||||
self.drv.delete_snapshot(fake_volume_name)
|
||||
|
||||
def test_create_volume_from_snapshot(self):
|
||||
setattr(self.rest_mock, 'create_volume_from_snapshot',
|
||||
lambda *_: True)
|
||||
self.stubs.Set(CoraidRESTClient, '_get_volume_info',
|
||||
lambda *_: fake_volume_info)
|
||||
self.stubs.Set(CoraidRESTClient, '_configure',
|
||||
lambda *_: fake_esm_success)
|
||||
self.drv.create_volume_from_snapshot(fake_volume_name,
|
||||
fake_volume_name,
|
||||
fake_repository_name)
|
|
@ -0,0 +1,388 @@
|
|||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
#
|
||||
# Copyright 2012 Alyseo.
|
||||
# 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.
|
||||
"""
|
||||
Desc : Driver to store volumes on Coraid Appliances.
|
||||
Require : Coraid EtherCloud ESM, Coraid VSX and Coraid SRX.
|
||||
Author : Jean-Baptiste RANSY <openstack@alyseo.com>
|
||||
"""
|
||||
|
||||
from cinder import context
|
||||
from cinder import exception
|
||||
from cinder import flags
|
||||
from cinder.openstack.common import cfg
|
||||
from cinder.openstack.common import jsonutils
|
||||
from cinder.openstack.common import log as logging
|
||||
from cinder.volume import driver
|
||||
from cinder.volume import volume_types
|
||||
|
||||
import cookielib
|
||||
import os
|
||||
import time
|
||||
import urllib2
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
FLAGS = flags.FLAGS
|
||||
coraid_opts = [
|
||||
cfg.StrOpt('coraid_esm_address',
|
||||
default='',
|
||||
help='IP address of Coraid ESM'),
|
||||
cfg.StrOpt('coraid_user',
|
||||
default='admin',
|
||||
help='User name to connect to Coraid ESM'),
|
||||
cfg.StrOpt('coraid_password',
|
||||
default='password',
|
||||
help='Password to connect to Coraid ESM'),
|
||||
cfg.StrOpt('coraid_repository_key',
|
||||
default='coraid_repository',
|
||||
help='Volume Type key name to store ESM Repository Name'),
|
||||
]
|
||||
FLAGS.register_opts(coraid_opts)
|
||||
|
||||
|
||||
class CoraidException(Exception):
|
||||
def __init__(self, message=None, error=None):
|
||||
super(CoraidException, self).__init__(message, error)
|
||||
|
||||
def __str__(self):
|
||||
return '%s: %s' % self.args
|
||||
|
||||
|
||||
class CoraidRESTException(CoraidException):
|
||||
pass
|
||||
|
||||
|
||||
class CoraidESMException(CoraidException):
|
||||
pass
|
||||
|
||||
|
||||
class CoraidRESTClient(object):
|
||||
"""Executes volume driver commands on Coraid ESM EtherCloud Appliance."""
|
||||
|
||||
def __init__(self, ipaddress, user, password):
|
||||
self.url = "https://%s:8443/" % ipaddress
|
||||
self.user = user
|
||||
self.password = password
|
||||
self.session = False
|
||||
self.cookiejar = cookielib.CookieJar()
|
||||
self.urlOpener = urllib2.build_opener(
|
||||
urllib2.HTTPCookieProcessor(self.cookiejar))
|
||||
LOG.debug(_('Running with CoraidDriver for ESM EtherCLoud'))
|
||||
self._login()
|
||||
|
||||
def _login(self):
|
||||
"""Login and Session Handler."""
|
||||
if not self.session or self.session < time.time():
|
||||
url = ('admin?op=login&username=%s&password=%s' %
|
||||
(self.user, self.password))
|
||||
data = 'Login'
|
||||
reply = self._esm(url, data)
|
||||
if reply.get('state') == 'adminSucceed':
|
||||
self.session = time.time() + 1100
|
||||
msg = _('Update session cookie %(session)s')
|
||||
LOG.debug(msg % dict(session=self.session))
|
||||
return True
|
||||
else:
|
||||
errmsg = response.get('message', '')
|
||||
msg = _('Message : %(message)s')
|
||||
raise CoraidESMException(msg % dict(message=errmsg))
|
||||
return True
|
||||
|
||||
def _esm(self, url=False, data=None):
|
||||
"""
|
||||
_esm represent the entry point to send requests to ESM Appliance.
|
||||
Send the HTTPS call, get response in JSON
|
||||
convert response into Python Object and return it.
|
||||
"""
|
||||
if url:
|
||||
url = self.url + url
|
||||
|
||||
req = urllib2.Request(url, data)
|
||||
|
||||
try:
|
||||
res = self.urlOpener.open(req).read()
|
||||
except Exception:
|
||||
raise CoraidRESTException(_('ESM urlOpen error'))
|
||||
|
||||
try:
|
||||
res_json = jsonutils.loads(res)
|
||||
except Exception:
|
||||
raise CoraidRESTException(_('JSON Error'))
|
||||
|
||||
return res_json
|
||||
else:
|
||||
raise CoraidRESTException(_('Request without URL'))
|
||||
|
||||
def _configure(self, data):
|
||||
"""In charge of all commands into 'configure'."""
|
||||
self._login()
|
||||
url = 'configure'
|
||||
LOG.debug(_('Configure data : %s'), data)
|
||||
response = self._esm(url, data)
|
||||
LOG.debug(_("Configure response : %s"), response)
|
||||
if response:
|
||||
if response.get('configState') == 'completedSuccessfully':
|
||||
return True
|
||||
else:
|
||||
errmsg = response.get('message', '')
|
||||
msg = _('Message : %(message)s')
|
||||
raise CoraidESMException(msg % dict(message=errmsg))
|
||||
return False
|
||||
|
||||
def _get_volume_info(self, lvname):
|
||||
"""Fetch information for a given Volume or Snapshot."""
|
||||
self._login()
|
||||
url = 'fetch?shelf=cms&orchStrRepo&lv=%s' % (lvname)
|
||||
response = self._esm(url)
|
||||
|
||||
items = []
|
||||
for cmd, reply in response:
|
||||
if len(reply['reply']) != 0:
|
||||
items.append(reply['reply'])
|
||||
|
||||
volume_info = False
|
||||
for item in items[0]:
|
||||
if item['lv']['name'] == lvname:
|
||||
volume_info = {
|
||||
"pool": item['lv']['containingPool'],
|
||||
"repo": item['repoName'],
|
||||
"vsxidx": item['lv']['lunIndex'],
|
||||
"index": item['lv']['lvStatus']['exportedLun']['lun'],
|
||||
"shelf": item['lv']['lvStatus']['exportedLun']['shelf']}
|
||||
|
||||
if volume_info:
|
||||
return volume_info
|
||||
else:
|
||||
msg = _('Informtion about Volume %(volname)s not found')
|
||||
raise CoraidESMException(msg % dict(volname=volume_name))
|
||||
|
||||
def _get_lun_address(self, volume_name):
|
||||
"""Return AoE Address for a given Volume."""
|
||||
volume_info = self._get_volume_info(volume_name)
|
||||
shelf = volume_info['shelf']
|
||||
lun = volume_info['index']
|
||||
return {'shelf': shelf, 'lun': lun}
|
||||
|
||||
def create_lun(self, volume_name, volume_size, repository):
|
||||
"""Create LUN on Coraid Backend Storage."""
|
||||
data = '[{"addr":"cms","data":"{' \
|
||||
'\\"servers\\":[\\"\\"],' \
|
||||
'\\"repoName\\":\\"%s\\",' \
|
||||
'\\"size\\":\\"%sG\\",' \
|
||||
'\\"lvName\\":\\"%s\\"}",' \
|
||||
'"op":"orchStrLun",' \
|
||||
'"args":"add"}]' % (repository, volume_size,
|
||||
volume_name)
|
||||
return self._configure(data)
|
||||
|
||||
def delete_lun(self, volume_name):
|
||||
"""Delete LUN."""
|
||||
volume_info = self._get_volume_info(volume_name)
|
||||
repository = volume_info['repo']
|
||||
data = '[{"addr":"cms","data":"{' \
|
||||
'\\"repoName\\":\\"%s\\",' \
|
||||
'\\"lvName\\":\\"%s\\"}",' \
|
||||
'"op":"orchStrLun/verified",' \
|
||||
'"args":"delete"}]' % (repository, volume_name)
|
||||
return self._configure(data)
|
||||
|
||||
def create_snapshot(self, volume_name, snapshot_name):
|
||||
"""Create Snapshot."""
|
||||
volume_info = self._get_volume_info(volume_name)
|
||||
repository = volume_info['repo']
|
||||
data = '[{"addr":"cms","data":"{' \
|
||||
'\\"repoName\\":\\"%s\\",' \
|
||||
'\\"lvName\\":\\"%s\\",' \
|
||||
'\\"newLvName\\":\\"%s\\"}",' \
|
||||
'"op":"orchStrLunMods",' \
|
||||
'"args":"addClSnap"}]' % (repository, volume_name,
|
||||
snapshot_name)
|
||||
return self._configure(data)
|
||||
|
||||
def delete_snapshot(self, snapshot_name):
|
||||
"""Delete Snapshot."""
|
||||
snapshot_info = self._get_volume_info(snapshot_name)
|
||||
repository = snapshot_info['repo']
|
||||
data = '[{"addr":"cms","data":"{' \
|
||||
'\\"repoName\\":\\"%s\\",' \
|
||||
'\\"lvName\\":\\"%s\\"}",' \
|
||||
'"op":"orchStrLunMods",' \
|
||||
'"args":"delClSnap"}]' % (repository, snapshot_name)
|
||||
return self._configure(data)
|
||||
|
||||
def create_volume_from_snapshot(self, snapshot_name,
|
||||
volume_name, repository):
|
||||
"""Create a LUN from a Snapshot."""
|
||||
snapshot_info = self._get_volume_info(snapshot_name)
|
||||
snapshot_repo = snapshot_info['repo']
|
||||
data = '[{"addr":"cms","data":"{' \
|
||||
'\\"lvName\\":\\"%s\\",' \
|
||||
'\\"repoName\\":\\"%s\\",' \
|
||||
'\\"newLvName\\":\\"%s\\",' \
|
||||
'\\"newRepoName\\":\\"%s\\"}",' \
|
||||
'"op":"orchStrLunMods",' \
|
||||
'"args":"addClone"}]' % (snapshot_name, snapshot_repo,
|
||||
volume_name, repository)
|
||||
return self._configure(data)
|
||||
|
||||
|
||||
class CoraidDriver(driver.VolumeDriver):
|
||||
"""This is the Class to set in cinder.conf (volume_driver)."""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(CoraidDriver, self).__init__(*args, **kwargs)
|
||||
|
||||
def do_setup(self, context):
|
||||
"""Initialize the volume driver."""
|
||||
self.esm = CoraidRESTClient(FLAGS.coraid_esm_address,
|
||||
FLAGS.coraid_user,
|
||||
FLAGS.coraid_password)
|
||||
|
||||
def check_for_setup_error(self):
|
||||
"""Return an error if prerequisites aren't met."""
|
||||
if not self.esm._login():
|
||||
raise LookupError(_("Cannot login on Coraid ESM"))
|
||||
|
||||
def _get_repository(self, volume_type):
|
||||
"""
|
||||
Return the ESM Repository from the Volume Type.
|
||||
The ESM Repository is stored into a volume_type_extra_specs key.
|
||||
"""
|
||||
volume_type_id = volume_type['id']
|
||||
repository_key_name = FLAGS.coraid_repository_key
|
||||
repository = volume_types.get_volume_type_extra_specs(
|
||||
volume_type_id, repository_key_name)
|
||||
return repository
|
||||
|
||||
def create_volume(self, volume):
|
||||
"""Create a Volume."""
|
||||
try:
|
||||
repository = self._get_repository(volume['volume_type'])
|
||||
self.esm.create_lun(volume['name'], volume['size'], repository)
|
||||
except Exception:
|
||||
msg = _('Fail to create volume %(volname)s')
|
||||
LOG.debug(msg % dict(volname=volume['name']))
|
||||
raise
|
||||
# NOTE(jbr_): The manager currently interprets any return as
|
||||
# being the model_update for provider location.
|
||||
# return None to not break it (thank to jgriffith and DuncanT)
|
||||
return
|
||||
|
||||
def delete_volume(self, volume):
|
||||
"""Delete a Volume."""
|
||||
try:
|
||||
self.esm.delete_lun(volume['name'])
|
||||
except Exception:
|
||||
msg = _('Failed to delete volume %(volname)s')
|
||||
LOG.debug(msg % dict(volname=volume['name']))
|
||||
raise
|
||||
return
|
||||
|
||||
def create_snapshot(self, snapshot):
|
||||
"""Create a Snapshot."""
|
||||
try:
|
||||
volume_name = FLAGS.volume_name_template % snapshot['volume_id']
|
||||
snapshot_name = FLAGS.snapshot_name_template % snapshot['id']
|
||||
self.esm.create_snapshot(volume_name, snapshot_name)
|
||||
except Exception:
|
||||
msg = _('Failed to Create Snapshot %(snapname)s')
|
||||
LOG.debug(msg % dict(snapname=snapshot_name))
|
||||
raise
|
||||
return
|
||||
|
||||
def delete_snapshot(self, snapshot):
|
||||
"""Delete a Snapshot."""
|
||||
try:
|
||||
snapshot_name = FLAGS.snapshot_name_template % snapshot['id']
|
||||
self.esm.delete_snapshot(snapshot_name)
|
||||
except Exception:
|
||||
msg = _('Failed to Delete Snapshot %(snapname)s')
|
||||
LOG.debug(msg % dict(snapname=snapshot_name))
|
||||
raise
|
||||
return
|
||||
|
||||
def create_volume_from_snapshot(self, volume, snapshot):
|
||||
"""Create a Volume from a Snapshot."""
|
||||
try:
|
||||
snapshot_name = FLAGS.snapshot_name_template % snapshot['id']
|
||||
repository = self._get_repository(volume['volume_type'])
|
||||
self.esm.create_volume_from_snapshot(snapshot_name,
|
||||
volume['name'],
|
||||
repository)
|
||||
except Exception:
|
||||
msg = _('Failed to Create Volume from Snapshot %(snapname)s')
|
||||
LOG.debug(msg % dict(snapname=snapshot_name))
|
||||
raise
|
||||
return
|
||||
|
||||
def initialize_connection(self, volume, connector):
|
||||
"""Return connection information."""
|
||||
try:
|
||||
infos = self.esm._get_lun_address(volume['name'])
|
||||
shelf = infos['shelf']
|
||||
lun = infos['lun']
|
||||
|
||||
aoe_properties = {
|
||||
'target_shelf': shelf,
|
||||
'target_lun': lun,
|
||||
}
|
||||
return {
|
||||
'driver_volume_type': 'aoe',
|
||||
'data': aoe_properties,
|
||||
}
|
||||
except Exception:
|
||||
msg = _('Failed to Initialize Connection. '
|
||||
'Volume Name: %(volname)s '
|
||||
'Shelf: %(shelf)s, '
|
||||
'Lun: %(lun)s')
|
||||
LOG.debug(msg % dict(volname=volume['name'],
|
||||
shelf=shelf,
|
||||
lun=lun))
|
||||
raise
|
||||
return
|
||||
|
||||
def get_volume_stats(self, refresh=False):
|
||||
"""Return Volume Stats."""
|
||||
return {'driver_version': '1.0',
|
||||
'free_capacity_gb': 'unknown',
|
||||
'reserved_percentage': 0,
|
||||
'storage_protocol': 'aoe',
|
||||
'total_capacity_gb': 'unknown',
|
||||
'vendor_name': 'Coraid',
|
||||
'volume_backend_name': 'EtherCloud ESM'}
|
||||
|
||||
def local_path(self, volume):
|
||||
pass
|
||||
|
||||
def create_export(self, context, volume):
|
||||
pass
|
||||
|
||||
def remove_export(self, context, volume):
|
||||
pass
|
||||
|
||||
def terminate_connection(self, volume, connector, **kwargs):
|
||||
pass
|
||||
|
||||
def ensure_export(self, context, volume):
|
||||
pass
|
||||
|
||||
def attach_volume(self, context, volume, instance_uuid, mountpoint):
|
||||
pass
|
||||
|
||||
def detach_volume(self, context, volume):
|
||||
pass
|
|
@ -143,3 +143,16 @@ def is_key_value_present(volume_type_id, key, value, volume_type=None):
|
|||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
|
||||
def get_volume_type_extra_specs(volume_type_id, key=False):
|
||||
volume_type = get_volume_type(context.get_admin_context(),
|
||||
volume_type_id)
|
||||
extra_specs = volume_type['extra_specs']
|
||||
if key:
|
||||
if extra_specs.get(key):
|
||||
return extra_specs.get(key)
|
||||
else:
|
||||
return False
|
||||
else:
|
||||
return extra_specs
|
||||
|
|
Loading…
Reference in New Issue