summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJenkins <jenkins@review.openstack.org>2014-08-13 01:37:02 +0000
committerGerrit Code Review <review@openstack.org>2014-08-13 01:37:02 +0000
commit81beebbc60b40a4e674269585108161767bda883 (patch)
treec2b7e5e24c7a486127581faddb92e6436d280f3e
parent2e31f9bdc5a6aa5c70450a10d7425a36927bf9d7 (diff)
parent6316a6ff47d1a1247e9ebdd9f432dda4fd2c4df7 (diff)
downloadoslo-vmware-81beebbc60b40a4e674269585108161767bda883.tar.gz
Merge "Port the Datastore and DatastorePath objects"
-rw-r--r--openstack-common.conf1
-rw-r--r--oslo/vmware/objects/__init__.py0
-rw-r--r--oslo/vmware/objects/datastore.py165
-rw-r--r--oslo/vmware/openstack/common/units.py38
-rw-r--r--tests/objects/__init__.py0
-rw-r--r--tests/objects/test_datastore.py191
6 files changed, 395 insertions, 0 deletions
diff --git a/openstack-common.conf b/openstack-common.conf
index bb5d255..e229456 100644
--- a/openstack-common.conf
+++ b/openstack-common.conf
@@ -3,6 +3,7 @@
# The list of modules to copy from oslo-incubator.git
module=excutils
module=gettextutils
+module=units
script=tools/run_cross_tests.sh
diff --git a/oslo/vmware/objects/__init__.py b/oslo/vmware/objects/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/oslo/vmware/objects/__init__.py
diff --git a/oslo/vmware/objects/datastore.py b/oslo/vmware/objects/datastore.py
new file mode 100644
index 0000000..83b09a4
--- /dev/null
+++ b/oslo/vmware/objects/datastore.py
@@ -0,0 +1,165 @@
+# Copyright (c) 2014 VMware, Inc.
+#
+# 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.
+
+import posixpath
+
+from oslo.vmware.openstack.common.gettextutils import _
+
+
+class Datastore(object):
+
+ def __init__(self, ref, name, capacity=None, freespace=None):
+ """Datastore object holds ref and name together for convenience.
+
+ :param ref: a vSphere reference to a datastore
+ :param name: vSphere unique name for this datastore
+ :param capacity: (optional) capacity in bytes of this datastore
+ :param freespace: (optional) free space in bytes of datastore
+ """
+ if name is None:
+ raise ValueError(_("Datastore name cannot be None"))
+ if ref is None:
+ raise ValueError(_("Datastore reference cannot be None"))
+ if freespace is not None and capacity is None:
+ raise ValueError(_("Invalid capacity"))
+ if capacity is not None and freespace is not None:
+ if capacity < freespace:
+ raise ValueError(_("Capacity is smaller than free space"))
+
+ self._ref = ref
+ self._name = name
+ self._capacity = capacity
+ self._freespace = freespace
+
+ @property
+ def ref(self):
+ return self._ref
+
+ @property
+ def name(self):
+ return self._name
+
+ @property
+ def capacity(self):
+ return self._capacity
+
+ @property
+ def freespace(self):
+ return self._freespace
+
+ def build_path(self, *paths):
+ """Constructs and returns a DatastorePath.
+
+ :param paths: list of path components, for constructing a path relative
+ to the root directory of the datastore
+ :return: a DatastorePath object
+ """
+ return DatastorePath(self._name, *paths)
+
+ def __str__(self):
+ return '[%s]' % self._name
+
+
+class DatastorePath(object):
+
+ """Class for representing a directory or file path in a vSphere datatore.
+
+ This provides various helper methods to access components and useful
+ variants of the datastore path.
+
+ Example usage:
+
+ DatastorePath("datastore1", "_base/foo", "foo.vmdk") creates an
+ object that describes the "[datastore1] _base/foo/foo.vmdk" datastore
+ file path to a virtual disk.
+
+ Note:
+ - Datastore path representations always uses forward slash as separator
+ (hence the use of the posixpath module).
+ - Datastore names are enclosed in square brackets.
+ - Path part of datastore path is relative to the root directory
+ of the datastore, and is always separated from the [ds_name] part with
+ a single space.
+ """
+
+ def __init__(self, datastore_name, *paths):
+ if datastore_name is None or datastore_name == '':
+ raise ValueError(_("Datastore name cannot be empty"))
+ self._datastore_name = datastore_name
+ self._rel_path = ''
+ if paths:
+ if None in paths:
+ raise ValueError(_("Path component cannot be None"))
+ self._rel_path = posixpath.join(*paths)
+
+ def __str__(self):
+ """Full datastore path to the file or directory."""
+ if self._rel_path != '':
+ return "[%s] %s" % (self._datastore_name, self.rel_path)
+ return "[%s]" % self._datastore_name
+
+ @property
+ def datastore(self):
+ return self._datastore_name
+
+ @property
+ def parent(self):
+ return DatastorePath(self.datastore, posixpath.dirname(self._rel_path))
+
+ @property
+ def basename(self):
+ return posixpath.basename(self._rel_path)
+
+ @property
+ def dirname(self):
+ return posixpath.dirname(self._rel_path)
+
+ @property
+ def rel_path(self):
+ return self._rel_path
+
+ def join(self, *paths):
+ """Join one or more path components intelligently into a datastore path.
+
+ If any component is an absolute path, all previous components are
+ thrown away, and joining continues. The return value is the
+ concatenation of the paths with exactly one slash ('/') inserted
+ between components, unless p is empty.
+
+ :return: A datastore path
+ """
+ if paths:
+ if None in paths:
+ raise ValueError(_("Path component cannot be None"))
+ return DatastorePath(self.datastore, self._rel_path, *paths)
+ return self
+
+ def __eq__(self, other):
+ return (isinstance(other, DatastorePath) and
+ self._datastore_name == other._datastore_name and
+ self._rel_path == other._rel_path)
+
+ @classmethod
+ def parse(cls, datastore_path):
+ """Constructs a DatastorePath object given a datastore path string."""
+ if not datastore_path:
+ raise ValueError(_("Datastore path cannot be empty"))
+
+ spl = datastore_path.split('[', 1)[1].split(']', 1)
+ path = ""
+ if len(spl) == 1:
+ datastore_name = spl[0]
+ else:
+ datastore_name, path = spl
+ return cls(datastore_name, path.strip())
diff --git a/oslo/vmware/openstack/common/units.py b/oslo/vmware/openstack/common/units.py
new file mode 100644
index 0000000..4817da5
--- /dev/null
+++ b/oslo/vmware/openstack/common/units.py
@@ -0,0 +1,38 @@
+# Copyright 2013 IBM Corp
+# 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 constants
+"""
+
+# Binary unit constants.
+Ki = 1024
+Mi = 1024 ** 2
+Gi = 1024 ** 3
+Ti = 1024 ** 4
+Pi = 1024 ** 5
+Ei = 1024 ** 6
+Zi = 1024 ** 7
+Yi = 1024 ** 8
+
+# Decimal unit constants.
+k = 1000
+M = 1000 ** 2
+G = 1000 ** 3
+T = 1000 ** 4
+P = 1000 ** 5
+E = 1000 ** 6
+Z = 1000 ** 7
+Y = 1000 ** 8
diff --git a/tests/objects/__init__.py b/tests/objects/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/objects/__init__.py
diff --git a/tests/objects/test_datastore.py b/tests/objects/test_datastore.py
new file mode 100644
index 0000000..7044cfb
--- /dev/null
+++ b/tests/objects/test_datastore.py
@@ -0,0 +1,191 @@
+# Copyright (c) 2014 VMware, Inc.
+#
+# 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 oslo.vmware.objects import datastore
+from oslo.vmware.openstack.common import units
+from tests import base
+
+
+class DatastoreTestCase(base.TestCase):
+
+ """Test the Datastore object."""
+
+ def test_ds(self):
+ ds = datastore.Datastore(
+ "fake_ref", "ds_name", 2 * units.Gi, 1 * units.Gi)
+ self.assertEqual('ds_name', ds.name)
+ self.assertEqual('fake_ref', ds.ref)
+ self.assertEqual(2 * units.Gi, ds.capacity)
+ self.assertEqual(1 * units.Gi, ds.freespace)
+
+ def test_ds_invalid_space(self):
+ self.assertRaises(ValueError, datastore.Datastore,
+ "fake_ref", "ds_name", 1 * units.Gi, 2 * units.Gi)
+ self.assertRaises(ValueError, datastore.Datastore,
+ "fake_ref", "ds_name", None, 2 * units.Gi)
+
+ def test_ds_no_capacity_no_freespace(self):
+ ds = datastore.Datastore("fake_ref", "ds_name")
+ self.assertIsNone(ds.capacity)
+ self.assertIsNone(ds.freespace)
+
+ def test_ds_invalid(self):
+ self.assertRaises(ValueError, datastore.Datastore, None, "ds_name")
+ self.assertRaises(ValueError, datastore.Datastore, "fake_ref", None)
+
+ def test_build_path(self):
+ ds = datastore.Datastore("fake_ref", "ds_name")
+ ds_path = ds.build_path("some_dir", "foo.vmdk")
+ self.assertEqual('[ds_name] some_dir/foo.vmdk', str(ds_path))
+
+
+class DatastorePathTestCase(base.TestCase):
+
+ """Test the DatastorePath object."""
+
+ def test_ds_path(self):
+ p = datastore.DatastorePath('dsname', 'a/b/c', 'file.iso')
+ self.assertEqual('[dsname] a/b/c/file.iso', str(p))
+ self.assertEqual('a/b/c/file.iso', p.rel_path)
+ self.assertEqual('a/b/c', p.parent.rel_path)
+ self.assertEqual('[dsname] a/b/c', str(p.parent))
+ self.assertEqual('dsname', p.datastore)
+ self.assertEqual('file.iso', p.basename)
+ self.assertEqual('a/b/c', p.dirname)
+
+ def test_ds_path_no_ds_name(self):
+ bad_args = [
+ ('', ['a/b/c', 'file.iso']),
+ (None, ['a/b/c', 'file.iso'])]
+ for t in bad_args:
+ self.assertRaises(
+ ValueError, datastore.DatastorePath,
+ t[0], *t[1])
+
+ def test_ds_path_invalid_path_components(self):
+ bad_args = [
+ ('dsname', [None]),
+ ('dsname', ['', None]),
+ ('dsname', ['a', None]),
+ ('dsname', ['a', None, 'b']),
+ ('dsname', [None, '']),
+ ('dsname', [None, 'b'])]
+
+ for t in bad_args:
+ self.assertRaises(
+ ValueError, datastore.DatastorePath,
+ t[0], *t[1])
+
+ def test_ds_path_no_subdir(self):
+ args = [
+ ('dsname', ['', 'x.vmdk']),
+ ('dsname', ['x.vmdk'])]
+
+ canonical_p = datastore.DatastorePath('dsname', 'x.vmdk')
+ self.assertEqual('[dsname] x.vmdk', str(canonical_p))
+ self.assertEqual('', canonical_p.dirname)
+ self.assertEqual('x.vmdk', canonical_p.basename)
+ self.assertEqual('x.vmdk', canonical_p.rel_path)
+ for t in args:
+ p = datastore.DatastorePath(t[0], *t[1])
+ self.assertEqual(str(canonical_p), str(p))
+
+ def test_ds_path_ds_only(self):
+ args = [
+ ('dsname', []),
+ ('dsname', ['']),
+ ('dsname', ['', ''])]
+
+ canonical_p = datastore.DatastorePath('dsname')
+ self.assertEqual('[dsname]', str(canonical_p))
+ self.assertEqual('', canonical_p.rel_path)
+ self.assertEqual('', canonical_p.basename)
+ self.assertEqual('', canonical_p.dirname)
+ for t in args:
+ p = datastore.DatastorePath(t[0], *t[1])
+ self.assertEqual(str(canonical_p), str(p))
+ self.assertEqual(canonical_p.rel_path, p.rel_path)
+
+ def test_ds_path_equivalence(self):
+ args = [
+ ('dsname', ['a/b/c/', 'x.vmdk']),
+ ('dsname', ['a/', 'b/c/', 'x.vmdk']),
+ ('dsname', ['a', 'b', 'c', 'x.vmdk']),
+ ('dsname', ['a/b/c', 'x.vmdk'])]
+
+ canonical_p = datastore.DatastorePath('dsname', 'a/b/c', 'x.vmdk')
+ for t in args:
+ p = datastore.DatastorePath(t[0], *t[1])
+ self.assertEqual(str(canonical_p), str(p))
+ self.assertEqual(canonical_p.datastore, p.datastore)
+ self.assertEqual(canonical_p.rel_path, p.rel_path)
+ self.assertEqual(str(canonical_p.parent), str(p.parent))
+
+ def test_ds_path_non_equivalence(self):
+ args = [
+ # leading slash
+ ('dsname', ['/a', 'b', 'c', 'x.vmdk']),
+ ('dsname', ['/a/b/c/', 'x.vmdk']),
+ ('dsname', ['a/b/c', '/x.vmdk']),
+ # leading space
+ ('dsname', ['a/b/c/', ' x.vmdk']),
+ ('dsname', ['a/', ' b/c/', 'x.vmdk']),
+ ('dsname', [' a', 'b', 'c', 'x.vmdk']),
+ # trailing space
+ ('dsname', ['/a/b/c/', 'x.vmdk ']),
+ ('dsname', ['a/b/c/ ', 'x.vmdk'])]
+
+ canonical_p = datastore.DatastorePath('dsname', 'a/b/c', 'x.vmdk')
+ for t in args:
+ p = datastore.DatastorePath(t[0], *t[1])
+ self.assertNotEqual(str(canonical_p), str(p))
+
+ def test_equal(self):
+ a = datastore.DatastorePath('ds_name', 'a')
+ b = datastore.DatastorePath('ds_name', 'a')
+ self.assertEqual(a, b)
+
+ def test_join(self):
+ p = datastore.DatastorePath('ds_name', 'a')
+ ds_path = p.join('b')
+ self.assertEqual('[ds_name] a/b', str(ds_path))
+
+ p = datastore.DatastorePath('ds_name', 'a')
+ ds_path = p.join()
+ bad_args = [
+ [None],
+ ['', None],
+ ['a', None],
+ ['a', None, 'b']]
+ for arg in bad_args:
+ self.assertRaises(ValueError, p.join, *arg)
+
+ def test_ds_path_parse(self):
+ p = datastore.DatastorePath.parse('[dsname]')
+ self.assertEqual('dsname', p.datastore)
+ self.assertEqual('', p.rel_path)
+
+ p = datastore.DatastorePath.parse('[dsname] folder')
+ self.assertEqual('dsname', p.datastore)
+ self.assertEqual('folder', p.rel_path)
+
+ p = datastore.DatastorePath.parse('[dsname] folder/file')
+ self.assertEqual('dsname', p.datastore)
+ self.assertEqual('folder/file', p.rel_path)
+
+ for p in [None, '']:
+ self.assertRaises(ValueError, datastore.DatastorePath.parse, p)
+
+ for p in ['bad path', '/a/b/c', 'a/b/c']:
+ self.assertRaises(IndexError, datastore.DatastorePath.parse, p)