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
+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:
+
+    # flat list of method names
     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
 
@@ -111,14 +129,19 @@ class PLCAPI:
         """
 
         # Look up method
-        if method not in self.methods:
+        if method not in self.all_methods:
             raise PLCInvalidAPIMethod, method
-
+        
         # 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
 
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
 
-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:
@@ -80,14 +81,17 @@ class Method:
 
         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
@@ -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.
         """
-
+        
         # 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
 
-        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: