Skip to content

Commit

Permalink
[GH-7] Add VNX host list support.
Browse files Browse the repository at this point in the history
Analyse host list from the output of storage group.
Create new resource `VNXHost` and `VNXHostList`.

Host contains only two property, the name of the host and the hba
connections(path) related to this host.

Pop the version to 0.2.13.
  • Loading branch information
Cedric Zhuang committed Jul 1, 2016
1 parent 494a361 commit f538f1c
Show file tree
Hide file tree
Showing 20 changed files with 521 additions and 123 deletions.
3 changes: 2 additions & 1 deletion README.rst
Expand Up @@ -10,7 +10,7 @@ StorOps: The Python Library for VNX & Unity
.. image:: https://img.shields.io/pypi/v/storops.svg
:target: https://pypi.python.org/pypi/storops

VERSION: 0.2.12
VERSION: 0.2.13

A minimalist Python library to manage VNX/Unity systems.
This document lies in the source code and go with the release.
Expand Down Expand Up @@ -56,6 +56,7 @@ Feature List
- list/create/delete file system snap
- list/create/delete NFS share
- show system domain information
- list hosts
- supported feature/operations
- list/start/cancel migration sessions
- enable/disable LUN deduplication
Expand Down
3 changes: 1 addition & 2 deletions comptest/__init__.py
Expand Up @@ -17,9 +17,8 @@

import logging

from comptest.utils import setup_log
from comptest.utils import setup_log, inter_process_locked
from storops import VNXSystem, UnitySystem, cache
from storops.lib.common import inter_process_locked

__author__ = 'Cedric Zhuang'

Expand Down
1 change: 0 additions & 1 deletion comptest/unity/__init__.py
Expand Up @@ -37,7 +37,6 @@ def __init__(self, name):
self.unity = None
self.pool = None
self.nas_server = None
self.name = name

def setup(self):
super(UnityTestResourceManager, self).setup()
Expand Down
7 changes: 6 additions & 1 deletion comptest/utils.py
Expand Up @@ -21,14 +21,15 @@
import sys
import os
from os.path import join, dirname, abspath
from fasteners import process_lock

import errno
from time import sleep

from retryz import retry

from storops.exception import StoropsException
from storops.lib.common import inter_process_locked
from storops.lib.common import get_lock_file
from test.utils import PersistedDict

__author__ = 'Cedric Zhuang'
Expand Down Expand Up @@ -253,3 +254,7 @@ def _clean_up():
request.addfinalizer(_clean_up)
_setup()
return fixture


def inter_process_locked(name):
return process_lock.interprocess_locked(get_lock_file(name))
91 changes: 25 additions & 66 deletions storops/lib/common.py
Expand Up @@ -15,25 +15,19 @@
# under the License.
from __future__ import unicode_literals

import json
import logging
from os import path, makedirs
import types

import errno
from enum import Enum as _Enum
import errno
from functools import partial

import functools
import json
import logging
from os import path, makedirs
import six
import time

import sys
import threading

from fasteners import process_lock
from retryz import retry
import cachez
import threading

import storops.exception

Expand Down Expand Up @@ -105,7 +99,7 @@ def __getitem__(self, item):
return self.list[item]


class Enum(_Enum):
class Enum(JsonPrinter, _Enum):
@classmethod
def verify(cls, value, allow_none=True):
if value is None and not allow_none:
Expand Down Expand Up @@ -147,9 +141,14 @@ def parse(cls, value):
'not supported value type: {}.'.format(type(value)))
return ret

def _get_properties(self, dec=0):
return {'value': self.value}

def is_equal(self, value):
if isinstance(value, six.string_types):
if isinstance(self.value, six.string_types):
ret = self.value.lower() == value.lower()
elif isinstance(self.value, six.integer_types):
ret = self.value == int(value)
else:
ret = self.value == value
return ret
Expand Down Expand Up @@ -206,6 +205,19 @@ def values(cls):
def enum_name(cls):
return cls.__name__

def __str__(self):
return JsonPrinter.__str__(self)

def __repr__(self):
return JsonPrinter.__repr__(self)

def get_dict_repr(self, dec=0):
if dec < 0:
ret = '{}.{}'.format(self.__class__.__name__, self.name)
else:
ret = super(Enum, self).get_dict_repr(dec)
return ret


class Dict(dict):
def __getattr__(self, item):
Expand All @@ -223,21 +235,6 @@ def __getattr__(self, item):
return ret


def get_config_prop(conf, prop, default=None):
value = default
if conf is not None:
if hasattr(conf, prop):
value = getattr(conf, prop)
elif hasattr(conf, '__getitem__'):
try:
value = conf[prop]
except (TypeError, KeyError):
pass
else:
raise ValueError('cannot get property from the config.')
return value


cache = cachez.cache
instance_cache = cachez.instance_cache
clear_instance_cache = cachez.clear_instance_cache
Expand Down Expand Up @@ -321,29 +318,6 @@ def value(self):
return ret


def log_enter_exit(func):
@functools.wraps(func)
def inner(self, *args, **kwargs):
cls_name = self.__class__.__name__
func_name = func.__name__
log.debug("entering %(cls)s.%(method)s.",
{'cls': cls_name,
'method': func_name})
start = time.time()
ret = func(self, *args, **kwargs)
end = time.time()
log.debug("exiting %(cls)s.%(method)s. "
"spent %(duration)s sec. "
"return %(return)s.",
{'cls': cls_name,
'duration': end - start,
'method': func_name,
'return': ret})
return ret

return inner


def _init_lock():
return threading.Lock()

Expand Down Expand Up @@ -389,17 +363,6 @@ def new_func(*args, **kw):
synchronized = SynchronizedDecorator.synchronized


def decorate_all_methods(decorator):
def _decorate_all_methods(cls):
for attr_name, attr_val in cls.__dict__.items():
if (isinstance(attr_val, types.FunctionType) and
not attr_name.startswith("_")):
setattr(cls, attr_name, decorator(attr_val))
return cls

return _decorate_all_methods


def const_seconds(value):
return value

Expand Down Expand Up @@ -464,10 +427,6 @@ def get_lock_file(name):
return path.join(lock_folder, name)


def inter_process_locked(name):
return process_lock.interprocess_locked(get_lock_file(name))


def round_it(ndigits=3):
def inner(func):
@six.wraps(func)
Expand Down
2 changes: 2 additions & 0 deletions storops/lib/resource.py
Expand Up @@ -157,6 +157,8 @@ def _get_properties(self, dec=0):
value = value.get_dict_repr(dec - 1)
elif isinstance(value, (datetime, timedelta)):
value = str(value)
elif isinstance(value, (tuple, list, set)):
value = [v.get_dict_repr(dec - 1) for v in value]
props[name] = value
except AttributeError:
# skip not available attributes
Expand Down
17 changes: 2 additions & 15 deletions storops/unity/enums.py
@@ -1,12 +1,12 @@
# coding=utf-8
from __future__ import unicode_literals

from storops.lib.common import Enum, JsonPrinter, EnumList
from storops.lib.common import Enum, EnumList

__author__ = 'Cedric Zhuang'


class UnityEnum(JsonPrinter, Enum):
class UnityEnum(Enum):
@property
def description(self):
return self.value[1]
Expand All @@ -31,13 +31,6 @@ def _get_properties(self, dec=0):
'value': self.index}
return props

def get_dict_repr(self, dec=0):
if dec < 0:
ret = '{}.{}'.format(self.__class__.__name__, self.name)
else:
ret = super(UnityEnum, self).get_dict_repr(dec)
return ret

@classmethod
def from_int(cls, value):
for item in cls.get_all():
Expand All @@ -49,12 +42,6 @@ def from_int(cls, value):
ret = super(UnityEnum, cls).from_int(value)
return ret

def __str__(self):
return JsonPrinter.__str__(self)

def __repr__(self):
return JsonPrinter.__repr__(self)


class UnityEnumList(EnumList):
@classmethod
Expand Down
7 changes: 5 additions & 2 deletions storops/vnx/block_cli.py
Expand Up @@ -187,8 +187,11 @@ def get_connection_port(self, sp=None, port_id=None, vport_id=None):
return cmd

@command
def get_sg(self, name=None):
cmd = 'storagegroup -list -host -iscsiAttributes'.split()
def get_sg(self, name=None, engineering=False):
cmd = ['storagegroup']
if engineering:
cmd.append('-messner')
cmd += '-list -host -iscsiAttributes'.split()
cmd += text_var('-gname', name)
return cmd

Expand Down
20 changes: 3 additions & 17 deletions storops/vnx/enums.py
Expand Up @@ -18,27 +18,13 @@
import logging
import re

from storops.lib.common import Enum, cache, JsonPrinter
from storops.lib.common import Enum, cache

log = logging.getLogger(__name__)


class VNXEnum(JsonPrinter, Enum):
def _get_properties(self, dec=0):
return {'value': self.value}

def get_dict_repr(self, dec=0):
if dec < 0:
ret = '{}.{}'.format(self.__class__.__name__, self.name)
else:
ret = super(VNXEnum, self).get_dict_repr(dec)
return ret

def __str__(self):
return JsonPrinter.__str__(self)

def __repr__(self):
return JsonPrinter.__repr__(self)
class VNXEnum(Enum):
pass


class VNXSPEnum(VNXEnum):
Expand Down
85 changes: 85 additions & 0 deletions storops/vnx/resource/host.py
@@ -0,0 +1,85 @@
# coding=utf-8
# Copyright (c) 2015 EMC Corporation.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from __future__ import unicode_literals

from storops.vnx.resource import VNXCliResource, VNXCliResourceList
from storops.vnx.resource.sg import VNXStorageGroupList

__author__ = 'Cedric Zhuang'


class VNXHost(VNXCliResource):
def __init__(self, name=None, cli=None):
super(VNXHost, self).__init__()
self._cli = cli
self._name = name

self.name = self._name
self.connections = None
self._existed = False

def property_names(self):
return ['name', 'connections']

def get_index(self):
return 'name'

def update(self, data=None):
host_list = VNXHostList(cli=self._cli)
for host in host_list:
if host.name == self._name:
self._existed = True
self.name = host.name
self.connections = host.connections
return self

@property
def existed(self):
return self._existed

@classmethod
def get(cls, cli, name=None):
if name is None:
ret = VNXHostList(cli)
else:
ret = VNXHost(cli=cli, name=name).update()
return ret


class VNXHostList(VNXCliResourceList):
@classmethod
def get_resource_class(cls):
return VNXHost

def update(self, data=None):
sg_list = VNXStorageGroupList(cli=self._cli, engineering=True)
hosts = []
host_names = []
for sg in sg_list:
if sg.hba_sp_pairs:
for pair in sg.hba_sp_pairs:
name = pair.host_name
if name in host_names:
host = hosts[host_names.index(name)]
else:
host = VNXHost(name=name, cli=self._cli)
host.connections = []
hosts.append(host)
host_names.append(name)

host.connections.append(pair)
self._list = hosts
return self

0 comments on commit f538f1c

Please sign in to comment.