NetApp unified driver implementation.
NetApp has growing number of multiple drivers depending on storage families and technologies. NetApp unified driver simplifies configuration and provides single entry point for all storage technologies/drivers. It provides new mechanism to support multiple NetApp technologies and block drivers related to them. Deprecated 7mode dfm and c mode webservice based drivers. blueprint netapp-unified-driver Change-Id: Ife3cb12ef2256e0124c9a02a968c20a580b5df93
This commit is contained in:
parent
d6df6ce398
commit
d92f73e04d
|
@ -29,10 +29,6 @@ NEXENTA_MODULE = "cinder.volume.drivers.nexenta.volume.NexentaDriver"
|
|||
SAN_MODULE = "cinder.volume.drivers.san.san.SanISCSIDriver"
|
||||
SOLARIS_MODULE = "cinder.volume.drivers.san.solaris.SolarisISCSIDriver"
|
||||
LEFTHAND_MODULE = "cinder.volume.drivers.san.hp_lefthand.HpSanISCSIDriver"
|
||||
NETAPP_MODULE = "cinder.volume.drivers.netapp.iscsi.NetAppISCSIDriver"
|
||||
NETAPP_CMODE_MODULE =\
|
||||
"cinder.volume.drivers.netapp.iscsi.NetAppCmodeISCSIDriver"
|
||||
NETAPP_NFS_MODULE = "cinder.volume.drivers.netapp.nfs.NetAppNFSDriver"
|
||||
NFS_MODULE = "cinder.volume.drivers.nfs.NfsDriver"
|
||||
SOLIDFIRE_MODULE = "cinder.volume.drivers.solidfire.SolidFire"
|
||||
STORWIZE_SVC_MODULE = "cinder.volume.drivers.storwize_svc.StorwizeSVCDriver"
|
||||
|
@ -114,30 +110,6 @@ class VolumeDriverCompatibility(test.TestCase):
|
|||
self._load_driver(LEFTHAND_MODULE)
|
||||
self.assertEquals(self._driver_module_name(), LEFTHAND_MODULE)
|
||||
|
||||
def test_netapp_old(self):
|
||||
self._load_driver('cinder.volume.netapp.NetAppISCSIDriver')
|
||||
self.assertEquals(self._driver_module_name(), NETAPP_MODULE)
|
||||
|
||||
def test_netapp_new(self):
|
||||
self._load_driver(NETAPP_MODULE)
|
||||
self.assertEquals(self._driver_module_name(), NETAPP_MODULE)
|
||||
|
||||
def test_netapp_cmode_old(self):
|
||||
self._load_driver('cinder.volume.netapp.NetAppCmodeISCSIDriver')
|
||||
self.assertEquals(self._driver_module_name(), NETAPP_CMODE_MODULE)
|
||||
|
||||
def test_netapp_cmode_new(self):
|
||||
self._load_driver(NETAPP_CMODE_MODULE)
|
||||
self.assertEquals(self._driver_module_name(), NETAPP_CMODE_MODULE)
|
||||
|
||||
def test_netapp_nfs_old(self):
|
||||
self._load_driver('cinder.volume.netapp_nfs.NetAppNFSDriver')
|
||||
self.assertEquals(self._driver_module_name(), NETAPP_NFS_MODULE)
|
||||
|
||||
def test_netapp_nfs_new(self):
|
||||
self._load_driver(NETAPP_NFS_MODULE)
|
||||
self.assertEquals(self._driver_module_name(), NETAPP_NFS_MODULE)
|
||||
|
||||
def test_nfs_old(self):
|
||||
self._load_driver('cinder.volume.nfs.NfsDriver')
|
||||
self.assertEquals(self._driver_module_name(), NFS_MODULE)
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -14,7 +14,7 @@
|
|||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
"""Unit tests for the NetApp-specific NFS driver module (netapp_nfs)."""
|
||||
"""Unit tests for the NetApp-specific NFS driver module."""
|
||||
|
||||
from cinder import context
|
||||
from cinder import exception
|
||||
|
@ -23,15 +23,12 @@ from cinder import test
|
|||
from cinder.volume import configuration as conf
|
||||
from cinder.volume.drivers.netapp import api
|
||||
from cinder.volume.drivers.netapp import nfs as netapp_nfs
|
||||
from cinder.volume.drivers import nfs
|
||||
from lxml import etree
|
||||
from mox import IgnoreArg
|
||||
from mox import IsA
|
||||
from mox import MockObject
|
||||
|
||||
import mox
|
||||
import suds
|
||||
import types
|
||||
|
||||
|
||||
def create_configuration():
|
||||
|
@ -74,63 +71,11 @@ class FakeResponce(object):
|
|||
self.Reason = 'Sample error'
|
||||
|
||||
|
||||
class NetappNfsDriverTestCase(test.TestCase):
|
||||
"""Test case for NetApp specific NFS clone driver."""
|
||||
|
||||
class NetappDirectCmodeNfsDriverTestCase(test.TestCase):
|
||||
"""Test direct NetApp C Mode driver."""
|
||||
def setUp(self):
|
||||
super(NetappNfsDriverTestCase, self).setUp()
|
||||
|
||||
self._driver = netapp_nfs.NetAppNFSDriver(
|
||||
configuration=create_configuration())
|
||||
|
||||
def test_check_for_setup_error(self):
|
||||
mox = self.mox
|
||||
drv = self._driver
|
||||
required_flags = ['netapp_wsdl_url',
|
||||
'netapp_login',
|
||||
'netapp_password',
|
||||
'netapp_server_hostname',
|
||||
'netapp_server_port']
|
||||
|
||||
# set required flags
|
||||
for flag in required_flags:
|
||||
setattr(drv.configuration, flag, None)
|
||||
|
||||
# check exception raises when flags are not set
|
||||
self.assertRaises(exception.CinderException,
|
||||
drv.check_for_setup_error)
|
||||
|
||||
# set required flags
|
||||
for flag in required_flags:
|
||||
setattr(drv.configuration, flag, 'val')
|
||||
|
||||
mox.StubOutWithMock(nfs.NfsDriver, 'check_for_setup_error')
|
||||
nfs.NfsDriver.check_for_setup_error()
|
||||
mox.ReplayAll()
|
||||
|
||||
drv.check_for_setup_error()
|
||||
|
||||
mox.VerifyAll()
|
||||
|
||||
# restore initial FLAGS
|
||||
for flag in required_flags:
|
||||
delattr(drv.configuration, flag)
|
||||
|
||||
def test_do_setup(self):
|
||||
mox = self.mox
|
||||
drv = self._driver
|
||||
|
||||
mox.StubOutWithMock(drv, 'check_for_setup_error')
|
||||
mox.StubOutWithMock(drv, '_get_client')
|
||||
|
||||
drv.check_for_setup_error()
|
||||
drv._get_client()
|
||||
|
||||
mox.ReplayAll()
|
||||
|
||||
drv.do_setup(IsA(context.RequestContext))
|
||||
|
||||
mox.VerifyAll()
|
||||
super(NetappDirectCmodeNfsDriverTestCase, self).setUp()
|
||||
self._custom_setup()
|
||||
|
||||
def test_create_snapshot(self):
|
||||
"""Test snapshot can be created and deleted."""
|
||||
|
@ -174,212 +119,6 @@ class NetappNfsDriverTestCase(test.TestCase):
|
|||
|
||||
mox.VerifyAll()
|
||||
|
||||
def _prepare_delete_snapshot_mock(self, snapshot_exists):
|
||||
drv = self._driver
|
||||
mox = self.mox
|
||||
|
||||
mox.StubOutWithMock(drv, '_get_provider_location')
|
||||
mox.StubOutWithMock(drv, '_volume_not_present')
|
||||
|
||||
if snapshot_exists:
|
||||
mox.StubOutWithMock(drv, '_execute')
|
||||
mox.StubOutWithMock(drv, '_get_volume_path')
|
||||
|
||||
drv._get_provider_location(IgnoreArg())
|
||||
drv._volume_not_present(IgnoreArg(),
|
||||
IgnoreArg()).AndReturn(not snapshot_exists)
|
||||
|
||||
if snapshot_exists:
|
||||
drv._get_volume_path(IgnoreArg(), IgnoreArg())
|
||||
drv._execute('rm', None, run_as_root=True)
|
||||
|
||||
mox.ReplayAll()
|
||||
|
||||
return mox
|
||||
|
||||
def test_delete_existing_snapshot(self):
|
||||
drv = self._driver
|
||||
mox = self._prepare_delete_snapshot_mock(True)
|
||||
|
||||
drv.delete_snapshot(FakeSnapshot())
|
||||
|
||||
mox.VerifyAll()
|
||||
|
||||
def test_delete_missing_snapshot(self):
|
||||
drv = self._driver
|
||||
mox = self._prepare_delete_snapshot_mock(False)
|
||||
|
||||
drv.delete_snapshot(FakeSnapshot())
|
||||
|
||||
mox.VerifyAll()
|
||||
|
||||
def _prepare_clone_mock(self, status):
|
||||
drv = self._driver
|
||||
mox = self.mox
|
||||
|
||||
volume = FakeVolume()
|
||||
setattr(volume, 'provider_location', '127.0.0.1:/nfs')
|
||||
|
||||
drv._client = MockObject(suds.client.Client)
|
||||
drv._client.factory = MockObject(suds.client.Factory)
|
||||
drv._client.service = MockObject(suds.client.ServiceSelector)
|
||||
|
||||
# ApiProxy() method is generated by ServiceSelector at runtime from the
|
||||
# XML, so mocking is impossible.
|
||||
setattr(drv._client.service,
|
||||
'ApiProxy',
|
||||
types.MethodType(lambda *args, **kwargs: FakeResponce(status),
|
||||
suds.client.ServiceSelector))
|
||||
mox.StubOutWithMock(drv, '_get_host_id')
|
||||
mox.StubOutWithMock(drv, '_get_full_export_path')
|
||||
|
||||
drv._get_host_id(IgnoreArg()).AndReturn('10')
|
||||
drv._get_full_export_path(IgnoreArg(), IgnoreArg()).AndReturn('/nfs')
|
||||
|
||||
return mox
|
||||
|
||||
def test_successfull_clone_volume(self):
|
||||
drv = self._driver
|
||||
mox = self._prepare_clone_mock('passed')
|
||||
# set required flags
|
||||
setattr(drv.configuration, 'synchronous_snapshot_create', False)
|
||||
mox.ReplayAll()
|
||||
|
||||
volume_name = 'volume_name'
|
||||
clone_name = 'clone_name'
|
||||
volume_id = volume_name + str(hash(volume_name))
|
||||
|
||||
drv._clone_volume(volume_name, clone_name, volume_id)
|
||||
|
||||
mox.VerifyAll()
|
||||
|
||||
def test_failed_clone_volume(self):
|
||||
drv = self._driver
|
||||
mox = self._prepare_clone_mock('failed')
|
||||
|
||||
mox.ReplayAll()
|
||||
|
||||
volume_name = 'volume_name'
|
||||
clone_name = 'clone_name'
|
||||
volume_id = volume_name + str(hash(volume_name))
|
||||
|
||||
self.assertRaises(exception.CinderException,
|
||||
drv._clone_volume,
|
||||
volume_name, clone_name, volume_id)
|
||||
|
||||
mox.VerifyAll()
|
||||
|
||||
def test_cloned_volume_size_fail(self):
|
||||
volume_clone_fail = FakeVolume(1)
|
||||
volume_src = FakeVolume(2)
|
||||
try:
|
||||
self._driver.create_cloned_volume(volume_clone_fail,
|
||||
volume_src)
|
||||
raise AssertionError()
|
||||
except exception.CinderException:
|
||||
pass
|
||||
|
||||
|
||||
class NetappCmodeNfsDriverTestCase(test.TestCase):
|
||||
"""Test case for NetApp C Mode specific NFS clone driver"""
|
||||
|
||||
def setUp(self):
|
||||
super(NetappCmodeNfsDriverTestCase, self).setUp()
|
||||
self._custom_setup()
|
||||
|
||||
def _custom_setup(self):
|
||||
self._driver = netapp_nfs.NetAppCmodeNfsDriver(
|
||||
configuration=create_configuration())
|
||||
|
||||
def test_check_for_setup_error(self):
|
||||
mox = self.mox
|
||||
drv = self._driver
|
||||
required_flags = [
|
||||
'netapp_wsdl_url',
|
||||
'netapp_login',
|
||||
'netapp_password',
|
||||
'netapp_server_hostname',
|
||||
'netapp_server_port']
|
||||
|
||||
# set required flags
|
||||
for flag in required_flags:
|
||||
setattr(drv.configuration, flag, None)
|
||||
# check exception raises when flags are not set
|
||||
self.assertRaises(exception.CinderException,
|
||||
drv.check_for_setup_error)
|
||||
|
||||
# set required flags
|
||||
for flag in required_flags:
|
||||
setattr(drv.configuration, flag, 'val')
|
||||
|
||||
mox.ReplayAll()
|
||||
|
||||
drv.check_for_setup_error()
|
||||
|
||||
mox.VerifyAll()
|
||||
|
||||
# restore initial FLAGS
|
||||
for flag in required_flags:
|
||||
delattr(drv.configuration, flag)
|
||||
|
||||
def test_do_setup(self):
|
||||
mox = self.mox
|
||||
drv = self._driver
|
||||
|
||||
mox.StubOutWithMock(drv, 'check_for_setup_error')
|
||||
mox.StubOutWithMock(drv, '_get_client')
|
||||
|
||||
drv.check_for_setup_error()
|
||||
drv._get_client()
|
||||
|
||||
mox.ReplayAll()
|
||||
|
||||
drv.do_setup(IsA(context.RequestContext))
|
||||
|
||||
mox.VerifyAll()
|
||||
|
||||
def test_create_snapshot(self):
|
||||
"""Test snapshot can be created and deleted"""
|
||||
mox = self.mox
|
||||
drv = self._driver
|
||||
|
||||
mox.StubOutWithMock(drv, '_clone_volume')
|
||||
drv._clone_volume(IgnoreArg(), IgnoreArg(), IgnoreArg())
|
||||
mox.ReplayAll()
|
||||
|
||||
drv.create_snapshot(FakeSnapshot())
|
||||
|
||||
mox.VerifyAll()
|
||||
|
||||
def test_create_volume_from_snapshot(self):
|
||||
"""Tests volume creation from snapshot"""
|
||||
drv = self._driver
|
||||
mox = self.mox
|
||||
volume = FakeVolume(1)
|
||||
snapshot = FakeSnapshot(2)
|
||||
|
||||
self.assertRaises(exception.CinderException,
|
||||
drv.create_volume_from_snapshot,
|
||||
volume,
|
||||
snapshot)
|
||||
|
||||
snapshot = FakeSnapshot(1)
|
||||
|
||||
location = '127.0.0.1:/nfs'
|
||||
expected_result = {'provider_location': location}
|
||||
mox.StubOutWithMock(drv, '_clone_volume')
|
||||
mox.StubOutWithMock(drv, '_get_volume_location')
|
||||
drv._clone_volume(IgnoreArg(), IgnoreArg(), IgnoreArg())
|
||||
drv._get_volume_location(IgnoreArg()).AndReturn(location)
|
||||
|
||||
mox.ReplayAll()
|
||||
|
||||
loc = drv.create_volume_from_snapshot(volume, snapshot)
|
||||
|
||||
self.assertEquals(loc, expected_result)
|
||||
|
||||
mox.VerifyAll()
|
||||
|
||||
def _prepare_delete_snapshot_mock(self, snapshot_exists):
|
||||
drv = self._driver
|
||||
mox = self.mox
|
||||
|
@ -419,44 +158,6 @@ class NetappCmodeNfsDriverTestCase(test.TestCase):
|
|||
|
||||
mox.VerifyAll()
|
||||
|
||||
def _prepare_clone_mock(self, status):
|
||||
drv = self._driver
|
||||
mox = self.mox
|
||||
|
||||
volume = FakeVolume()
|
||||
setattr(volume, 'provider_location', '127.0.0.1:/nfs')
|
||||
|
||||
drv._client = MockObject(suds.client.Client)
|
||||
drv._client.factory = MockObject(suds.client.Factory)
|
||||
drv._client.service = MockObject(suds.client.ServiceSelector)
|
||||
# CloneNasFile method is generated by ServiceSelector at runtime from
|
||||
# the
|
||||
# XML, so mocking is impossible.
|
||||
setattr(drv._client.service,
|
||||
'CloneNasFile',
|
||||
types.MethodType(lambda *args, **kwargs: FakeResponce(status),
|
||||
suds.client.ServiceSelector))
|
||||
mox.StubOutWithMock(drv, '_get_host_ip')
|
||||
mox.StubOutWithMock(drv, '_get_export_path')
|
||||
|
||||
drv._get_host_ip(IgnoreArg()).AndReturn('127.0.0.1')
|
||||
drv._get_export_path(IgnoreArg()).AndReturn('/nfs')
|
||||
return mox
|
||||
|
||||
def test_clone_volume(self):
|
||||
drv = self._driver
|
||||
mox = self._prepare_clone_mock('passed')
|
||||
|
||||
mox.ReplayAll()
|
||||
|
||||
volume_name = 'volume_name'
|
||||
clone_name = 'clone_name'
|
||||
volume_id = volume_name + str(hash(volume_name))
|
||||
|
||||
drv._clone_volume(volume_name, clone_name, volume_id)
|
||||
|
||||
mox.VerifyAll()
|
||||
|
||||
def test_cloned_volume_size_fail(self):
|
||||
volume_clone_fail = FakeVolume(1)
|
||||
volume_src = FakeVolume(2)
|
||||
|
@ -467,12 +168,12 @@ class NetappCmodeNfsDriverTestCase(test.TestCase):
|
|||
except exception.CinderException:
|
||||
pass
|
||||
|
||||
|
||||
class NetappDirectCmodeNfsDriverTestCase(NetappCmodeNfsDriverTestCase):
|
||||
"""Test direct NetApp C Mode driver"""
|
||||
def _custom_setup(self):
|
||||
kwargs = {}
|
||||
kwargs['netapp_mode'] = 'proxy'
|
||||
kwargs['configuration'] = create_configuration()
|
||||
self._driver = netapp_nfs.NetAppDirectCmodeNfsDriver(
|
||||
configuration=create_configuration())
|
||||
**kwargs)
|
||||
|
||||
def test_check_for_setup_error(self):
|
||||
mox = self.mox
|
||||
|
@ -591,7 +292,7 @@ class NetappDirectCmodeNfsDriverTestCase(NetappCmodeNfsDriverTestCase):
|
|||
|
||||
|
||||
class NetappDirect7modeNfsDriverTestCase(NetappDirectCmodeNfsDriverTestCase):
|
||||
"""Test direct NetApp C Mode driver"""
|
||||
"""Test direct NetApp C Mode driver."""
|
||||
def _custom_setup(self):
|
||||
self._driver = netapp_nfs.NetAppDirect7modeNfsDriver(
|
||||
configuration=create_configuration())
|
||||
|
|
|
@ -0,0 +1,147 @@
|
|||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright (c) 2012 NetApp, Inc.
|
||||
# Copyright (c) 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.
|
||||
"""
|
||||
Unified driver for NetApp storage systems.
|
||||
|
||||
Supports call to multiple storage systems of different families and protocols.
|
||||
"""
|
||||
|
||||
from cinder import exception
|
||||
from cinder.openstack.common import importutils
|
||||
from cinder.openstack.common import log as logging
|
||||
from cinder.volume.drivers.netapp.options import netapp_proxy_opts
|
||||
from oslo.config import cfg
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
CONF = cfg.CONF
|
||||
CONF.register_opts(netapp_proxy_opts)
|
||||
|
||||
|
||||
#NOTE(singn): Holds family:{protocol:driver} registration information.
|
||||
#Plug in new families and protocols to support new drivers.
|
||||
#No other code modification required.
|
||||
netapp_unified_plugin_registry =\
|
||||
{'ontap_cluster':
|
||||
{
|
||||
'iscsi':
|
||||
'cinder.volume.drivers.netapp.iscsi.NetAppDirectCmodeISCSIDriver',
|
||||
'nfs': 'cinder.volume.drivers.netapp.nfs.NetAppDirectCmodeNfsDriver'
|
||||
}, 'ontap_7mode':
|
||||
{
|
||||
'iscsi':
|
||||
'cinder.volume.drivers.netapp.iscsi.NetAppDirect7modeISCSIDriver',
|
||||
'nfs':
|
||||
'cinder.volume.drivers.netapp.nfs.NetAppDirect7modeNfsDriver'
|
||||
},
|
||||
}
|
||||
|
||||
#NOTE(singn): Holds family:protocol information.
|
||||
#Protocol represents the default protocol driver option
|
||||
#in case no protocol is specified by the user in configuration.
|
||||
netapp_family_default =\
|
||||
{
|
||||
'ontap_cluster': 'nfs',
|
||||
'ontap_7mode': 'nfs'
|
||||
}
|
||||
|
||||
|
||||
class NetAppDriver(object):
|
||||
""""NetApp unified block storage driver.
|
||||
|
||||
Acts as a mediator to NetApp storage drivers.
|
||||
Proxies requests based on the storage family and protocol configured.
|
||||
Override the proxy driver method by adding method in this driver.
|
||||
"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(NetAppDriver, self).__init__()
|
||||
self.configuration = kwargs.get('configuration', None)
|
||||
if self.configuration:
|
||||
self.configuration.append_config_values(netapp_proxy_opts)
|
||||
else:
|
||||
raise exception.InvalidInput(
|
||||
reason=_("Required configuration not found"))
|
||||
self.driver = NetAppDriverFactory.create_driver(
|
||||
self.configuration.netapp_storage_family,
|
||||
self.configuration.netapp_storage_protocol,
|
||||
*args, **kwargs)
|
||||
|
||||
def __setattr__(self, name, value):
|
||||
"""Sets the attribute."""
|
||||
if getattr(self, 'driver', None):
|
||||
self.driver.__setattr__(name, value)
|
||||
return
|
||||
object.__setattr__(self, name, value)
|
||||
|
||||
def __getattr__(self, name):
|
||||
""""Gets the attribute."""
|
||||
drv = object.__getattribute__(self, 'driver')
|
||||
return getattr(drv, name)
|
||||
|
||||
|
||||
class NetAppDriverFactory(object):
|
||||
"""Factory to instantiate appropriate NetApp driver."""
|
||||
|
||||
@staticmethod
|
||||
def create_driver(
|
||||
storage_family, storage_protocol, *args, **kwargs):
|
||||
""""Creates an appropriate driver based on family and protocol."""
|
||||
fmt = {'storage_family': storage_family,
|
||||
'storage_protocol': storage_protocol}
|
||||
LOG.info(_('Requested unified config: %(storage_family)s and '
|
||||
'%(storage_protocol)s') % fmt)
|
||||
storage_family = storage_family.lower()
|
||||
family_meta = netapp_unified_plugin_registry.get(storage_family)
|
||||
if not family_meta:
|
||||
raise exception.InvalidInput(
|
||||
reason=_('Storage family %s is not supported')
|
||||
% storage_family)
|
||||
if not storage_protocol:
|
||||
storage_protocol = netapp_family_default.get(storage_family)
|
||||
if not storage_protocol:
|
||||
msg_fmt = {'storage_family': storage_family}
|
||||
raise exception.InvalidInput(
|
||||
reason=_('No default storage protocol found'
|
||||
' for storage family %(storage_family)s')
|
||||
% msg_fmt)
|
||||
storage_protocol = storage_protocol.lower()
|
||||
driver_loc = family_meta.get(storage_protocol)
|
||||
if not driver_loc:
|
||||
msg_fmt = {'storage_protocol': storage_protocol,
|
||||
'storage_family': storage_family}
|
||||
raise exception.InvalidInput(
|
||||
reason=_('Protocol %(storage_protocol)s is not supported'
|
||||
' for storage family %(storage_family)s')
|
||||
% msg_fmt)
|
||||
NetAppDriverFactory.check_netapp_driver(driver_loc)
|
||||
kwargs = kwargs or {}
|
||||
kwargs['netapp_mode'] = 'proxy'
|
||||
driver = importutils.import_object(driver_loc, *args, **kwargs)
|
||||
LOG.info(_('NetApp driver of family %(storage_family)s and protocol'
|
||||
' %(storage_protocol)s loaded') % fmt)
|
||||
return driver
|
||||
|
||||
@staticmethod
|
||||
def check_netapp_driver(location):
|
||||
"""Checks if the driver requested is a netapp driver."""
|
||||
if location.find(".netapp.") == -1:
|
||||
raise exception.InvalidInput(
|
||||
reason=_("Only loading netapp drivers supported."))
|
File diff suppressed because it is too large
Load Diff
|
@ -22,52 +22,52 @@ import copy
|
|||
import os
|
||||
import time
|
||||
|
||||
from oslo.config import cfg
|
||||
import suds
|
||||
from suds.sax import text
|
||||
|
||||
from cinder import exception
|
||||
from cinder.openstack.common import log as logging
|
||||
from cinder.volume.drivers.netapp.api import NaApiError
|
||||
from cinder.volume.drivers.netapp.api import NaElement
|
||||
from cinder.volume.drivers.netapp.api import NaServer
|
||||
from cinder.volume.drivers.netapp.iscsi import netapp_opts
|
||||
from cinder.volume.drivers.netapp.options import netapp_basicauth_opts
|
||||
from cinder.volume.drivers.netapp.options import netapp_connection_opts
|
||||
from cinder.volume.drivers.netapp.options import netapp_transport_opts
|
||||
from cinder.volume.drivers.netapp.utils import provide_ems
|
||||
from cinder.volume.drivers.netapp.utils import validate_instantiation
|
||||
from cinder.volume.drivers import nfs
|
||||
from oslo.config import cfg
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
netapp_nfs_opts = [
|
||||
cfg.IntOpt('synchronous_snapshot_create',
|
||||
default=0,
|
||||
help='Does snapshot creation call returns immediately')]
|
||||
|
||||
|
||||
CONF = cfg.CONF
|
||||
CONF.register_opts(netapp_nfs_opts)
|
||||
CONF.register_opts(netapp_connection_opts)
|
||||
CONF.register_opts(netapp_transport_opts)
|
||||
CONF.register_opts(netapp_basicauth_opts)
|
||||
|
||||
|
||||
class NetAppNFSDriver(nfs.NfsDriver):
|
||||
"""Executes commands relating to Volumes."""
|
||||
"""Base class for NetApp NFS driver.
|
||||
Executes commands relating to Volumes.
|
||||
"""
|
||||
def __init__(self, *args, **kwargs):
|
||||
# NOTE(vish): db is set by Manager
|
||||
validate_instantiation(**kwargs)
|
||||
self._execute = None
|
||||
self._context = None
|
||||
super(NetAppNFSDriver, self).__init__(*args, **kwargs)
|
||||
self.configuration.append_config_values(netapp_opts)
|
||||
self.configuration.append_config_values(netapp_nfs_opts)
|
||||
self.configuration.append_config_values(netapp_connection_opts)
|
||||
self.configuration.append_config_values(netapp_basicauth_opts)
|
||||
self.configuration.append_config_values(netapp_transport_opts)
|
||||
|
||||
def set_execute(self, execute):
|
||||
self._execute = execute
|
||||
|
||||
def do_setup(self, context):
|
||||
self._context = context
|
||||
self.check_for_setup_error()
|
||||
self._client = self._get_client()
|
||||
raise NotImplementedError()
|
||||
|
||||
def check_for_setup_error(self):
|
||||
"""Returns an error if prerequisites aren't met."""
|
||||
self._check_dfm_flags()
|
||||
super(NetAppNFSDriver, self).check_for_setup_error()
|
||||
raise NotImplementedError()
|
||||
|
||||
def create_volume_from_snapshot(self, volume, snapshot):
|
||||
"""Creates a volume from a snapshot."""
|
||||
|
@ -75,10 +75,10 @@ class NetAppNFSDriver(nfs.NfsDriver):
|
|||
snap_size = snapshot.volume_size
|
||||
|
||||
if vol_size != snap_size:
|
||||
msg = (_('Cannot create volume of size %(vol_size)s from '
|
||||
'snapshot of size %(snap_size)s') %
|
||||
{'vol_size': vol_size, 'snap_size': snap_size})
|
||||
raise exception.CinderException(msg)
|
||||
msg = _('Cannot create volume of size %(vol_size)s from '
|
||||
'snapshot of size %(snap_size)s')
|
||||
msg_fmt = {'vol_size': vol_size, 'snap_size': snap_size}
|
||||
raise exception.CinderException(msg % msg_fmt)
|
||||
|
||||
self._clone_volume(snapshot.name, volume.name, snapshot.volume_id)
|
||||
share = self._get_volume_location(snapshot.volume_id)
|
||||
|
@ -101,95 +101,22 @@ class NetAppNFSDriver(nfs.NfsDriver):
|
|||
self._execute('rm', self._get_volume_path(nfs_mount, snapshot.name),
|
||||
run_as_root=True)
|
||||
|
||||
def _check_dfm_flags(self):
|
||||
"""Raises error if any required configuration flag for OnCommand proxy
|
||||
is missing.
|
||||
"""
|
||||
required_flags = ['netapp_wsdl_url',
|
||||
'netapp_login',
|
||||
'netapp_password',
|
||||
'netapp_server_hostname',
|
||||
'netapp_server_port']
|
||||
for flag in required_flags:
|
||||
if not getattr(self.configuration, flag, None):
|
||||
raise exception.CinderException(_('%s is not set') % flag)
|
||||
|
||||
def _get_client(self):
|
||||
"""Creates SOAP _client for ONTAP-7 DataFabric Service."""
|
||||
client = suds.client.Client(
|
||||
self.configuration.netapp_wsdl_url,
|
||||
username=self.configuration.netapp_login,
|
||||
password=self.configuration.netapp_password)
|
||||
soap_url = 'http://%s:%s/apis/soap/v1' % (
|
||||
self.configuration.netapp_server_hostname,
|
||||
self.configuration.netapp_server_port)
|
||||
client.set_options(location=soap_url)
|
||||
|
||||
return client
|
||||
"""Creates client for server."""
|
||||
raise NotImplementedError()
|
||||
|
||||
def _get_volume_location(self, volume_id):
|
||||
"""Returns NFS mount address as <nfs_ip_address>:<nfs_mount_dir>"""
|
||||
"""Returns NFS mount address as <nfs_ip_address>:<nfs_mount_dir>."""
|
||||
nfs_server_ip = self._get_host_ip(volume_id)
|
||||
export_path = self._get_export_path(volume_id)
|
||||
return (nfs_server_ip + ':' + export_path)
|
||||
|
||||
def _clone_volume(self, volume_name, clone_name, volume_id):
|
||||
"""Clones mounted volume with OnCommand proxy API."""
|
||||
host_id = self._get_host_id(volume_id)
|
||||
export_path = self._get_full_export_path(volume_id, host_id)
|
||||
|
||||
request = self._client.factory.create('Request')
|
||||
request.Name = 'clone-start'
|
||||
|
||||
clone_start_args = ('<source-path>%s/%s</source-path>'
|
||||
'<destination-path>%s/%s</destination-path>')
|
||||
|
||||
request.Args = text.Raw(clone_start_args % (export_path,
|
||||
volume_name,
|
||||
export_path,
|
||||
clone_name))
|
||||
|
||||
resp = self._client.service.ApiProxy(Target=host_id,
|
||||
Request=request)
|
||||
|
||||
if (resp.Status == 'passed' and
|
||||
self.configuration.synchronous_snapshot_create):
|
||||
clone_id = resp.Results['clone-id'][0]
|
||||
clone_id_info = clone_id['clone-id-info'][0]
|
||||
clone_operation_id = int(clone_id_info['clone-op-id'][0])
|
||||
|
||||
self._wait_for_clone_finished(clone_operation_id, host_id)
|
||||
elif resp.Status == 'failed':
|
||||
raise exception.CinderException(resp.Reason)
|
||||
|
||||
def _wait_for_clone_finished(self, clone_operation_id, host_id):
|
||||
"""
|
||||
Polls ONTAP7 for clone status. Returns once clone is finished.
|
||||
:param clone_operation_id: Identifier of ONTAP clone operation
|
||||
"""
|
||||
clone_list_options = ('<clone-id>'
|
||||
'<clone-id-info>'
|
||||
'<clone-op-id>%d</clone-op-id>'
|
||||
'<volume-uuid></volume-uuid>'
|
||||
'</clone-id>'
|
||||
'</clone-id-info>')
|
||||
|
||||
request = self._client.factory.create('Request')
|
||||
request.Name = 'clone-list-status'
|
||||
request.Args = text.Raw(clone_list_options % clone_operation_id)
|
||||
|
||||
resp = self._client.service.ApiProxy(Target=host_id, Request=request)
|
||||
|
||||
while resp.Status != 'passed':
|
||||
time.sleep(1)
|
||||
resp = self._client.service.ApiProxy(Target=host_id,
|
||||
Request=request)
|
||||
raise NotImplementedError()
|
||||
|
||||
def _get_provider_location(self, volume_id):
|
||||
"""
|
||||
Returns provider location for given volume
|
||||
:param volume_id:
|
||||
"""
|
||||
"""Returns provider location for given volume."""
|
||||
volume = self.db.volume_get(self._context, volume_id)
|
||||
return volume.provider_location
|
||||
|
||||
|
@ -201,38 +128,6 @@ class NetAppNFSDriver(nfs.NfsDriver):
|
|||
"""Returns NFS export path for the given volume."""
|
||||
return self._get_provider_location(volume_id).split(':')[1]
|
||||
|
||||
def _get_host_id(self, volume_id):
|
||||
"""Returns ID of the ONTAP-7 host."""
|
||||
host_ip = self._get_host_ip(volume_id)
|
||||
server = self._client.service
|
||||
|
||||
resp = server.HostListInfoIterStart(ObjectNameOrId=host_ip)
|
||||
tag = resp.Tag
|
||||
|
||||
try:
|
||||
res = server.HostListInfoIterNext(Tag=tag, Maximum=1)
|
||||
if hasattr(res, 'Hosts') and res.Hosts.HostInfo:
|
||||
return res.Hosts.HostInfo[0].HostId
|
||||
finally:
|
||||
server.HostListInfoIterEnd(Tag=tag)
|
||||
|
||||
def _get_full_export_path(self, volume_id, host_id):
|
||||
"""Returns full path to the NFS share, e.g. /vol/vol0/home."""
|
||||
export_path = self._get_export_path(volume_id)
|
||||
command_args = '<pathname>%s</pathname>'
|
||||
|
||||
request = self._client.factory.create('Request')
|
||||
request.Name = 'nfs-exportfs-storage-path'
|
||||
request.Args = text.Raw(command_args % export_path)
|
||||
|
||||
resp = self._client.service.ApiProxy(Target=host_id,
|
||||
Request=request)
|
||||
|
||||
if resp.Status == 'passed':
|
||||
return resp.Results['actual-pathname'][0]
|
||||
elif resp.Status == 'failed':
|
||||
raise exception.CinderException(resp.Reason)
|
||||
|
||||
def _volume_not_present(self, nfs_mount, volume_name):
|
||||
"""Check if volume exists."""
|
||||
try:
|
||||
|
@ -262,7 +157,8 @@ class NetAppNFSDriver(nfs.NfsDriver):
|
|||
|
||||
def _get_volume_path(self, nfs_share, volume_name):
|
||||
"""Get volume path (local fs path) for given volume name on given nfs
|
||||
share
|
||||
share.
|
||||
|
||||
@param nfs_share string, example 172.18.194.100:/var/nfs
|
||||
@param volume_name string,
|
||||
example volume-91ee65ec-c473-4391-8c09-162b00c68a8c
|
||||
|
@ -276,10 +172,10 @@ class NetAppNFSDriver(nfs.NfsDriver):
|
|||
src_vol_size = src_vref.size
|
||||
|
||||
if vol_size != src_vol_size:
|
||||
msg = (_('Cannot create clone of size %(vol_size)s from '
|
||||
'volume of size %(src_vol_size)s') %
|
||||
{'vol_size': vol_size, 'src_vol_size': src_vol_size})
|
||||
raise exception.CinderException(msg)
|
||||
msg = _('Cannot create clone of size %(vol_size)s from '
|
||||
'volume of size %(src_vol_size)s')
|
||||
msg_fmt = {'vol_size': vol_size, 'src_vol_size': src_vol_size}
|
||||
raise exception.CinderException(msg % msg_fmt)
|
||||
|
||||
self._clone_volume(src_vref.name, volume.name, src_vref.id)
|
||||
share = self._get_volume_location(src_vref.id)
|
||||
|
@ -290,71 +186,6 @@ class NetAppNFSDriver(nfs.NfsDriver):
|
|||
"""Retrieve status info from volume group."""
|
||||
super(NetAppNFSDriver, self)._update_volume_status()
|
||||
|
||||
backend_name = self.configuration.safe_get('volume_backend_name')
|
||||
self._stats["volume_backend_name"] = (backend_name or
|
||||
'NetApp_NFS_7mode')
|
||||
self._stats["vendor_name"] = 'NetApp'
|
||||
self._stats["driver_version"] = '1.0'
|
||||
|
||||
|
||||
class NetAppCmodeNfsDriver (NetAppNFSDriver):
|
||||
"""Executes commands related to volumes on c mode."""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(NetAppCmodeNfsDriver, self).__init__(*args, **kwargs)
|
||||
|
||||
def do_setup(self, context):
|
||||
self._context = context
|
||||
self.check_for_setup_error()
|
||||
self._client = self._get_client()
|
||||
|
||||
def check_for_setup_error(self):
|
||||
"""Returns an error if prerequisites aren't met."""
|
||||
self._check_flags()
|
||||
|
||||
def _clone_volume(self, volume_name, clone_name, volume_id):
|
||||
"""Clones mounted volume with NetApp Cloud Services."""
|
||||
host_ip = self._get_host_ip(volume_id)
|
||||
export_path = self._get_export_path(volume_id)
|
||||
LOG.debug(_("Cloning with params ip %(host_ip)s, exp_path"
|
||||
"%(export_path)s, vol %(volume_name)s, "
|
||||
"clone_name %(clone_name)s"),
|
||||
{'host_ip': host_ip, 'export_path': export_path,
|
||||
'volume_name': volume_name, 'clone_name': clone_name})
|
||||
self._client.service.CloneNasFile(host_ip, export_path,
|
||||
volume_name, clone_name)
|
||||
|
||||
def _check_flags(self):
|
||||
"""Raises error if any required configuration flag for NetApp Cloud
|
||||
Webservices is missing.
|
||||
"""
|
||||
required_flags = ['netapp_wsdl_url',
|
||||
'netapp_login',
|
||||
'netapp_password',
|
||||
'netapp_server_hostname',
|
||||
'netapp_server_port']
|
||||
for flag in required_flags:
|
||||
if not getattr(self.configuration, flag, None):
|
||||
raise exception.CinderException(_('%s is not set') % flag)
|
||||
|
||||
def _get_client(self):
|
||||
"""Creates SOAP _client for NetApp Cloud service."""
|
||||
client = suds.client.Client(
|
||||
self.configuration.netapp_wsdl_url,
|
||||
username=self.configuration.netapp_login,
|
||||
password=self.configuration.netapp_password)
|
||||
return client
|
||||
|
||||
def _update_volume_status(self):
|
||||
"""Retrieve status info from volume group."""
|
||||
super(NetAppCmodeNfsDriver, self)._update_volume_status()
|
||||
|
||||
backend_name = self.configuration.safe_get('volume_backend_name')
|
||||
self._stats["volume_backend_name"] = (backend_name or
|
||||
'NetApp_NFS_Cluster')
|
||||
self._stats["vendor_name"] = 'NetApp'
|
||||
self._stats["driver_version"] = '1.0'
|
||||
|
||||
|
||||
class NetAppDirectNfsDriver (NetAppNFSDriver):
|
||||
"""Executes commands related to volumes on NetApp filer."""
|
||||
|
@ -409,26 +240,10 @@ class NetAppDirectNfsDriver (NetAppNFSDriver):
|
|||
if not isinstance(elem, NaElement):
|
||||
raise ValueError('Expects NaElement')
|
||||
|
||||
def _invoke_successfully(self, na_element, vserver=None):
|
||||
"""Invoke the api for successful result.
|
||||
|
||||
If vserver is present then invokes vserver/vfiler api
|
||||
else filer/Cluster api.
|
||||
:param vserver: vserver/vfiler name.
|
||||
"""
|
||||
self._is_naelement(na_element)
|
||||
server = copy.copy(self._client)
|
||||
if vserver:
|
||||
server.set_vserver(vserver)
|
||||
else:
|
||||
server.set_vserver(None)
|
||||
result = server.invoke_successfully(na_element, True)
|
||||
return result
|
||||
|
||||
def _get_ontapi_version(self):
|
||||
"""Gets the supported ontapi version."""
|
||||
ontapi_version = NaElement('system-get-ontapi-version')
|
||||
res = self._invoke_successfully(ontapi_version, False)
|
||||
res = self._client.invoke_successfully(ontapi_version, False)
|
||||
major = res.get_child_content('major-version')
|
||||
minor = res.get_child_content('minor-version')
|
||||
return (major, minor)
|
||||
|
@ -447,6 +262,22 @@ class NetAppDirectCmodeNfsDriver (NetAppDirectNfsDriver):
|
|||
(major, minor) = self._get_ontapi_version()
|
||||
client.set_api_version(major, minor)
|
||||
|
||||
def _invoke_successfully(self, na_element, vserver=None):
|
||||
"""Invoke the api for successful result.
|
||||
|
||||
If vserver is present then invokes vserver api
|
||||
else Cluster api.
|
||||
:param vserver: vserver name.
|
||||
"""
|
||||
self._is_naelement(na_element)
|
||||
server = copy.copy(self._client)
|
||||
if vserver:
|
||||
server.set_vserver(vserver)
|
||||
else:
|
||||
server.set_vserver(None)
|
||||
result = server.invoke_successfully(na_element, True)
|
||||
return result
|
||||
|
||||
def _clone_volume(self, volume_name, clone_name, volume_id):
|
||||
"""Clones mounted volume on NetApp Cluster."""
|
||||
host_ip = self._get_host_ip(volume_id)
|
||||
|
@ -495,17 +326,18 @@ class NetAppDirectCmodeNfsDriver (NetAppDirectNfsDriver):
|
|||
vols = attr_list.get_children()
|
||||
vol_id = vols[0].get_child_by_name('volume-id-attributes')
|
||||
return vol_id.get_child_content('name')
|
||||
raise exception.NotFound(_("No volume on cluster with vserver"
|
||||
"%(vserver)s and junction path "
|
||||
"%(junction)s"), {'vserver': vserver,
|
||||
'junction': junction})
|
||||
msg_fmt = {'vserver': vserver, 'junction': junction}
|
||||
raise exception.NotFound(_("""No volume on cluster with vserver
|
||||
%(vserver)s and junction path %(junction)s
|
||||
""") % msg_fmt)
|
||||
|
||||
def _clone_file(self, volume, src_path, dest_path, vserver=None):
|
||||
"""Clones file on vserver."""
|
||||
LOG.debug(_("Cloning with params volume %(volume)s,src %(src_path)s,"
|
||||
"dest %(dest_path)s, vserver %(vserver)s"),
|
||||
{'volume': volume, 'src_path': src_path,
|
||||
'dest_path': dest_path, 'vserver': vserver})
|
||||
msg = _("""Cloning with params volume %(volume)s,src %(src_path)s,
|
||||
dest %(dest_path)s, vserver %(vserver)s""")
|
||||
msg_fmt = {'volume': volume, 'src_path': src_path,
|
||||
'dest_path': dest_path, 'vserver': vserver}
|
||||
LOG.debug(msg % msg_fmt)
|
||||
clone_create = NaElement.create_node_with_children(
|
||||
'clone-create',
|
||||
**{'volume': volume, 'source-path': src_path,
|
||||
|
@ -515,12 +347,13 @@ class NetAppDirectCmodeNfsDriver (NetAppDirectNfsDriver):
|
|||
def _update_volume_status(self):
|
||||
"""Retrieve status info from volume group."""
|
||||
super(NetAppDirectCmodeNfsDriver, self)._update_volume_status()
|
||||
|
||||
netapp_backend = 'NetApp_NFS_cluster_direct'
|
||||
backend_name = self.configuration.safe_get('volume_backend_name')
|
||||
self._stats["volume_backend_name"] = (backend_name or
|
||||
'NetApp_NFS_cluster_direct')
|
||||
netapp_backend)
|
||||
self._stats["vendor_name"] = 'NetApp'
|
||||
self._stats["driver_version"] = '1.0'
|
||||
provide_ems(self, self._client, self._stats, netapp_backend)
|
||||
|
||||
|
||||
class NetAppDirect7modeNfsDriver (NetAppDirectNfsDriver):
|
||||
|
@ -534,6 +367,22 @@ class NetAppDirect7modeNfsDriver (NetAppDirectNfsDriver):
|
|||
(major, minor) = self._get_ontapi_version()
|
||||
client.set_api_version(major, minor)
|
||||
|
||||
def _invoke_successfully(self, na_element, vfiler=None):
|
||||
"""Invoke the api for successful result.
|
||||
|
||||
If vfiler is present then invokes vfiler api
|
||||
else filer api.
|
||||
:param vfiler: vfiler name.
|
||||
"""
|
||||
self._is_naelement(na_element)
|
||||
server = copy.copy(self._client)
|
||||
if vfiler:
|
||||
server.set_vfiler(vfiler)
|
||||
else:
|
||||
server.set_vfiler(None)
|
||||
result = server.invoke_successfully(na_element, True)
|
||||
return result
|
||||
|
||||
def _clone_volume(self, volume_name, clone_name, volume_id):
|
||||
"""Clones mounted volume with NetApp filer."""
|
||||
export_path = self._get_export_path(volume_id)
|
||||
|
@ -565,8 +414,9 @@ class NetAppDirect7modeNfsDriver (NetAppDirectNfsDriver):
|
|||
|
||||
:returns: clone-id
|
||||
"""
|
||||
LOG.debug(_("Cloning with src %(src_path)s, dest %(dest_path)s"),
|
||||
{'src_path': src_path, 'dest_path': dest_path})
|
||||
msg_fmt = {'src_path': src_path, 'dest_path': dest_path}
|
||||
LOG.debug(_("""Cloning with src %(src_path)s, dest %(dest_path)s""")
|
||||
% msg_fmt)
|
||||
clone_start = NaElement.create_node_with_children(
|
||||
'clone-start',
|
||||
**{'source-path': src_path,
|
||||
|
@ -629,9 +479,11 @@ class NetAppDirect7modeNfsDriver (NetAppDirectNfsDriver):
|
|||
def _update_volume_status(self):
|
||||
"""Retrieve status info from volume group."""
|
||||
super(NetAppDirect7modeNfsDriver, self)._update_volume_status()
|
||||
|
||||
netapp_backend = 'NetApp_NFS_7mode_direct'
|
||||
backend_name = self.configuration.safe_get('volume_backend_name')
|
||||
self._stats["volume_backend_name"] = (backend_name or
|
||||
'NetApp_NFS_7mode_direct')
|
||||
self._stats["vendor_name"] = 'NetApp'
|
||||
self._stats["driver_version"] = '1.0'
|
||||
provide_ems(self, self._client, self._stats, netapp_backend,
|
||||
server_type="7mode")
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright (c) 2012 NetApp, Inc.
|
||||
# Copyright (c) 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.
|
||||
|
||||
"""Contains configuration options for NetApp drivers.
|
||||
|
||||
Common place to hold configuration options for all NetApp drivers.
|
||||
Options need to be grouped into granular units to be able to be reused
|
||||
by different modules and classes. This does not restrict declaring options in
|
||||
individual modules. If options are not re usable then can be declared in
|
||||
individual modules. It is recommended to Keep options at a single
|
||||
place to ensure re usability and better management of configuration options.
|
||||
"""
|
||||
|
||||
from oslo.config import cfg
|
||||
|
||||
netapp_proxy_opts = [
|
||||
cfg.StrOpt('netapp_storage_family',
|
||||
default='ontap_cluster',
|
||||
help='Storage family type.'),
|
||||
cfg.StrOpt('netapp_storage_protocol',
|
||||
default=None,
|
||||
help='Storage protocol type.'), ]
|
||||
|
||||
netapp_connection_opts = [
|
||||
cfg.StrOpt('netapp_server_hostname',
|
||||
default=None,
|
||||
help='Host name for the storage controller'),
|
||||
cfg.IntOpt('netapp_server_port',
|
||||
default=80,
|
||||
help='Port number for the storage controller'), ]
|
||||
|
||||
netapp_transport_opts = [
|
||||
cfg.StrOpt('netapp_transport_type',
|
||||
default='http',
|
||||
help='Transport type protocol'), ]
|
||||
|
||||
netapp_basicauth_opts = [
|
||||
cfg.StrOpt('netapp_login',
|
||||
default=None,
|
||||
help='User name for the storage controller'),
|
||||
cfg.StrOpt('netapp_password',
|
||||
default=None,
|
||||
help='Password for the storage controller',
|
||||
secret=True), ]
|
||||
|
||||
netapp_provisioning_opts = [
|
||||
cfg.FloatOpt('netapp_size_multiplier',
|
||||
default=1.2,
|
||||
help='Volume size multiplier to ensure while creation'),
|
||||
cfg.StrOpt('netapp_volume_list',
|
||||
default=None,
|
||||
help='Comma separated volumes to be used for provisioning'), ]
|
||||
|
||||
netapp_cluster_opts = [
|
||||
cfg.StrOpt('netapp_vserver',
|
||||
default='openstack',
|
||||
help='Cluster vserver to use for provisioning'), ]
|
||||
|
||||
netapp_7mode_opts = [
|
||||
cfg.StrOpt('netapp_vfiler',
|
||||
default=None,
|
||||
help='Vfiler to use for provisioning'), ]
|
|
@ -0,0 +1,120 @@
|
|||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright (c) 2012 NetApp, Inc.
|
||||
# Copyright (c) 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.
|
||||
"""
|
||||
Utilities for NetApp drivers.
|
||||
|
||||
This module contains common utilities to be used by one or more
|
||||
NetApp drivers to achieve the desired functionality.
|
||||
"""
|
||||
|
||||
import copy
|
||||
import socket
|
||||
|
||||
from cinder.openstack.common import log as logging
|
||||
from cinder.openstack.common import timeutils
|
||||
from cinder.volume.drivers.netapp.api import NaApiError
|
||||
from cinder.volume.drivers.netapp.api import NaElement
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def provide_ems(requester, server, stats, netapp_backend,
|
||||
server_type="cluster"):
|
||||
"""Provide ems with volume stats for the requester.
|
||||
|
||||
:param server_type: cluster or 7mode.
|
||||
"""
|
||||
def _create_ems(stats, netapp_backend, server_type):
|
||||
"""Create ems api request."""
|
||||
ems_log = NaElement('ems-autosupport-log')
|
||||
host = socket.getfqdn() or 'Cinder_node'
|
||||
dest = "cluster node" if server_type == "cluster"\
|
||||
else "7 mode controller"
|
||||
ems_log.add_new_child('computer-name', host)
|
||||
ems_log.add_new_child('event-id', '0')
|
||||
ems_log.add_new_child('event-source',
|
||||
'Cinder driver %s' % netapp_backend)
|
||||
ems_log.add_new_child('app-version', stats.get('driver_version',
|
||||
'Undefined'))
|
||||
ems_log.add_new_child('category', 'provisioning')
|
||||
ems_log.add_new_child('event-description',
|
||||
'OpenStack volume created on %s' % dest)
|
||||
ems_log.add_new_child('log-level', '6')
|
||||
ems_log.add_new_child('auto-support', 'true')
|
||||
return ems_log
|
||||
|
||||
def _create_vs_get():
|
||||
"""Create vs_get api request."""
|
||||
vs_get = NaElement('vserver-get-iter')
|
||||
vs_get.add_new_child('max-records', '1')
|
||||
query = NaElement('query')
|
||||
query.add_node_with_children('vserver-info',
|
||||
**{'vserver-type': 'node'})
|
||||
vs_get.add_child_elem(query)
|
||||
desired = NaElement('desired-attributes')
|
||||
desired.add_node_with_children(
|
||||
'vserver-info', **{'vserver-name': '', 'vserver-type': ''})
|
||||
vs_get.add_child_elem(desired)
|
||||
return vs_get
|
||||
|
||||
def _get_cluster_node(na_server):
|
||||
"""Get the cluster node for ems."""
|
||||
na_server.set_vserver(None)
|
||||
vs_get = _create_vs_get()
|
||||
res = na_server.invoke_successfully(vs_get)
|
||||
if (res.get_child_content('num-records') and
|
||||
int(res.get_child_content('num-records')) > 0):
|
||||
attr_list = res.get_child_by_name('attributes-list')
|
||||
vs_info = attr_list.get_child_by_name('vserver-info')
|
||||
vs_name = vs_info.get_child_content('vserver-name')
|
||||
return vs_name
|
||||
raise NaApiError(code='Not found', message='No records found')
|
||||
|
||||
do_ems = True
|
||||
if hasattr(requester, 'last_ems'):
|
||||
sec_limit = 604800
|
||||
if not (timeutils.is_older_than(requester.last_ems, sec_limit) or
|
||||
timeutils.is_older_than(requester.last_ems, sec_limit - 59)):
|
||||
do_ems = False
|
||||
if do_ems:
|
||||
na_server = copy.copy(server)
|
||||
na_server.set_timeout(25)
|
||||
ems = _create_ems(stats, netapp_backend, server_type)
|
||||
try:
|
||||
if server_type == "cluster":
|
||||
node = _get_cluster_node(na_server)
|
||||
na_server.set_vserver(node)
|
||||
else:
|
||||
na_server.set_vfiler(None)
|
||||
na_server.invoke_successfully(ems, True)
|
||||
requester.last_ems = timeutils.utcnow()
|
||||
LOG.debug(_("ems executed successfully."))
|
||||
except NaApiError as e:
|
||||
LOG.debug(_("Failed to invoke ems. Message : %s") % e)
|
||||
|
||||
|
||||
def validate_instantiation(**kwargs):
|
||||
"""Checks if a driver is instantiated other than by the unified driver.
|
||||
|
||||
Helps check direct instantiation of netapp drivers.
|
||||
Call this function in every netapp block driver constructor.
|
||||
"""
|
||||
if kwargs and kwargs.get('netapp_mode') == 'proxy':
|
||||
return
|
||||
LOG.warn(_("It is not the recommended way to use drivers by NetApp. "
|
||||
"Please use NetAppDriver to achieve the functionality."))
|
|
@ -83,12 +83,6 @@ MAPPING = {
|
|||
'cinder.volume.drivers.san.solaris.SolarisISCSIDriver',
|
||||
'cinder.volume.san.HpSanISCSIDriver':
|
||||
'cinder.volume.drivers.san.hp_lefthand.HpSanISCSIDriver',
|
||||
'cinder.volume.netapp.NetAppISCSIDriver':
|
||||
'cinder.volume.drivers.netapp.iscsi.NetAppISCSIDriver',
|
||||
'cinder.volume.netapp.NetAppCmodeISCSIDriver':
|
||||
'cinder.volume.drivers.netapp.iscsi.NetAppCmodeISCSIDriver',
|
||||
'cinder.volume.netapp_nfs.NetAppNFSDriver':
|
||||
'cinder.volume.drivers.netapp.nfs.NetAppNFSDriver',
|
||||
'cinder.volume.nfs.NfsDriver':
|
||||
'cinder.volume.drivers.nfs.NfsDriver',
|
||||
'cinder.volume.solidfire.SolidFire':
|
||||
|
|
Loading…
Reference in New Issue