From df458e0b77d90dc2697f524b24a4ca842ddf2285 Mon Sep 17 00:00:00 2001
From: Iustin Pop <iustin@google.com>
Date: Wed, 19 Mar 2008 16:55:05 +0000
Subject: [PATCH] Change the opcode hierarchy and implementation

This patch adds a new top-level class (BaseJO) that is used for both
opcodes and a new Job class.

This new class and the related changes to the OpCode abstract class are
used to implement simple to-dict/from-dict transformations, so that we
can easily serialize the classes using json.

Reviewed-by: imsnah
---
 lib/opcodes.py | 102 +++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 98 insertions(+), 4 deletions(-)

diff --git a/lib/opcodes.py b/lib/opcodes.py
index 46f6a0ae6..38d80c08d 100644
--- a/lib/opcodes.py
+++ b/lib/opcodes.py
@@ -36,18 +36,112 @@ are two kinds of classes defined:
 # few public methods:
 # pylint: disable-msg=R0903
 
-class OpCode(object):
-  """Abstract OpCode"""
-  OP_ID = "OP_ABSTRACT"
+
+class BaseJO(object):
+  """A simple serializable object.
+
+  This object serves as a parent class for both OpCode and Job since
+  they are serialized in the same way.
+
+  """
   __slots__ = []
 
   def __init__(self, **kwargs):
     for key in kwargs:
       if key not in self.__slots__:
-        raise TypeError("OpCode %s doesn't support the parameter '%s'" %
+        raise TypeError("Object %s doesn't support the parameter '%s'" %
                         (self.__class__.__name__, key))
       setattr(self, key, kwargs[key])
 
+  def __getstate__(self):
+    state = {}
+    for name in self.__slots__:
+      if hasattr(self, name):
+        state[name] = getattr(self, name)
+    return state
+
+  def __setstate__(self, state):
+    if not isinstance(state, dict):
+      raise ValueError("Invalid data to __setstate__: expected dict, got %s" %
+                       type(state))
+
+    for name in self.__slots__:
+      if name not in state:
+        delattr(self, name)
+
+    for name in state:
+      setattr(self, name, state[name])
+
+
+class Job(BaseJO):
+  """Job definition structure"""
+  STATUS_PENDING = 1
+  STATUS_RUNNING = 2
+  STATUS_FINISHED = 3
+  RESULT_OK = 1
+  RESULT_FAIL = 2
+  RESULT_ABORT = 3
+
+  __slots__ = ["job_id", "op_list", "status", "result"]
+
+  def __getstate__(self):
+    """Specialized getstate for jobs
+
+    """
+    data = BaseJO.__getstate__(self)
+    if "op_list" in data:
+      data["op_list"] = [op.__getstate__() for op in data["op_list"]]
+    return data
+
+  def __setstate__(self, state):
+    """Specialized setstate for jobs
+
+    """
+    BaseJO.__setstate__(self, state)
+    if "op_list" in state:
+      self.op_list = [OpCode.LoadOpcode(op) for op in state["op_list"]]
+
+
+class OpCode(BaseJO):
+  """Abstract OpCode"""
+  OP_ID = "OP_ABSTRACT"
+  __slots__ = []
+
+  def __getstate__(self):
+    """Specialized getstate for opcodes.
+
+    """
+    data = BaseJO.__getstate__(self)
+    data["OP_ID"] = self.OP_ID
+    return data
+
+  @classmethod
+  def LoadOpcode(cls, data):
+    """Generic load opcode method.
+
+    """
+    if not isinstance(data, dict):
+      raise ValueError("Invalid data to LoadOpCode (%s)" % type(data))
+    if "OP_ID" not in data:
+      raise ValueError("Invalid data to LoadOpcode, missing OP_ID")
+    op_id = data["OP_ID"]
+    op_class = None
+    for item in globals().values():
+      if (isinstance(item, type) and
+          issubclass(item, cls) and
+          hasattr(item, "OP_ID") and
+          getattr(item, "OP_ID") == op_id):
+        op_class = item
+        break
+    if op_class is None:
+      raise ValueError("Invalid data to LoadOpCode: OP_ID %s unsupported" %
+                       op_id)
+    op = op_class()
+    new_data = data.copy()
+    del new_data["OP_ID"]
+    op.__setstate__(new_data)
+    return op
+
 
 class OpInitCluster(OpCode):
   """Initialise the cluster."""
-- 
GitLab