¡@

Home 

OpenStack Study: test_netapp_ssc.py

OpenStack Index

**** CubicPower OpenStack Study ****

# Copyright (c) 2012 NetApp, Inc.

# 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.

"""Unit tests for the NetApp-specific ssc module."""

import BaseHTTPServer

import copy

import httplib

from lxml import etree

from mox import IgnoreArg

import six

from cinder import exception

from cinder import test

from cinder.volume.drivers.netapp import api

from cinder.volume.drivers.netapp import ssc_utils

**** CubicPower OpenStack Study ****

class FakeHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):

"""HTTP handler that doesn't spam the log."""

**** CubicPower OpenStack Study ****

    def log_message(self, format, *args):

        pass

**** CubicPower OpenStack Study ****

class FakeHttplibSocket(object):

"""A fake socket implementation for httplib.HTTPResponse."""

**** CubicPower OpenStack Study ****

    def __init__(self, value):

        self._rbuffer = six.StringIO(value)

        self._wbuffer = six.StringIO('')

        oldclose = self._wbuffer.close

        def newclose():

            self.result = self._wbuffer.getvalue()

            oldclose()

        self._wbuffer.close = newclose

**** CubicPower OpenStack Study ****

        def newclose():

            self.result = self._wbuffer.getvalue()

            oldclose()

        self._wbuffer.close = newclose

**** CubicPower OpenStack Study ****

    def makefile(self, mode, _other):

        """Returns the socket's internal buffer"""

        if mode == 'r' or mode == 'rb':

            return self._rbuffer

        if mode == 'w' or mode == 'wb':

            return self._wbuffer

RESPONSE_PREFIX_DIRECT_CMODE = """

"""

RESPONSE_PREFIX_DIRECT = """

"""

RESPONSE_SUFFIX_DIRECT = """"""

**** CubicPower OpenStack Study ****

class FakeDirectCMODEServerHandler(FakeHTTPRequestHandler):

"""HTTP handler that fakes enough stuff to allow the driver to run."""

**** CubicPower OpenStack Study ****

    def do_GET(s):

        """Respond to a GET request."""

        if '/servlets/netapp.servlets.admin.XMLrequest_filer' not in s.path:

            s.send_response(404)

            s.end_headers

            return

        s.send_response(200)

        s.send_header("Content-Type", "text/xml; charset=utf-8")

        s.end_headers()

        out = s.wfile

        out.write(''

                  '                  ' status="failed" errno="Not_Allowed"/>')

**** CubicPower OpenStack Study ****

    def do_POST(s):

        """Respond to a POST request."""

        if '/servlets/netapp.servlets.admin.XMLrequest_filer' not in s.path:

            s.send_response(404)

            s.end_headers

            return

        request_xml = s.rfile.read(int(s.headers['Content-Length']))

        root = etree.fromstring(request_xml)

        body = [x for x in root.iterchildren()]

        request = body[0]

        tag = request.tag

        localname = etree.QName(tag).localname or tag

        if 'volume-get-iter' == localname:

            body = """

                

                

                    iscsi

                    Openstack

                    aggr0

                    

                    /iscsi

                    rw

                

                

                    214748364

                    224748364

                    enabled

                    file

                

                

                    true

                    

                    false

                    online

                    false

                    false

                    true

                

                

                

                

                    nfsvol

                    Openstack

                    

                    aggr0

                    

                    /nfs

                    rw

                

                

                    14748364

                    24748364

                    enabled

                    

                    volume

                

                

                    true

                    

                    false

                    online

                    false

                    false

                    true

                

                

                

                

                    nfsvol2

                    Openstack

                    

                    aggr0

                    

                    /nfs2

                    rw

                

                

                    14748364

                    24748364

                    enabled

                    

                    volume

                

                

                    true

                    

                    false

                    online

                    true

                    true

                    true

                

                

                

                

                    nfsvol3

                    Openstack

                    

                    aggr0

                    

                    /nfs3

                    rw

                

                

                    enabled

                    

                    volume

                    

                

                

                    true

                    

                    false

                    online

                    false

                    false

                    true

                

                

                

                4"""

        elif 'aggr-options-list-info' == localname:

                body = """

                             

                             

                             ha_policy

                             cfo

                             

                             

                             raidtype

                             raid_dp

                             

                             

                             """

        elif 'sis-get-iter' == localname:

                body = """

                             

                             

                             /vol/iscsi

                             

                             true

                             

                             enabled

                             

                             

                             """

        elif 'storage-disk-get-iter' == localname:

            body = """

                             

                             

                             

            SATA

                             

                             

                             

                             """

        else:

                # Unknown API

                s.send_response(500)

                s.end_headers

                return

        s.send_response(200)

        s.send_header("Content-Type", "text/xml; charset=utf-8")

        s.end_headers()

        s.wfile.write(RESPONSE_PREFIX_DIRECT_CMODE)

        s.wfile.write(RESPONSE_PREFIX_DIRECT)

        s.wfile.write(body)

        s.wfile.write(RESPONSE_SUFFIX_DIRECT)

**** CubicPower OpenStack Study ****

class FakeDirectCmodeHTTPConnection(object):

"""A fake httplib.HTTPConnection for netapp tests.

Requests made via this connection actually get translated and routed into

the fake direct handler above, we then turn the response into

the httplib.HTTPResponse that the caller expects.

"""

**** CubicPower OpenStack Study ****

    def __init__(self, host, timeout=None):

        self.host = host

**** CubicPower OpenStack Study ****

    def request(self, method, path, data=None, headers=None):

        if not headers:

            headers = {}

        req_str = '%s %s HTTP/1.1\r\n' % (method, path)

        for key, value in headers.iteritems():

            req_str += "%s: %s\r\n" % (key, value)

        if data:

            req_str += '\r\n%s' % data

        # NOTE(vish): normally the http transport normailizes from unicode

        sock = FakeHttplibSocket(req_str.decode("latin-1").encode("utf-8"))

        # NOTE(vish): stop the server from trying to look up address from

        #             the fake socket

        FakeDirectCMODEServerHandler.address_string = lambda x: '127.0.0.1'

        self.app = FakeDirectCMODEServerHandler(sock, '127.0.0.1:80', None)

        self.sock = FakeHttplibSocket(sock.result)

        self.http_response = httplib.HTTPResponse(self.sock)

**** CubicPower OpenStack Study ****

    def set_debuglevel(self, level):

        pass

**** CubicPower OpenStack Study ****

    def getresponse(self):

        self.http_response.begin()

        return self.http_response

**** CubicPower OpenStack Study ****

    def getresponsebody(self):

        return self.sock.result

def createNetAppVolume(**kwargs):

    vol = ssc_utils.NetAppVolume(kwargs['name'], kwargs['vs'])

    vol.state['vserver_root'] = kwargs.get('vs_root')

    vol.state['status'] = kwargs.get('status')

    vol.state['junction_active'] = kwargs.get('junc_active')

    vol.space['size_avl_bytes'] = kwargs.get('avl_byt')

    vol.space['size_total_bytes'] = kwargs.get('total_byt')

    vol.space['space-guarantee-enabled'] = kwargs.get('sg_enabled')

    vol.space['space-guarantee'] = kwargs.get('sg')

    vol.space['thin_provisioned'] = kwargs.get('thin')

    vol.mirror['mirrored'] = kwargs.get('mirrored')

    vol.qos['qos_policy_group'] = kwargs.get('qos')

    vol.aggr['name'] = kwargs.get('aggr_name')

    vol.aggr['junction'] = kwargs.get('junction')

    vol.sis['dedup'] = kwargs.get('dedup')

    vol.sis['compression'] = kwargs.get('compression')

    vol.aggr['raid_type'] = kwargs.get('raid')

    vol.aggr['ha_policy'] = kwargs.get('ha')

    vol.aggr['disk_type'] = kwargs.get('disk')

    return vol

**** CubicPower OpenStack Study ****

def createNetAppVolume(**kwargs):

    vol = ssc_utils.NetAppVolume(kwargs['name'], kwargs['vs'])

    vol.state['vserver_root'] = kwargs.get('vs_root')

    vol.state['status'] = kwargs.get('status')

    vol.state['junction_active'] = kwargs.get('junc_active')

    vol.space['size_avl_bytes'] = kwargs.get('avl_byt')

    vol.space['size_total_bytes'] = kwargs.get('total_byt')

    vol.space['space-guarantee-enabled'] = kwargs.get('sg_enabled')

    vol.space['space-guarantee'] = kwargs.get('sg')

    vol.space['thin_provisioned'] = kwargs.get('thin')

    vol.mirror['mirrored'] = kwargs.get('mirrored')

    vol.qos['qos_policy_group'] = kwargs.get('qos')

    vol.aggr['name'] = kwargs.get('aggr_name')

    vol.aggr['junction'] = kwargs.get('junction')

    vol.sis['dedup'] = kwargs.get('dedup')

    vol.sis['compression'] = kwargs.get('compression')

    vol.aggr['raid_type'] = kwargs.get('raid')

    vol.aggr['ha_policy'] = kwargs.get('ha')

    vol.aggr['disk_type'] = kwargs.get('disk')

    return vol

**** CubicPower OpenStack Study ****

class SscUtilsTestCase(test.TestCase):

"""Test ssc utis."""

vol1 = createNetAppVolume(name='vola', vs='openstack',

vs_root=False, status='online', junc_active=True,

avl_byt='1000', total_byt='1500',

sg_enabled=False,

sg='file', thin=False, mirrored=False,

qos=None, aggr_name='aggr1', junction='/vola',

dedup=False, compression=False,

raid='raiddp', ha='cfo', disk='SSD')

vol2 = createNetAppVolume(name='volb', vs='openstack',

vs_root=False, status='online', junc_active=True,

avl_byt='2000', total_byt='2500',

sg_enabled=True,

sg='file', thin=True, mirrored=False,

qos=None, aggr_name='aggr2', junction='/volb',

dedup=True, compression=False,

raid='raid4', ha='cfo', disk='SSD')

vol3 = createNetAppVolume(name='volc', vs='openstack',

vs_root=False, status='online', junc_active=True,

avl_byt='3000', total_byt='3500',

sg_enabled=True,

sg='volume', thin=True, mirrored=False,

qos=None, aggr_name='aggr1', junction='/volc',

dedup=True, compression=True,

raid='raiddp', ha='cfo', disk='SAS')

vol4 = createNetAppVolume(name='vold', vs='openstack',

vs_root=False, status='online', junc_active=True,

avl_byt='4000', total_byt='4500',

sg_enabled=False,

sg='none', thin=False, mirrored=False,

qos=None, aggr_name='aggr1', junction='/vold',

dedup=False, compression=False,

raid='raiddp', ha='cfo', disk='SSD')

vol5 = createNetAppVolume(name='vole', vs='openstack',

vs_root=False, status='online', junc_active=True,

avl_byt='5000', total_byt='5500',

sg_enabled=True,

sg='none', thin=False, mirrored=True,

qos=None, aggr_name='aggr2', junction='/vole',

dedup=True, compression=False,

raid='raid4', ha='cfo', disk='SAS')

**** CubicPower OpenStack Study ****

    def setUp(self):

        super(SscUtilsTestCase, self).setUp()

        self.stubs.Set(httplib, 'HTTPConnection',

                       FakeDirectCmodeHTTPConnection)

**** CubicPower OpenStack Study ****

    def test_cl_vols_ssc_all(self):

        """Test cluster ssc for all vols."""

        na_server = api.NaServer('127.0.0.1')

        vserver = 'openstack'

        test_vols = set([copy.deepcopy(self.vol1),

                         copy.deepcopy(self.vol2), copy.deepcopy(self.vol3)])

        sis = {'vola': {'dedup': False, 'compression': False},

               'volb': {'dedup': True, 'compression': False}}

        mirrored = {'vola': [{'dest_loc': 'openstack1:vol1',

                              'rel_type': 'data_protection',

                              'mirr_state': 'broken'},

                             {'dest_loc': 'openstack2:vol2',

                              'rel_type': 'data_protection',

                              'mirr_state': 'snapmirrored'}],

                    'volb': [{'dest_loc': 'openstack1:vol2',

                              'rel_type': 'data_protection',

                              'mirr_state': 'broken'}]}

        self.mox.StubOutWithMock(ssc_utils, 'query_cluster_vols_for_ssc')

        self.mox.StubOutWithMock(ssc_utils, 'get_sis_vol_dict')

        self.mox.StubOutWithMock(ssc_utils, 'get_snapmirror_vol_dict')

        self.mox.StubOutWithMock(ssc_utils, 'query_aggr_options')

        self.mox.StubOutWithMock(ssc_utils, 'query_aggr_storage_disk')

        ssc_utils.query_cluster_vols_for_ssc(

            na_server, vserver, None).AndReturn(test_vols)

        ssc_utils.get_sis_vol_dict(na_server, vserver, None).AndReturn(sis)

        ssc_utils.get_snapmirror_vol_dict(na_server, vserver, None).AndReturn(

            mirrored)

        raiddp = {'ha_policy': 'cfo', 'raid_type': 'raiddp'}

        ssc_utils.query_aggr_options(

            na_server, IgnoreArg()).AndReturn(raiddp)

        ssc_utils.query_aggr_storage_disk(

            na_server, IgnoreArg()).AndReturn('SSD')

        raid4 = {'ha_policy': 'cfo', 'raid_type': 'raid4'}

        ssc_utils.query_aggr_options(

            na_server, IgnoreArg()).AndReturn(raid4)

        ssc_utils.query_aggr_storage_disk(

            na_server, IgnoreArg()).AndReturn('SAS')

        self.mox.ReplayAll()

        res_vols = ssc_utils.get_cluster_vols_with_ssc(

            na_server, vserver, volume=None)

        self.mox.VerifyAll()

        for vol in res_vols:

            if vol.id['name'] == 'volc':

                self.assertEqual(vol.sis['compression'], False)

                self.assertEqual(vol.sis['dedup'], False)

            else:

                pass

**** CubicPower OpenStack Study ****

    def test_cl_vols_ssc_single(self):

        """Test cluster ssc for single vol."""

        na_server = api.NaServer('127.0.0.1')

        vserver = 'openstack'

        test_vols = set([copy.deepcopy(self.vol1)])

        sis = {'vola': {'dedup': False, 'compression': False}}

        mirrored = {'vola': [{'dest_loc': 'openstack1:vol1',

                              'rel_type': 'data_protection',

                              'mirr_state': 'broken'},

                             {'dest_loc': 'openstack2:vol2',

                              'rel_type': 'data_protection',

                              'mirr_state': 'snapmirrored'}]}

        self.mox.StubOutWithMock(ssc_utils, 'query_cluster_vols_for_ssc')

        self.mox.StubOutWithMock(ssc_utils, 'get_sis_vol_dict')

        self.mox.StubOutWithMock(ssc_utils, 'get_snapmirror_vol_dict')

        self.mox.StubOutWithMock(ssc_utils, 'query_aggr_options')

        self.mox.StubOutWithMock(ssc_utils, 'query_aggr_storage_disk')

        ssc_utils.query_cluster_vols_for_ssc(

            na_server, vserver, 'vola').AndReturn(test_vols)

        ssc_utils.get_sis_vol_dict(

            na_server, vserver, 'vola').AndReturn(sis)

        ssc_utils.get_snapmirror_vol_dict(

            na_server, vserver, 'vola').AndReturn(mirrored)

        raiddp = {'ha_policy': 'cfo', 'raid_type': 'raiddp'}

        ssc_utils.query_aggr_options(

            na_server, 'aggr1').AndReturn(raiddp)

        ssc_utils.query_aggr_storage_disk(na_server, 'aggr1').AndReturn('SSD')

        self.mox.ReplayAll()

        res_vols = ssc_utils.get_cluster_vols_with_ssc(

            na_server, vserver, volume='vola')

        self.mox.VerifyAll()

        self.assertEqual(len(res_vols), 1)

**** CubicPower OpenStack Study ****

    def test_get_cluster_ssc(self):

        """Test get cluster ssc map."""

        na_server = api.NaServer('127.0.0.1')

        vserver = 'openstack'

        test_vols = set(

            [self.vol1, self.vol2, self.vol3, self.vol4, self.vol5])

        self.mox.StubOutWithMock(ssc_utils, 'get_cluster_vols_with_ssc')

        ssc_utils.get_cluster_vols_with_ssc(

            na_server, vserver).AndReturn(test_vols)

        self.mox.ReplayAll()

        res_map = ssc_utils.get_cluster_ssc(na_server, vserver)

        self.mox.VerifyAll()

        self.assertEqual(len(res_map['mirrored']), 1)

        self.assertEqual(len(res_map['dedup']), 3)

        self.assertEqual(len(res_map['compression']), 1)

        self.assertEqual(len(res_map['thin']), 2)

        self.assertEqual(len(res_map['all']), 5)

**** CubicPower OpenStack Study ****

    def test_vols_for_boolean_specs(self):

        """Test ssc for boolean specs."""

        test_vols = set(

            [self.vol1, self.vol2, self.vol3, self.vol4, self.vol5])

        ssc_map = {'mirrored': set([self.vol1]),

                   'dedup': set([self.vol1, self.vol2, self.vol3]),

                   'compression': set([self.vol3, self.vol4]),

                   'thin': set([self.vol5, self.vol2]), 'all': test_vols}

        test_map = {'mirrored': ('netapp_mirrored', 'netapp_unmirrored'),

                    'dedup': ('netapp_dedup', 'netapp_nodedup'),

                    'compression': ('netapp_compression',

                                    'netapp_nocompression'),

                    'thin': ('netapp_thin_provisioned',

                             'netapp_thick_provisioned')}

        for type in test_map.keys():

            # type

            extra_specs = {test_map[type][0]: 'true'}

            res = ssc_utils.get_volumes_for_specs(ssc_map, extra_specs)

            self.assertEqual(len(res), len(ssc_map[type]))

            # opposite type

            extra_specs = {test_map[type][1]: 'true'}

            res = ssc_utils.get_volumes_for_specs(ssc_map, extra_specs)

            self.assertEqual(len(res), len(ssc_map['all'] - ssc_map[type]))

            # both types

            extra_specs =\

                {test_map[type][0]: 'true', test_map[type][1]: 'true'}

            res = ssc_utils.get_volumes_for_specs(ssc_map, extra_specs)

            self.assertEqual(len(res), len(ssc_map['all']))

**** CubicPower OpenStack Study ****

    def test_vols_for_optional_specs(self):

        """Test ssc for optional specs."""

        test_vols =\

            set([self.vol1, self.vol2, self.vol3, self.vol4, self.vol5])

        ssc_map = {'mirrored': set([self.vol1]),

                   'dedup': set([self.vol1, self.vol2, self.vol3]),

                   'compression': set([self.vol3, self.vol4]),

                   'thin': set([self.vol5, self.vol2]), 'all': test_vols}

        extra_specs =\

            {'netapp_dedup': 'true',

             'netapp:raid_type': 'raid4', 'netapp:disk_type': 'SSD'}

        res = ssc_utils.get_volumes_for_specs(ssc_map, extra_specs)

        self.assertEqual(len(res), 1)

**** CubicPower OpenStack Study ****

    def test_query_cl_vols_for_ssc(self):

        na_server = api.NaServer('127.0.0.1')

        na_server.set_api_version(1, 15)

        vols = ssc_utils.query_cluster_vols_for_ssc(na_server, 'Openstack')

        self.assertEqual(len(vols), 2)

        for vol in vols:

            if vol.id['name'] != 'iscsi' or vol.id['name'] != 'nfsvol':

                pass

            else:

                raise exception.InvalidVolume('Invalid volume returned.')

**** CubicPower OpenStack Study ****

    def test_query_aggr_options(self):

        na_server = api.NaServer('127.0.0.1')

        aggr_attribs = ssc_utils.query_aggr_options(na_server, 'aggr0')

        if aggr_attribs:

            self.assertEqual(aggr_attribs['ha_policy'], 'cfo')

            self.assertEqual(aggr_attribs['raid_type'], 'raid_dp')

        else:

            raise exception.InvalidParameterValue("Incorrect aggr options")

**** CubicPower OpenStack Study ****

    def test_query_aggr_storage_disk(self):

        na_server = api.NaServer('127.0.0.1')

        eff_disk_type = ssc_utils.query_aggr_storage_disk(na_server, 'aggr0')

        self.assertEqual(eff_disk_type, 'SATA')