Merge "NetApp unified driver implementation."
This commit is contained in:
commit
a23df3db13
@ -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())
|
||||
|
147
cinder/volume/drivers/netapp/common.py
Normal file
147
cinder/volume/drivers/netapp/common.py
Normal file
@ -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")
|
||||
|
77
cinder/volume/drivers/netapp/options.py
Normal file
77
cinder/volume/drivers/netapp/options.py
Normal file
@ -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'), ]
|
120
cinder/volume/drivers/netapp/utils.py
Normal file
120
cinder/volume/drivers/netapp/utils.py
Normal file
@ -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…
x
Reference in New Issue
Block a user