Refactor resource.py/Resource

Create a class of 'Recource' to replace the original definition.
It will bring several advantages.

1. The new class will be used as tuple which can't be modified,
accessed by name or index.

2. The instance of new class can be hashable whatever the type of
members of instace. So, it can be used in method of 'build_graph'.

3. Add some other functions to it, such as transform it to dict.

4. It is easy to chose members of it to be serialized by modifing
the definition of property of 'Resource'.

Change-Id: Ib5f3fea53bff9fe0a7c3b88339af26b70a6b7d3c
This commit is contained in:
zengchen 2017-04-06 20:18:49 +08:00
parent c24b80e8ce
commit b1255e1966
6 changed files with 56 additions and 35 deletions

View File

@ -11,10 +11,34 @@
# under the License.
from collections import namedtuple
class Resource(object):
__slots__ = ('type', 'id', 'name', 'extra_info')
_Resource = namedtuple("Resource", ('type', 'id', 'name', 'extra_info'))
def __init__(self, type, id, name, extra_info=None):
self.type = type
self.id = id
self.name = name
self.extra_info = extra_info
def __setattr__(self, key, value):
try:
getattr(self, key)
except AttributeError:
pass
else:
raise AttributeError()
def Resource(type, id, name, extra_info=None):
return _Resource(type, id, name, extra_info)
return super(Resource, self).__setattr__(key, value)
def __hash__(self):
return hash(self.key)
def __eq__(self, other):
return self.key == other.key
def to_dict(self):
return {item: getattr(self, item) for item in self.__slots__}
@property
def key(self):
return (self.type, self.id, self.name)

View File

@ -40,15 +40,7 @@ class CompleteProtectTask(task.Task):
def get_flow(context, protectable_registry, workflow_engine, plan, provider,
checkpoint):
# The 'extra-info' field of resources in plan is optional
# The extra_info field of the resource is a dict.
# The dict can not be handled by build_graph. It will throw a
# error. TypeError: unhashable type: 'dict'
resources = set()
for item in plan.get("resources"):
item["extra_info"] = None
resources.add(Resource(**item))
resources = set(Resource(**item) for item in plan.get("resources"))
resource_graph = protectable_registry.build_graph(context,
resources)
checkpoint.resource_graph = resource_graph

View File

@ -219,7 +219,9 @@ def unpack_graph(packed_graph):
def serialize_resource_graph(resource_graph):
packed_resource_graph = pack_graph(resource_graph)
return jsonutils.dumps(packed_resource_graph)
return jsonutils.dumps(
packed_resource_graph,
default=lambda r: (r.type, r.id, r.name, r.extra_info))
def deserialize_resource_graph(serialized_resource_graph):

View File

@ -320,9 +320,7 @@ class ProtectionManager(manager.Manager):
'err': six.text_type(err)})
raise
return dict(id=resource_instance.id, name=resource_instance.name,
type=resource_instance.type,
extra_info=resource_instance.extra_info)
return resource_instance.to_dict()
@messaging.expected_exceptions(exception.ListProtectableResourceFailed)
def list_protectable_dependents(self, context,
@ -334,7 +332,7 @@ class ProtectionManager(manager.Manager):
'id': protectable_id})
parent_resource = Resource(type=protectable_type, id=protectable_id,
name="", extra_info=None)
name="")
registry = self.protectable_registry
try:
@ -346,13 +344,7 @@ class ProtectionManager(manager.Manager):
'err': six.text_type(err)})
raise
result = []
for resource in dependent_resources:
result.append(dict(type=resource.type, id=resource.id,
name=resource.name,
extra_info=resource.extra_info))
return result
return [resource.to_dict() for resource in dependent_resources]
def list_providers(self, context, marker=None, limit=None,
sort_keys=None, sort_dirs=None, filters=None):

View File

@ -12,7 +12,6 @@
from karbor import exception
from karbor.i18n import _
from karbor.resource import Resource
from karbor.services.protection.graph import build_graph
import six
@ -109,15 +108,7 @@ class ProtectableRegistry(object):
def build_graph(self, context, resources):
def fetch_dependent_resources_context(resource):
dependent_resources = self.fetch_dependent_resources(
context, resource)
# The extra_info field of the resource is a dict
# The dict can not be handled by build_graph, it will throw a
# error. TypeError: unhashable type: 'dict'
return [Resource(type=dependent_resource.type,
id=dependent_resource.id,
name=dependent_resource.name, extra_info=None)
for dependent_resource in dependent_resources]
return self.fetch_dependent_resources(context, resource)
return build_graph(
start_nodes=resources,

View File

@ -14,6 +14,7 @@ from oslo_serialization import jsonutils
from oslo_serialization import msgpackutils
from karbor import exception
from karbor import resource
import karbor.services.protection.graph as graph
from karbor.tests import base
@ -143,6 +144,25 @@ class GraphBuilderTest(base.TestCase):
unserialized = graph.unpack_graph(fmt.loads(serialized))
self.assertEqual(test_graph, unserialized)
def test_graph_serialize(self):
resource_a = resource.Resource('server', 0, 'a', {'name': 'a'})
resource_b = resource.Resource('volume', 1, 'b', {'name': 'b'})
test_base = {
resource_a: [resource_b],
resource_b: []
}
test_graph = graph.build_graph(test_base.keys(), test_base.__getitem__)
self.assertIn(
graph.serialize_resource_graph(test_graph),
[
'[{"0x1": ["server", 0, "a", {"name": "a"}], '
'"0x0": ["volume", 1, "b", {"name": "b"}]}, '
'[["0x1", ["0x0"]]]]',
'[{"0x0": ["volume", 1, "b", {"name": "b"}], '
'"0x1": ["server", 0, "a", {"name": "a"}]}, '
'[["0x1", ["0x0"]]]]'
])
def test_graph_deserialize_unordered_adjacency(self):
test_base = {
"A1": ["B1", "B2"],