legacy methods - this is more an excuse for studying how the API can be
authorThierry Parmentelat <thierry.parmentelat@sophia.inria.fr>
Tue, 17 Jun 2008 13:25:53 +0000 (13:25 +0000)
committerThierry Parmentelat <thierry.parmentelat@sophia.inria.fr>
Tue, 17 Jun 2008 13:25:53 +0000 (13:25 +0000)
reorganized into several parts

PLC/API.py
PLC/Legacy/NodeNetworks.py [new file with mode: 0644]
PLC/Legacy/Types.py [new file with mode: 0644]
PLC/Legacy/__init__.py [new file with mode: 0644]
PLC/Method.py
PLC/Shell.py

index 7fcfa09..5e97ebf 100644 (file)
@@ -83,10 +83,28 @@ except ImportError:
 from PLC.Config import Config
 from PLC.Faults import *
 import PLC.Methods
 from PLC.Config import Config
 from PLC.Faults import *
 import PLC.Methods
+import PLC.Legacy
+
+def import_deep(name):
+    mod = __import__(name)
+    components = name.split('.')
+    for comp in components[1:]:
+        mod = getattr(mod, comp)
+    return mod
 
 class PLCAPI:
 
 class PLCAPI:
+
+    # flat list of method names
     methods = PLC.Methods.methods
 
     methods = PLC.Methods.methods
 
+    # dict {methodname:module}
+    legacy_map={}
+    for module in PLC.Legacy.__all__ :
+        for method in getattr(import_deep("PLC.Legacy."+module),"methods"):
+            legacy_map[method]=module
+
+    all_methods = methods+legacy_map.keys()
+    
     def __init__(self, config = "/etc/planetlab/plc_config", encoding = "utf-8"):
         self.encoding = encoding
 
     def __init__(self, config = "/etc/planetlab/plc_config", encoding = "utf-8"):
         self.encoding = encoding
 
@@ -111,14 +129,19 @@ class PLCAPI:
         """
 
         # Look up method
         """
 
         # Look up method
-        if method not in self.methods:
+        if method not in self.all_methods:
             raise PLCInvalidAPIMethod, method
             raise PLCInvalidAPIMethod, method
-
+        
         # Get new instance of method
         try:
             classname = method.split(".")[-1]
         # Get new instance of method
         try:
             classname = method.split(".")[-1]
-            module = __import__("PLC.Methods." + method, globals(), locals(), [classname])
-            return getattr(module, classname)(self)
+            if method in self.methods:
+                module = __import__("PLC.Methods." + method, globals(), locals(), [classname])
+                return getattr(module, classname)(self)
+            else:
+                modulename=self.legacy_map[method]
+                module = __import__("PLC.Legacy." + modulename, globals(), locals(), [classname])
+                return getattr(module, classname)(self)
         except ImportError, AttributeError:
             raise PLCInvalidAPIMethod, method
 
         except ImportError, AttributeError:
             raise PLCInvalidAPIMethod, method
 
diff --git a/PLC/Legacy/NodeNetworks.py b/PLC/Legacy/NodeNetworks.py
new file mode 100644 (file)
index 0000000..7e805e5
--- /dev/null
@@ -0,0 +1,78 @@
+# Thierry Parmentelat - INRIA
+# $Id$
+
+from PLC.Method import Method
+
+def import_deep(name):
+    mod = __import__(name)
+    components = name.split('.')
+    for comp in components[1:]:
+        mod = getattr(mod, comp)
+    return mod
+
+methods = [
+    "AddNodeNetwork",
+    "AddNodeNetworkSetting",
+    "DeleteNodeNetwork",
+    "DeleteNodeNetworkSetting",
+    "GetNodeNetworkSettings",
+    "GetNodeNetworks",
+    "UpdateNodeNetwork",
+    "UpdateNodeNetworkSetting",
+]
+
+# does any required renaming
+def rename (x):
+    if x=='nodenetwork_id':
+        return 'interface_id'
+    if x=='nodenetwork_ids':
+        return 'interface_ids'
+    else:
+        return x
+
+# apply rename on list (columns) or dict (filter) args
+def patch_legacy_arg (arg):
+    if isinstance(arg,list):
+        return [rename(x) for x in arg]
+    if isinstance(arg,dict):
+        return dict ( [ (rename(k),v) for (k,v) in arg.iteritems() ] )
+    return arg
+
+def factory (legacyname):
+    # new method name
+    newname=legacyname.replace("NodeNetwork","Interface")
+    # locate new class
+    newclass=getattr(import_deep("PLC.Methods."+newname),newname)
+    # create class for legacy name
+    legacyclass = type(legacyname,(newclass,), 
+                       {"__doc__":"Legacy method - please use %s instead"%newname})
+    # xxx should rewrite 'call' to handle any argument using nodenetwork_id(s)
+    for internal in ["roles","accepts","returns"]:
+        setattr(legacyclass,internal,getattr(newclass,internal))
+    # turn off type checking, as introspection code fails on wrapped_call
+    setattr(legacyclass,"skip_typecheck",True)
+    # rewrite call
+    def wrapped_call (self,auth,*args, **kwds):
+        newargs=[patch_legacy_arg(x) for x in args]
+        newkwds=dict ( [ (k,patch_legacy_arg(v)) for (k,v) in kwds.iteritems() ] )
+        return getattr(newclass,"call")(self,auth,*newargs,**newkwds)
+    setattr(legacyclass,"call",wrapped_call)
+
+    return legacyclass
+
+# this does not work, as __module__ is not yet supported
+#for legacyname in methods:
+#   setattr(__module__,legacyname,factory(legacyname))
+# this does not work either, as we are importing the module so we cannot locate it yet
+#for legacyname in methods:
+#    this_module = import_deep(__name__)
+#    setattr(__module__,legacyname,factory(legacyname))
+
+AddNodeNetwork=factory("AddNodeNetwork")
+AddNodeNetworkSetting=factory("AddNodeNetworkSetting")
+DeleteNodeNetwork=factory("DeleteNodeNetwork")
+DeleteNodeNetworkSetting=factory("DeleteNodeNetworkSetting")
+GetNodeNetworkSettings=factory("GetNodeNetworkSettings")
+GetNodeNetworks=factory("GetNodeNetworks")
+UpdateNodeNetwork=factory("UpdateNodeNetwork")
+UpdateNodeNetworkSetting=factory("UpdateNodeNetworkSetting")
diff --git a/PLC/Legacy/Types.py b/PLC/Legacy/Types.py
new file mode 100644 (file)
index 0000000..71b2309
--- /dev/null
@@ -0,0 +1,69 @@
+# Thierry Parmentelat - INRIA
+# $Id$
+
+from PLC.Method import Method
+
+def import_deep(name):
+    mod = __import__(name)
+    components = name.split('.')
+    for comp in components[1:]:
+        mod = getattr(mod, comp)
+    return mod
+
+map = {
+    "AddSliceAttributeType"         : "AddTagType",
+    "DeleteSliceAttributeType"      : "DeleteTagType",
+    "GetSliceAttributeTypes"        : "GetTagTypes",
+    "UpdateSliceAttributeType"      : "UpdateTagType",
+    "AddNodeNetworkSettingType"     : "AddTagType",
+    "DeleteNodeNetworkSettingType"  : "DeleteTagType",
+    "GetNodeNetworkSettingTypes"    : "GetTagTypes",
+    "UpdateNodeNetworkSettingType"  : "UpdateTagType",
+}    
+
+methods = map.keys()
+
+# does any required renaming
+def rename (x):
+    if x=='name':
+        return 'tagname'
+    else:
+        return x
+
+# apply rename on list (columns) or dict (filter) args
+def patch_legacy_arg (arg):
+    if isinstance(arg,list):
+        return [rename(x) for x in arg]
+    if isinstance(arg,dict):
+        return dict ( [ (rename(k),v) for (k,v) in arg.iteritems() ] )
+    return arg
+
+def factory (legacyname, newname):
+    # locate new class
+    newclass=getattr(import_deep("PLC.Methods."+newname),newname)
+    # create class for legacy name
+    legacyclass = type(legacyname,(newclass,), 
+                       {"__doc__":"Legacy method - please use %s instead"%newname})
+    for internal in ["roles","accepts","returns"]:
+        setattr(legacyclass,internal,getattr(newclass,internal))
+    # turn off type checking, as introspection code fails on wrapped_call
+    setattr(legacyclass,"skip_typecheck",True)
+    # rewrite call
+    def wrapped_call (self,auth,*args, **kwds):
+        newargs=[patch_legacy_arg(x) for x in args]
+        newkwds=dict ( [ (k,patch_legacy_arg(v)) for (k,v) in kwds.iteritems() ] )
+        return getattr(newclass,"call")(self,auth,*newargs,**newkwds)
+    setattr(legacyclass,"call",wrapped_call)
+
+    return legacyclass
+
+# see NodeNetworks for attempts to avoid this
+
+AddSliceAttributeType=factory("AddSliceAttributeType","AddTagType")
+DeleteSliceAttributeType=factory("DeleteSliceAttributeType","DeleteTagType")
+GetSliceAttributeTypes=factory("GetSliceAttributeTypes","GetTagTypes")
+UpdateSliceAttributeType=factory("UpdateSliceAttributeType","UpdateTagType")
+AddNodeNetworkSettingType=factory("AddNodeNetworkSettingType","AddTagType")
+DeleteNodeNetworkSettingType=factory("DeleteNodeNetworkSettingType","DeleteTagType")
+GetNodeNetworkSettingTypes=factory("GetNodeNetworkSettingTypes","GetTagTypes")
+UpdateNodeNetworkSettingType=factory("UpdateNodeNetworkSettingType","UpdateTagType")
diff --git a/PLC/Legacy/__init__.py b/PLC/Legacy/__init__.py
new file mode 100644 (file)
index 0000000..64896ac
--- /dev/null
@@ -0,0 +1,11 @@
+# supposed to be managed manually
+# syntax is modulename.methodname
+
+# xxx could we use __all__ and have each module define its 
+# own set of methods ?
+
+__all__ = """
+NodeNetworks
+Types
+""".split()
+
index 71977e6..a7f070e 100644 (file)
@@ -24,7 +24,8 @@ from PLC.Events import Event, Events
 from PLC.Nodes import Node, Nodes
 from PLC.Persons import Person, Persons
 
 from PLC.Nodes import Node, Nodes
 from PLC.Persons import Person, Persons
 
-class Method:
+# we inherit object because we use new-style classes for legacy methods
+class Method (object):
     """
     Base class for all PLCAPI functions. At a minimum, all PLCAPI
     functions must define:
     """
     Base class for all PLCAPI functions. At a minimum, all PLCAPI
     functions must define:
@@ -80,14 +81,17 @@ class Method:
 
         try:
            start = time.time()
 
         try:
            start = time.time()
-            (min_args, max_args, defaults) = self.args()
+
+            # legacy code cannot be type-checked, due to the way Method.args() works
+            if not hasattr(self,"skip_typecheck"):
+                (min_args, max_args, defaults) = self.args()
                                
                                
-           # Check that the right number of arguments were passed in
-            if len(args) < len(min_args) or len(args) > len(max_args):
-                raise PLCInvalidArgumentCount(len(args), len(min_args), len(max_args))
+                # Check that the right number of arguments were passed in
+                if len(args) < len(min_args) or len(args) > len(max_args):
+                    raise PLCInvalidArgumentCount(len(args), len(min_args), len(max_args))
 
 
-            for name, value, expected in zip(max_args, args, self.accepts):
-                self.type_check(name, value, expected, args)
+                for name, value, expected in zip(max_args, args, self.accepts):
+                    self.type_check(name, value, expected, args)
        
            result = self.call(*args, **kwds)
            runtime = time.time() - start
        
            result = self.call(*args, **kwds)
            runtime = time.time() - start
@@ -253,7 +257,7 @@ class Method:
         That represents the minimum and maximum sets of arguments that
         this function accepts and the defaults for the optional arguments.
         """
         That represents the minimum and maximum sets of arguments that
         this function accepts and the defaults for the optional arguments.
         """
-
+        
         # Inspect call. Remove self from the argument list.
         max_args = self.call.func_code.co_varnames[1:self.call.func_code.co_argcount]
         defaults = self.call.func_defaults
         # Inspect call. Remove self from the argument list.
         max_args = self.call.func_code.co_varnames[1:self.call.func_code.co_argcount]
         defaults = self.call.func_defaults
index 6e4a77b..3ed0a75 100644 (file)
@@ -165,7 +165,7 @@ class Shell:
             if role is not None:
                 self.auth['Role'] = role
 
             if role is not None:
                 self.auth['Role'] = role
 
-        for method in PLC.Methods.methods:
+        for method in PLC.Methods.methods + PLC.API.PLCAPI.legacy_map.keys():
             api_function = self.api.callable(method)
 
             if self.server is None:
             api_function = self.api.callable(method)
 
             if self.server is None: