Пример #1
0
class ScaleIOConfig(object):
    """ScaleIO config manager."""

    __scheme__ = {
        "network_timeout": Integer(min=0, optional=True),
        "request_retries": Integer(min=0, optional=True),
        "volume_prefix": String(optional=True),
        "volume_name": String(optional=True),
    }

    @classmethod
    def _get_scheme(cls):
        """Returns config scheme."""

        return DictScheme(cls.__scheme__)

    def _validate(self, options):
        """Validates config."""

        try:
            return validate("config", options, self._get_scheme())
        except ValidationError as e:
            raise exceptions.ScaleIOConfigError(e)

    def apply(self, **options):
        """Applies config options to a config module."""

        self._validate(options)

        for option, value in options.items():
            setattr(pyscaleio.config, option.upper(), value)
Пример #2
0
class VTree(BaseResource):
    """Volume Tree (VTree) resource model."""

    __scheme__ = {
        "name": String(optional=True),
        "baseVolumeId": String(),
        "storagePoolId": String(),
    }

    __parents__ = frozenset([
        ("baseVolumeId", "Volume"),
        ("storagePoolId", "StoragePool")
    ])
Пример #3
0
class ProtectionDomain(MutableResource):
    """ProtectionDomain resource model."""

    __scheme__ = {
        "name": String(),
        "systemId": String(),
    }
    __parents__ = frozenset([
        ("systemId", "System")
    ])

    @property
    def name(self):
        return self["name"]
Пример #4
0
def test_choices_invalid_value():
    error = pytest.raises(
        InvalidValueError,
        lambda: _validate("c", String(choices=("a", "b")))).value

    assert error.object_name == ""
    assert error.object_value == "c"
Пример #5
0
def test_dict_invalid_key_scheme():
    error = pytest.raises(
        InvalidTypeError, lambda: _validate({
            True: "boolean",
            "string": "a",
        }, Dict(key_scheme=String()))).value

    assert error.object_name == "[True]"
    assert error.object_type == bool
Пример #6
0
def test_dict_invalid_value_scheme():
    error = pytest.raises(
        InvalidTypeError, lambda: _validate({
            False: 0,
            "string": "a",
        }, Dict(value_scheme=String()))).value

    assert error.object_name == "[False]"
    assert error.object_type == int
Пример #7
0
def test_dict_scheme_missing_parameter():
    error = pytest.raises(
        MissingParameterError, lambda: _validate({
            2: "value",
        }, DictScheme({
            1: Bool(),
            2: String()
        }))).value

    assert error.object_name == "[1]"
Пример #8
0
def test_model_validation_negative(client, modelklass):

    klass = modelklass("Volume", (BaseResource, ),
                       {"__scheme__": {
                           "name": String()
                       }})
    volume_payload = mock_resource_get("Volume", "test", {"id": "test"})
    with httmock.HTTMock(login_payload, volume_payload):
        with pytest.raises(exceptions.ScaleIOValidationError) as e:
            klass("test")
        assert "instance['name'] is missing" in str(e)
Пример #9
0
def test_model_one(client, modelklass):

    klass = modelklass("Volume", (BaseResource, ),
                       {"__scheme__": {
                           "name": String(optional=True)
                       }})
    payload = {"id": "test", "name": "test_volume"}
    volume_payload = mock_resource_get("Volume", "test", payload)

    with httmock.HTTMock(login_payload, volume_payload):
        volume = klass.one("test")
        assert volume == payload
Пример #10
0
def test_dict_scheme_with_schema():
    _validate({
        False: "string",
        1: True,
        "integer": 10,
        4.4: 44.4,
    },
              DictScheme({
                  False: String(),
                  1: Bool(),
                  "integer": Integer(),
                  3.3: Float(optional=True),
                  4.4: Float(optional=True),
              }))
Пример #11
0
def test_dict_scheme_modification():
    _validate_modification({
        "1": "10",
        "2": "20",
        "3": "30"
    }, DictScheme({
        "1": ToInt(),
        "2": String(),
        "3": ToInt()
    }), {
        "1": 10,
        "2": "20",
        "3": 30
    })
Пример #12
0
class ExportsInfo(Sequence):
    """Information about volume exports."""

    __scheme__ = List(DictScheme({
        "sdcId": String(),
        "sdcIp": String(),
        "limitIops": Integer(),
        "limitBwInMbps": Integer()
    }), optional=True)

    def __init__(self, data=None):
        self._data = data or []

    def __getitem__(self, index):
        return self._data[index]

    def __len__(self):
        return len(self._data)

    def __contains__(self, key):
        if isinstance(key, Sdc):
            return key["id"] in (e["sdcId"] for e in self._data)
        else:
            super(ExportsInfo, self).__contains__(key)
Пример #13
0
def test_model_validation(client, modelklass):

    klass = modelklass("Volume", (BaseResource, ),
                       {"__scheme__": {
                           "name": String()
                       }})

    volume_payload = mock_resource_get("Volume", "test", {
        "id": "test",
        "name": "test_volume"
    })
    with httmock.HTTMock(login_payload, volume_payload):
        volume = klass("test")
        assert volume.get("id") == "test"
        assert volume.get("name") == "test_volume"
Пример #14
0
class System(EditableResource):
    """System resource model."""

    __scheme__ = {
        "name": String(optional=True),
        "restrictedSdcModeEnabled": Bool(),
    }

    @property
    def name(self):
        return self.get("name")

    @property
    def is_restricted(self):
        return self["restrictedSdcModeEnabled"]
Пример #15
0
def test_model_all(client, modelklass):

    klass = modelklass("Volume", (BaseResource, ),
                       {"__scheme__": {
                           "name": String(optional=True)
                       }})
    payload = [{
        "id": "test{0}".format(i),
        "name": "test_volume{0}".format(i)
    } for i in range(1, 2 + 1)]
    volumes_payload = mock_resources_get("Volume", payload)

    with httmock.HTTMock(login_payload, volumes_payload):
        volumes = klass.all()
        assert len(volumes) == 2
        assert sorted(["test1", "test2"]) == sorted(v["id"] for v in volumes)
Пример #16
0
def test_model_update(client, modelklass, old_payload, new_payload):

    klass = modelklass("Volume", (BaseResource, ),
                       {"__scheme__": {
                           "name": String(optional=True)
                       }})
    volume_payload = mock_resource_get("Volume", "test", old_payload)
    volume_update_payload = mock_resource_get("Volume", "test", new_payload)

    with httmock.HTTMock(login_payload, volume_payload):
        volume = klass("test")
        assert volume == old_payload

    with httmock.HTTMock(volume_update_payload):
        volume.update()
        assert volume == new_payload
Пример #17
0
def test_custom_model_scheme(client, modelklass, scheme, data):

    base_scheme = {"id": String()}
    full_scheme = base_scheme
    full_scheme.update(scheme)

    data = data
    data.update({"id": "test_id"})

    with mock.patch("pyscaleio.models.BaseResource.__scheme__", base_scheme):
        klass = modelklass("Volume", (BaseResource, ), {"__scheme__": scheme})
        result = klass._get_scheme()
        assert isinstance(result, DictScheme)
        assert result._DictScheme__scheme == full_scheme

        result = klass(instance=data)._get_scheme()
        assert isinstance(result, DictScheme)
        assert result._DictScheme__scheme == full_scheme
Пример #18
0
def test_model_all_by_ids(client, modelklass):

    klass = modelklass("Volume", (BaseResource, ),
                       {"__scheme__": {
                           "name": String(optional=True)
                       }})
    payload = [{
        "id": "test{0}".format(i),
        "name": "test_volume{0}".format(i)
    } for i in range(1, 3 + 1)]

    def mocked_action(name, action, args):
        return [item for item in payload if item["id"] in args["ids"]]

    with mock.patch("pyscaleio.ScaleIOClient.perform_action_on_type",
                    side_effect=mocked_action):
        volumes = klass.all(instance_ids=["test2", "test3"])
        assert len(volumes) == 2

        volumes = klass.all(instance_ids="test2")
        assert len(volumes) == 1
        assert volumes[0]["id"] == "test2"
Пример #19
0
def test_dict_key_value():
    _validate({
        "one": 1.0,
        "two": 2.0,
    }, Dict(String(), Float()))
Пример #20
0
def test_string_regex_invalid():
    with pytest.raises(InvalidValueError):
        _validate("12345", String(regex=r"^\d{4}$"))
Пример #21
0
def test_string_regex_valid():
    _validate("12345", String(regex=r"^\d{5}$"))
Пример #22
0
def test_string():
    _validate("string", String())
Пример #23
0
def test_choices():
    _validate("b", String(choices=("a", "b")))
Пример #24
0
class Volume(MutableResource):
    """Volume resource model."""

    __scheme__ = {
        "name": String(optional=True),
        "mappedSdcInfo": ExportsInfo.__scheme__,
        "useRmcache": Bool(),
        "sizeInKb": Integer(),
        "storagePoolId": String(),
        "volumeType": String(choices=constants.VOLUME_TYPES),
        "ancestorVolumeId": String(optional=True),
    }
    __parents__ = frozenset([
        ("ancestorVolumeId", "Volume"),
        ("storagePoolId", "StoragePool"),
        ("vtreeId", "VTree"),
    ])

    @pyscaleio.inject
    @classmethod
    def one_by_name(cls, client, name, **kwargs):
        """Returns volume instance by name.

        :param name: volume name (required)

        :rtype: pyscaleio.Volume
        """

        volume_id = client.perform_action_on_type(
            cls._get_name(), "queryIdByKey", {"name": name})

        return cls(volume_id, **kwargs)

    @classmethod
    def create(cls, size, pool, name=None, rmcache=None, thin=True, **kwargs):
        """Creates Volume instance.

        :param size: volume size in GB (required)
        :param pool: storage pool id (required)
        :param name: volume name
        :param thin: is volume 'thin' or 'thick' provisioned
        :param rmcache: volume rmcache

        :rtype: pyscaleio.Volume
        """

        volume_size = (size * constants.GIGABYTE) // constants.KILOBYTE
        volume = {
            "volumeSizeInKb": str(volume_size),
            "storagePoolId": pool
        }
        if name:
            volume["name"] = name
        if rmcache:
            volume["useRmcache"] = rmcache

        volume["volumeType"] = constants.VOLUME_TYPE_THICK
        if thin:
            volume["volumeType"] = constants.VOLUME_TYPE_THIN

        return super(Volume, cls).create(volume, **kwargs)

    @property
    def name(self):
        return self.get("name")

    @property
    def size(self):
        return self["sizeInKb"] * constants.KILOBYTE

    @property
    def type(self):
        return self.get("volumeType")

    @property
    def exports(self):
        return ExportsInfo(self.get("mappedSdcInfo"))

    @property
    def path(self):
        device_name = config.VOLUME_NAME.format(
            system_id=self._client.system["id"],
            volume_id=self["id"]
        )
        return os.path.join(config.VOLUME_PREFIX, device_name)

    def rename(self, name):
        """Changes volume name.

        :param name: new volume name
        """

        return super(Volume, self).perform("setVolumeName", {"newName": name})

    def resize(self, size):
        """Changes volumes size.

        :param size: new volume size in GB (required)
        """

        return super(Volume, self).perform("setVolumeSize", {"sizeInGB": str(size)})

    def snapshot(self, name=None):
        """Creates snapshot of current volume.

        :param name: snapshot name
        """

        snapshot = {"volumeId": self["id"]}
        if name:
            snapshot["snapshotName"] = name

        result = self._client.system.perform(
            "snapshotVolumes", {"snapshotDefs": [snapshot]})

        return Volume(result["volumeIdList"][0])

    def throttle(self, sdc_id=None, sdc_guid=None, iops=None, mbps=None):
        """Throttles I/O on current volume.

        :param iops: I/O operations in seconds (> 10)
        :param mbps: network bandwidth as megabytes in seconds
        """

        if sdc_id and sdc_guid:
            raise exceptions.ScaleIONotBothParameters("sdc_id", "sdc_guid")

        if iops is None and mbps is None:
            raise exceptions.ScaleIORequiredParameters("iops", "mbps")

        if iops is not None and iops != 0 and iops <= 10:
            raise exceptions.ScaleIOInvalidLimit("iops", "must be greater than 10.")

        if mbps is not None:
            mbps = mbps * constants.KILOBYTE
            if bool(mbps % constants.KILOBYTE):
                raise exceptions.ScaleIOInvalidLimit("mbps", "must be granular to 1024 Kbps")

        data = {}
        if sdc_id:
            data["sdcId"] = sdc_id
        if sdc_guid:
            data["guid"] = sdc_guid
        if iops is not None:
            data["iopsLimit"] = str(iops)
        if mbps is not None:
            data["bandwidthLimitInKbps"] = str(mbps)

        return super(Volume, self).perform("setMappedSdcLimits", data)

    def export(self, sdc_id=None, sdc_guid=None, multiple=False):
        """Exports volume to specified SDC.

        :param sdc_id: id of SDC instance
        :param sdc_guid: guid of SDC instance
        :param multiple: allows export to multiple SDCs (optional)
        """

        if sdc_id and sdc_guid:
            raise exceptions.ScaleIONotBothParameters("sdc_id", "sdc_guid")

        data = {}
        if sdc_id:
            data["sdcId"] = sdc_id
        if sdc_guid:
            data["guid"] = sdc_guid

        if multiple:
            data["allowMultipleMappings"] = utils.bool_to_str(multiple)

        return super(Volume, self).perform("addMappedSdc", data)

    def unexport(self, sdc_id=None, sdc_guid=None):
        """Unexports volume from specified SDC.
        Without parameters unexports volume from all SDCs

        :param sdc_id: id of SDC instance
        :param sdc_guid: guid if SDC instance
        """

        if sdc_id and sdc_guid:
            raise exceptions.ScaleIONotBothParameters("sdc_id", "sdc_guid")

        data = {}
        if sdc_id:
            data["sdcId"] = sdc_id
        elif sdc_guid:
            data["guid"] = sdc_guid
        else:
            data["allSdcs"] = ""

        return super(Volume, self).perform("removeMappedSdc", data)

    def delete(self, mode=constants.VOLUME_REMOVE_ONLY_ME):
        """Removes volume with specified mode.

        :param mode: volume remove mode (optional)
        """

        return super(Volume, self).delete({"removeMode": mode})
Пример #25
0
def test_string_invalid_type():
    error = pytest.raises(InvalidTypeError,
                          lambda: _validate(b"bytes", String())).value

    assert error.object_name == ""
    assert error.object_type == bytes
Пример #26
0
@pytest.mark.parametrize(("name", "result"), [
    ("Volume", "Volume"),
    ("VTree", "VTree"),
    ("StoragePool", "StoragePool"),
    ("Sdc", "Sdc"),
])
def test_custom_model_name(client, modelklass, name, result):

    assert BaseResource.__resource__ is None
    with mock.patch("pyscaleio.models.BaseResource.__scheme__", {}):
        klass = modelklass(name, (BaseResource, ), {"__scheme__": {}})
        assert klass._get_name() == result
        assert klass(instance={})._get_name() == result


@pytest.mark.parametrize("scheme", [{}, {"id": String()}])
def test_base_model_scheme(client, modelklass, scheme):

    with mock.patch("pyscaleio.models.BaseResource.__scheme__", scheme):
        result = BaseResource._get_scheme()
        assert isinstance(result, DictScheme)
        assert result._DictScheme__scheme == scheme

        result = BaseResource(instance={"id": "test"})._get_scheme()
        assert isinstance(result, DictScheme)
        assert result._DictScheme__scheme == scheme


@pytest.mark.parametrize(("scheme", "data"),
                         [({
                             "name": String()
Пример #27
0
class Sdc(MutableResource):
    """SDC resource model."""

    __scheme__ = {
        "name": String(optional=True),
        "sdcIp": String(),
        "sdcGuid": String(),
        "sdcApproved": Bool(),
        "mdmConnectionState": String(
            choices=constants.SDC_MDM_STATES),
    }
    __parents__ = frozenset([
        ("systemId", "System")
    ])

    @pyscaleio.inject
    @classmethod
    def all_approved(cls, client, **kwargs):
        """Returns list of all approved SDCs."""

        instances = client.perform_action_on_type(
            cls._get_name(), "queryAllApprovedSdc", {})

        return [cls(instance=instance, client=client)
            for instance in instances
        ]

    @pyscaleio.inject
    @classmethod
    def one_by_ip(cls, client, ip_address, **kwargs):
        """Returns SDC instance by specified IP address.

        :param ip_address: IP address of SDC

        :rtype: pyscaleio.SDC
        """

        instance_id = client.perform_action_on_type(
            cls._get_name(), "queryIdByKey", {"ip": ip_address})

        return cls(instance_id, client=client)

    @property
    def name(self):
        return self.get("name")

    @property
    def ip(self):
        return self["sdcIp"]

    @property
    def guid(self):
        return self["sdcGuid"]

    @property
    def is_approved(self):
        return self["sdcApproved"]

    @property
    def is_connected(self):
        return self["mdmConnectionState"] == constants.SDC_MDM_STATE_CONNECTED
Пример #28
0
def test_string_max_length_invalid():
    with pytest.raises(InvalidValueError):
        _validate("12345", String(max_length=4))
Пример #29
0
class StoragePool(MutableResource):
    """StoragePool resource model."""

    __scheme__ = {
        "name": String(optional=True),
        "protectionDomainId": String(),
        "checksumEnabled": Bool(),
        "useRfcache": Bool(),
    }
    __parents__ = frozenset([
        ("protectionDomainId", "ProtectionDomain")
    ])

    @pyscaleio.inject
    @classmethod
    def one_by_name(cls, client, name, domain_name):
        """Returns StoragePool instance by name and protection domain name.

        :param name: storage pool name
        :param domain_name: protection domain name

        :rtype: pyscaleio.StoragePool
        """

        data = {
            "name": name,
            "protectionDomainName": domain_name
        }
        pool_id = client.perform_action_on_type(
            cls._get_name(), "queryIdByKey", data)

        return cls(pool_id, client=client)

    @pyscaleio.inject
    @classmethod
    def create(cls, client, domain, checksum=False, rfcache=False, name=None, **kwargs):
        """Creates StoragePool instance.

        :param domain: protection domain id (required)
        :param checksum: enable checksum protection (default is False)
        :param rfcache: use read-only flash cache (default is False)

        :rtype: pyscaleio.StoragePool
        """

        pool = {
            "protectionDomainId": domain,
            "checksumEnabled": utils.bool_to_str(checksum),
            "useRfcache": utils.bool_to_str(rfcache),
        }
        if name:
            pool["name"] = name

        return super(StoragePool, cls).create(pool, **kwargs)

    @property
    def name(self):
        return self.get("name")

    @property
    def checksum_enabled(self):
        return self["checksumEnabled"]

    @property
    def rfcache_enabled(self):
        return self["useRfcache"]

    def create_volume(self, size, **kwargs):
        """Creates Volume instance in current StoragePool.

        :param size: volume size in GB (required)

        :rtype: pyscaleio.Volume
        """

        kwargs["client"] = self._client
        return Volume.create(size, self["id"], **kwargs)
Пример #30
0
class BaseResource(Mapping):
    """Base resource model."""

    __scheme__ = {
        "id": String(),
        "links": List(
            DictScheme({
                "href": String(),
                "rel": String()
            }), optional=True
        )
    }
    """Data scheme for instance validation."""

    __parents__ = None
    """
    References to parent resources by fields.

    Example:
        frozenset([
            ("parentField", "ResourceClassName")
        ])
    """

    __resource__ = None
    """
    Custom resource name that overrides default
    name based on name of resource class.
    """

    @classmethod
    def _get_name(cls):
        """Returns resource name.

        Attention: for internal use only!
        """

        resource = cls.__resource__
        if not resource:
            resource = camelize(underscore(cls.__name__))
        return resource

    @classmethod
    def _get_scheme(cls):
        """Returns resource scheme for proper validation.

        Attention: for internal use only!
        """

        scheme = {}
        for base in cls.mro():
            if bool(
                issubclass(base, BaseResource) and
                getattr(base, "__scheme__", None)
            ):
                scheme.update(base.__scheme__)

        return DictScheme(scheme, ignore_unknown=True)

    @classmethod
    def one(cls, instance_id, **kwargs):
        """Returns instance of resource.

        :param instance_id: id of resource instance

        :returns: instance of resource
        """

        return cls(instance_id, **kwargs)

    @pyscaleio.inject
    @classmethod
    def all(cls, client, instance_ids=None, **kwargs):
        """Returns list of resource instances.

        :param instance_ids: list of instance ids (optional)

        :returns: list of resource instances
        """

        if not instance_ids:
            instances = client.get_instances_of(cls._get_name())
        else:
            if isinstance(instance_ids, string_types):
                instance_ids = (instance_ids,)
            instances = client.perform_action_on_type(
                cls._get_name(), "queryBySelectedIds", {"ids": instance_ids})

        return [cls(instance=instance, client=client)
            for instance in instances
        ]

    @pyscaleio.inject
    def __init__(self, client, instance_id=None, instance=None):
        self._client = client
        self._scheme = {}

        if instance_id and instance:
            raise exceptions.ScaleIONotBothParameters("instance_id", "instance")

        if instance_id:
            instance = self._client.get_instance_of(self._get_name(), instance_id)

        self._instance = self._validate(instance or {})

    def __getitem__(self, key):
        return self._instance[key]

    def __iter__(self):
        return iter(self._instance)

    def __len__(self):
        return len(self._instance)

    @property
    def links(self):
        return self["links"]

    def _validate(self, instance):
        """Validates the instance if resource according to scheme.

        Attention: for internal use only!
        """

        try:
            return validate("instance", instance, self._get_scheme())
        except ValidationError as e:
            raise exceptions.ScaleIOValidationError(e)

    def update(self):
        """Updates resource instance."""

        instance = self._client.get_instance_of(self._get_name(), self["id"])
        instance = self._validate(instance)

        fields = set(list(instance) + list(self._instance))
        for field in fields:
            try:
                instance[field]
            except KeyError:
                del self._instance[field]
            else:
                self._instance[field] = instance[field]