step2 : basic functions for handling nodetags and nodegroups - still highly volatile
authorThierry Parmentelat <thierry.parmentelat@sophia.inria.fr>
Fri, 23 May 2008 10:25:13 +0000 (10:25 +0000)
committerThierry Parmentelat <thierry.parmentelat@sophia.inria.fr>
Fri, 23 May 2008 10:25:13 +0000 (10:25 +0000)
31 files changed:
PLC/.cvsignore [deleted file]
PLC/InitScripts.py
PLC/Methods/AddConfFileToNodeGroup.py
PLC/Methods/AddNodeGroup.py
PLC/Methods/AddNodeTag.py [new file with mode: 0644]
PLC/Methods/AddNodeTagType.py [new file with mode: 0644]
PLC/Methods/AddSliceAttribute.py
PLC/Methods/DeleteConfFileFromNodeGroup.py
PLC/Methods/DeleteInitScript.py
PLC/Methods/DeleteNodeGroup.py
PLC/Methods/DeleteNodeTag.py [new file with mode: 0644]
PLC/Methods/DeleteNodeTagType.py [new file with mode: 0644]
PLC/Methods/GetInterfaceSettings.py
PLC/Methods/GetNodeGroups.py
PLC/Methods/GetNodeTagTypes.py [new file with mode: 0644]
PLC/Methods/GetNodeTags.py [new file with mode: 0644]
PLC/Methods/GetSlivers.py
PLC/Methods/UpdateNodeGroup.py
PLC/Methods/UpdateNodeTag.py [new file with mode: 0644]
PLC/Methods/UpdateNodeTagType.py [new file with mode: 0644]
PLC/Methods/__init__.py
PLC/NodeGroups.py
PLC/NodeTagTypes.py [new file with mode: 0644]
PLC/NodeTags.py [new file with mode: 0644]
PLC/Nodes.py
PLC/Test.py
PLC/__init__.py
migrations/migrate-v4-to-v5.sh
migrations/migrate-v4-to-v5.sql [new file with mode: 0644]
php/.cvsignore [deleted file]
planetlab5.sql

diff --git a/PLC/.cvsignore b/PLC/.cvsignore
deleted file mode 100644 (file)
index 0d20b64..0000000
+++ /dev/null
@@ -1 +0,0 @@
-*.pyc
index 9f864d2..028293b 100644 (file)
@@ -43,7 +43,7 @@ class InitScript(Row):
 
 class InitScripts(Table):
     """
-    Representation of the initscipts table in the database.
+    Representation of the initscripts table in the database.
     """
 
     def __init__(self, api, initscript_filter = None, columns = None):
index 6ff642c..25ea249 100644 (file)
@@ -20,7 +20,7 @@ class AddConfFileToNodeGroup(Method):
         Auth(),
         ConfFile.fields['conf_file_id'],
         Mixed(NodeGroup.fields['nodegroup_id'],
-              NodeGroup.fields['name'])
+              NodeGroup.fields['groupname'])
         ]
 
     returns = Parameter(int, '1 if successful')
index 34f5f97..9555f73 100644 (file)
@@ -4,8 +4,7 @@ from PLC.Parameter import Parameter, Mixed
 from PLC.NodeGroups import NodeGroup, NodeGroups
 from PLC.Auth import Auth
 
-can_update = lambda (field, value): field in \
-             ['name', 'description']
+can_update = lambda (field, value): field in NodeGroup.fields.keys() and field != NodeGroup.primary_field
 
 class AddNodeGroup(Method):
     """
@@ -28,7 +27,7 @@ class AddNodeGroup(Method):
 
 
     def call(self, auth, nodegroup_fields):
-        nodegroup_fields = dict(filter(can_update, nodegroup_fields.items()))
+        nodegroup_fields = dict([f for f in nodegroup_fields.items() if can_update(f)])
         nodegroup = NodeGroup(self.api, nodegroup_fields)
         nodegroup.sync()
 
diff --git a/PLC/Methods/AddNodeTag.py b/PLC/Methods/AddNodeTag.py
new file mode 100644 (file)
index 0000000..e6db8ed
--- /dev/null
@@ -0,0 +1,89 @@
+#
+# Thierry Parmentelat - INRIA
+#
+# $Revision: 9423 $
+#
+from PLC.Faults import *
+from PLC.Method import Method
+from PLC.Parameter import Parameter, Mixed
+from PLC.Auth import Auth
+
+from PLC.NodeTagTypes import NodeTagType, NodeTagTypes
+from PLC.NodeTags import NodeTag, NodeTags
+from PLC.Nodes import Node, Nodes
+
+from PLC.Nodes import Nodes
+from PLC.Sites import Sites
+
+class AddNodeTag(Method):
+    """
+    Sets the specified tag for the specified node
+    to the specified value.
+
+    In general only tech(s), PI(s) and of course admin(s) are allowed to
+    do the change, but this is defined in the node tag type object.
+
+    Returns the new node_tag_id (> 0) if successful, faults
+    otherwise.
+    """
+
+    roles = ['admin', 'pi', 'tech', 'user']
+
+    accepts = [
+        Auth(),
+        # no other way to refer to a node
+        NodeTag.fields['node_id'],
+        Mixed(NodeTagType.fields['node_tag_type_id'],
+              NodeTagType.fields['name']),
+        NodeTag.fields['value'],
+        ]
+
+    returns = Parameter(int, 'New node_tag_id (> 0) if successful')
+
+    object_type = 'Node'
+
+
+    def call(self, auth, node_id, node_tag_type_id_or_name, value):
+        nodes = Nodes(self.api, [node_id])
+        if not nodes:
+            raise PLCInvalidArgument, "No such node %r"%node_id
+        node = nodes[0]
+
+        node_tag_types = NodeTagTypes(self.api, [node_tag_type_id_or_name])
+        if not node_tag_types:
+            raise PLCInvalidArgument, "No such node tag type %r"%node_tag_type_id_or_name
+        node_tag_type = node_tag_types[0]
+
+       # checks for existence - does not allow several different tags
+        conflicts = NodeTags(self.api,
+                                        {'node_id':node['node_id'],
+                                         'node_tag_type_id':node_tag_type['node_tag_type_id']})
+
+        if len(conflicts) :
+            raise PLCInvalidArgument, "Node %d already has tag %d"%(node['node_id'],
+                                                                               node_tag_type['node_tag_type_id'])
+
+       # check permission : it not admin, is the user affiliated with the right site
+       if 'admin' not in self.caller['roles']:
+           # locate node
+           node = Nodes (self.api,[node['node_id']])[0]
+           # locate site
+           site = Sites (self.api, [node['site_id']])[0]
+           # check caller is affiliated with this site
+           if self.caller['person_id'] not in site['person_ids']:
+               raise PLCPermissionDenied, "Not a member of the hosting site %s"%site['abbreviated_site']
+           
+           required_min_role = node_tag_type ['min_role_id']
+           if required_min_role is not None and \
+                   min(self.caller['role_ids']) > required_min_role:
+               raise PLCPermissionDenied, "Not allowed to modify the specified node tag, requires role %d",required_min_role
+
+        node_tag = NodeTag(self.api)
+        node_tag['node_id'] = node['node_id']
+        node_tag['node_tag_type_id'] = node_tag_type['node_tag_type_id']
+        node_tag['value'] = value
+
+        node_tag.sync()
+       self.object_ids = [node_tag['node_tag_id']]
+
+        return node_tag['node_tag_id']
diff --git a/PLC/Methods/AddNodeTagType.py b/PLC/Methods/AddNodeTagType.py
new file mode 100644 (file)
index 0000000..c95f520
--- /dev/null
@@ -0,0 +1,45 @@
+#
+# Thierry Parmentelat - INRIA
+#
+# $Revision: 9423 $
+#
+
+
+from PLC.Faults import *
+from PLC.Method import Method
+from PLC.Parameter import Parameter, Mixed
+from PLC.NodeTagTypes import NodeTagType, NodeTagTypes
+from PLC.Auth import Auth
+
+can_update = lambda (field, value): field in \
+             ['name', 'description', 'category', 'min_role_id']
+
+class AddNodeTagType(Method):
+    """
+    Adds a new type of node tag.
+    Any fields specified are used, otherwise defaults are used.
+
+    Returns the new node_tag_id (> 0) if successful,
+    faults otherwise.
+    """
+
+    roles = ['admin']
+
+    node_tag_type_fields = dict(filter(can_update, NodeTagType.fields.items()))
+
+    accepts = [
+        Auth(),
+        node_tag_type_fields
+        ]
+
+    returns = Parameter(int, 'New node_tag_id (> 0) if successful')
+
+
+    def call(self, auth, node_tag_type_fields):
+        node_tag_type_fields = dict(filter(can_update, node_tag_type_fields.items()))
+        node_tag_type = NodeTagType(self.api, node_tag_type_fields)
+        node_tag_type.sync()
+
+       self.object_ids = [node_tag_type['node_tag_type_id']]
+
+        return node_tag_type['node_tag_type_id']
index ad32437..8102fe1 100644 (file)
@@ -38,7 +38,7 @@ class AddSliceAttribute(Method):
               Node.fields['hostname'],
              None),
        Mixed(NodeGroup.fields['nodegroup_id'],
-              NodeGroup.fields['name'])
+              NodeGroup.fields['groupname'])
         ]
 
     returns = Parameter(int, 'New slice_attribute_id (> 0) if successful')
index 5504b0f..7e3eca5 100644 (file)
@@ -19,7 +19,7 @@ class DeleteConfFileFromNodeGroup(Method):
         Auth(),
         ConfFile.fields['conf_file_id'],
         Mixed(NodeGroup.fields['nodegroup_id'],
-              NodeGroup.fields['name'])
+              NodeGroup.fields['groupname'])
         ]
 
     returns = Parameter(int, '1 if successful')
index 47a9993..7c4b01e 100644 (file)
@@ -8,21 +8,22 @@ class DeleteInitScript(Method):
     """
     Deletes an existing initscript.  
     
-    Returns 1 if successfuli, faults otherwise. 
+    Returns 1 if successful, faults otherwise. 
     """
 
     roles = ['admin']
 
     accepts = [
         Auth(),
-        InitScript.fields['initscript_id']
+        Mixed(InitScript.fields['initscript_id'],
+              InitScript.fields['name']),
         ]
 
     returns = Parameter(int, '1 if successful')
     
 
-    def call(self, auth, initscript_id):
-        initscripts = InitScripts(self.api, [initscript_id])
+    def call(self, auth, initscript_id_or_name):
+        initscripts = InitScripts(self.api, [initscript_id_or_name])
         if not initscripts:
             raise PLCInvalidArgument, "No such initscript"
 
index 7650150..7e65ad6 100644 (file)
@@ -18,7 +18,7 @@ class DeleteNodeGroup(Method):
     accepts = [
         Auth(),
         Mixed(NodeGroup.fields['nodegroup_id'],
-             NodeGroup.fields['name'])
+             NodeGroup.fields['groupname'])
         ]
 
     returns = Parameter(int, '1 if successful')
diff --git a/PLC/Methods/DeleteNodeTag.py b/PLC/Methods/DeleteNodeTag.py
new file mode 100644 (file)
index 0000000..b7bfd3f
--- /dev/null
@@ -0,0 +1,73 @@
+#
+# Thierry Parmentelat - INRIA
+#
+# $Revision: 9423 $
+#
+
+from PLC.Faults import *
+from PLC.Method import Method
+from PLC.Parameter import Parameter, Mixed
+from PLC.Auth import Auth
+
+from PLC.NodeTags import NodeTag, NodeTags
+from PLC.Nodes import Node, Nodes
+
+from PLC.Nodes import Node, Nodes
+from PLC.Sites import Site, Sites
+
+class DeleteNodeTag(Method):
+    """
+    Deletes the specified node tag
+
+    Attributes may require the caller to have a particular role in order
+    to be deleted, depending on the related node tag type.
+    Admins may delete attributes of any slice or sliver.
+
+    Returns 1 if successful, faults otherwise.
+    """
+
+    roles = ['admin', 'pi', 'user']
+
+    accepts = [
+        Auth(),
+        NodeTag.fields['node_tag_id']
+        ]
+
+    returns = Parameter(int, '1 if successful')
+
+    object_type = 'Node'
+
+
+    def call(self, auth, node_tag_id):
+        node_tags = NodeTags(self.api, [node_tag_id])
+        if not node_tags:
+            raise PLCInvalidArgument, "No such node tag %r"%node_tag_id
+        node_tag = node_tags[0]
+
+        ### reproducing a check from UpdateSliceAttribute, looks dumb though
+        nodes = Nodes(self.api, [node_tag['node_id']])
+        if not nodes:
+            raise PLCInvalidArgument, "No such node %r"%node_tag['node_id']
+        node = nodes[0]
+
+        assert node_tag['node_tag_id'] in node['tag_ids']
+
+       # check permission : it not admin, is the user affiliated with the right site
+       if 'admin' not in self.caller['roles']:
+           # locate node
+           node = Nodes (self.api,[node['node_id']])[0]
+           # locate site
+           site = Sites (self.api, [node['site_id']])[0]
+           # check caller is affiliated with this site
+           if self.caller['person_id'] not in site['person_ids']:
+               raise PLCPermissionDenied, "Not a member of the hosting site %s"%site['abbreviated_site']
+           
+           required_min_role = node_tag_type ['min_role_id']
+           if required_min_role is not None and \
+                   min(self.caller['role_ids']) > required_min_role:
+               raise PLCPermissionDenied, "Not allowed to modify the specified node tag, requires role %d",required_min_role
+
+        node_tag.delete()
+       self.object_ids = [node_tag['node_tag_id']]
+
+        return 1
diff --git a/PLC/Methods/DeleteNodeTagType.py b/PLC/Methods/DeleteNodeTagType.py
new file mode 100644 (file)
index 0000000..16b750d
--- /dev/null
@@ -0,0 +1,39 @@
+#
+# Thierry Parmentelat - INRIA
+#
+# $Revision: 9423 $
+#
+from PLC.Faults import *
+from PLC.Method import Method
+from PLC.Parameter import Parameter, Mixed
+from PLC.NodeTagTypes import NodeTagType, NodeTagTypes
+from PLC.Auth import Auth
+
+class DeleteNodeTagType(Method):
+    """
+    Deletes the specified node tag type.
+
+    Returns 1 if successful, faults otherwise.
+    """
+
+    roles = ['admin']
+
+    accepts = [
+        Auth(),
+        Mixed(NodeTagType.fields['node_tag_type_id'],
+              NodeTagType.fields['name']),
+        ]
+
+    returns = Parameter(int, '1 if successful')
+
+
+    def call(self, auth, node_tag_type_id_or_name):
+        node_tag_types = NodeTagTypes(self.api, [node_tag_type_id_or_name])
+        if not node_tag_types:
+            raise PLCInvalidArgument, "No such node tag type"
+        node_tag_type = node_tag_types[0]
+
+        node_tag_type.delete()
+       self.object_ids = [node_tag_type['node_tag_type_id']]
+
+        return 1
index 779ef0c..c532d7c 100644 (file)
@@ -7,7 +7,6 @@ from PLC.Faults import *
 from PLC.Method import Method
 from PLC.Parameter import Parameter, Mixed
 from PLC.Filter import Filter
-from PLC.Persons import Person, Persons
 from PLC.Auth import Auth
 
 from PLC.InterfaceSettings import InterfaceSetting, InterfaceSettings
index f4927ef..5711217 100644 (file)
@@ -19,7 +19,7 @@ class GetNodeGroups(Method):
     accepts = [
         Auth(),
         Mixed([Mixed(NodeGroup.fields['nodegroup_id'],
-                     NodeGroup.fields['name'])],
+                     NodeGroup.fields['groupname'])],
               Filter(NodeGroup.fields)),
         Parameter([str], "List of fields to return", nullok = True)
         ]
diff --git a/PLC/Methods/GetNodeTagTypes.py b/PLC/Methods/GetNodeTagTypes.py
new file mode 100644 (file)
index 0000000..32a6787
--- /dev/null
@@ -0,0 +1,33 @@
+#
+# Thierry Parmentelat - INRIA
+#
+# $Revision: 9423 $
+#
+from PLC.Method import Method
+from PLC.Parameter import Parameter, Mixed
+from PLC.Filter import Filter
+from PLC.Auth import Auth
+from PLC.NodeTagTypes import NodeTagType, NodeTagTypes
+
+class GetNodeTagTypes(Method):
+    """
+    Returns an array of structs containing details about
+    node tag types.
+
+    The usual filtering scheme applies on this method.
+    """
+
+    roles = ['admin', 'pi', 'user', 'tech', 'node']
+
+    accepts = [
+        Auth(),
+        Mixed([Mixed(NodeTagType.fields['node_tag_type_id'],
+                     NodeTagType.fields['name'])],
+              Filter(NodeTagType.fields)),
+        Parameter([str], "List of fields to return", nullok = True)
+        ]
+
+    returns = [NodeTagType.fields]
+
+    def call(self, auth, node_tag_type_filter = None, return_fields = None):
+        return NodeTagTypes(self.api, node_tag_type_filter, return_fields)
diff --git a/PLC/Methods/GetNodeTags.py b/PLC/Methods/GetNodeTags.py
new file mode 100644 (file)
index 0000000..fd53dcd
--- /dev/null
@@ -0,0 +1,44 @@
+#
+# Thierry Parmentelat - INRIA
+#
+# $Revision: 9423 $
+#
+from PLC.Faults import *
+from PLC.Method import Method
+from PLC.Parameter import Parameter, Mixed
+from PLC.Filter import Filter
+from PLC.Auth import Auth
+
+from PLC.NodeTags import NodeTag, NodeTags
+from PLC.Sites import Site, Sites
+from PLC.Nodes import Node, Nodes
+
+class GetNodeTags(Method):
+    """
+    Returns an array of structs containing details about
+    nodes and related tags.
+
+    If node_tag_filter is specified and is an array of
+    node tag identifiers, only node tags matching
+    the filter will be returned. If return_fields is specified, only
+    the specified details will be returned.
+    """
+
+    roles = ['admin', 'pi', 'user', 'node']
+
+    accepts = [
+        Auth(),
+        Mixed([NodeTag.fields['node_tag_id']],
+              Parameter(int,"Node tag id"),
+              Filter(NodeTag.fields)),
+        Parameter([str], "List of fields to return", nullok = True)
+        ]
+
+    returns = [NodeTag.fields]
+    
+
+    def call(self, auth, node_tag_filter = None, return_fields = None):
+
+        node_tags = NodeTags(self.api, node_tag_filter, return_fields)
+
+        return node_tags
index 5624c0c..7f936ea 100644 (file)
@@ -127,7 +127,7 @@ class GetSlivers(Method):
         'node_id': Node.fields['node_id'],
         'hostname': Node.fields['hostname'],
         'networks': [Interface.fields],
-        'groups': [NodeGroup.fields['name']],
+        'groups': [NodeGroup.fields['groupname']],
         'conf_files': [ConfFile.fields],
        'initscripts': [InitScript.fields],
         'slivers': [{
index c84c7f1..dddca60 100644 (file)
@@ -23,7 +23,7 @@ class UpdateNodeGroup(Method):
     accepts = [
         Auth(),
         Mixed(NodeGroup.fields['nodegroup_id'],
-             NodeGroup.fields['name']),
+             NodeGroup.fields['groupname']),
         nodegroup_fields
         ]
 
diff --git a/PLC/Methods/UpdateNodeTag.py b/PLC/Methods/UpdateNodeTag.py
new file mode 100644 (file)
index 0000000..55857b2
--- /dev/null
@@ -0,0 +1,72 @@
+#
+# Thierry Parmentelat - INRIA
+#
+# $Revision: 9423 $
+#
+
+from PLC.Faults import *
+from PLC.Method import Method
+from PLC.Parameter import Parameter, Mixed
+from PLC.Auth import Auth
+
+from PLC.NodeTags import NodeTag, NodeTags
+from PLC.Nodes import Node, Nodes
+
+from PLC.Nodes import Nodes
+from PLC.Sites import Sites
+
+class UpdateNodeTag(Method):
+    """
+    Updates the value of an existing node tag
+
+    Access rights depend on the node tag type.
+
+    Returns 1 if successful, faults otherwise.
+    """
+
+    roles = ['admin', 'pi', 'tech', 'user']
+
+    accepts = [
+        Auth(),
+        NodeTag.fields['node_tag_id'],
+        NodeTag.fields['value']
+        ]
+
+    returns = Parameter(int, '1 if successful')
+
+    object_type = 'Node'
+
+    def call(self, auth, node_tag_id, value):
+        node_tags = NodeTags(self.api, [node_tag_id])
+        if not node_tags:
+            raise PLCInvalidArgument, "No such node tag %r"%node_tag_id
+        node_tag = node_tags[0]
+
+        ### reproducing a check from UpdateSliceAttribute, looks dumb though
+        nodes = Nodes(self.api, [node_tag['node_id']])
+        if not nodes:
+            raise PLCInvalidArgument, "No such node %r"%node_tag['node_id']
+        node = nodes[0]
+
+        assert node_tag['node_tag_id'] in node['tag_ids']
+
+       # check permission : it not admin, is the user affiliated with the right site
+       if 'admin' not in self.caller['roles']:
+           # locate node
+           node = Nodes (self.api,[node['node_id']])[0]
+           # locate site
+           site = Sites (self.api, [node['site_id']])[0]
+           # check caller is affiliated with this site
+           if self.caller['person_id'] not in site['person_ids']:
+               raise PLCPermissionDenied, "Not a member of the hosting site %s"%site['abbreviated_site']
+           
+           required_min_role = node_tag_type ['min_role_id']
+           if required_min_role is not None and \
+                   min(self.caller['role_ids']) > required_min_role:
+               raise PLCPermissionDenied, "Not allowed to modify the specified node tag, requires role %d",required_min_role
+
+        node_tag['value'] = value
+        node_tag.sync()
+
+       self.object_ids = [node_tag['node_tag_id']]
+        return 1
diff --git a/PLC/Methods/UpdateNodeTagType.py b/PLC/Methods/UpdateNodeTagType.py
new file mode 100644 (file)
index 0000000..952b3ff
--- /dev/null
@@ -0,0 +1,48 @@
+#
+# Thierry Parmentelat - INRIA
+#
+# $Revision: 9423 $
+#
+from PLC.Faults import *
+from PLC.Method import Method
+from PLC.Parameter import Parameter, Mixed
+from PLC.NodeTagTypes import NodeTagType, NodeTagTypes
+from PLC.Auth import Auth
+
+can_update = lambda (field, value): field in \
+             ['name', 'description', 'category', 'min_role_id']
+
+class UpdateNodeTagType(Method):
+    """
+    Updates the parameters of an existing tag type
+    with the values in node_tag_type_fields.
+
+    Returns 1 if successful, faults otherwise.
+    """
+
+    roles = ['admin']
+
+    node_tag_type_fields = dict(filter(can_update, NodeTagType.fields.items()))
+
+    accepts = [
+        Auth(),
+        Mixed(NodeTagType.fields['node_tag_type_id'],
+              NodeTagType.fields['name']),
+        node_tag_type_fields
+        ]
+
+    returns = Parameter(int, '1 if successful')
+
+    def call(self, auth, node_tag_type_id_or_name, node_tag_type_fields):
+        node_tag_type_fields = dict(filter(can_update, node_tag_type_fields.items()))
+
+        node_tag_types = NodeTagTypes(self.api, [node_tag_type_id_or_name])
+        if not node_tag_types:
+            raise PLCInvalidArgument, "No such tag type"
+        node_tag_type = node_tag_types[0]
+
+        node_tag_type.update(node_tag_type_fields)
+        node_tag_type.sync()
+       self.object_ids = [node_tag_type['node_tag_type_id']]
+
+        return 1
index 86731b5..8ed5c3e 100644 (file)
@@ -15,6 +15,8 @@ AddNetworkMethod
 AddNetworkType
 AddNode
 AddNodeGroup
+AddNodeTag
+AddNodeTagType
 AddNodeToPCU
 AddPCU
 AddPCUProtocolType
@@ -60,6 +62,8 @@ DeleteNetworkType
 DeleteNode
 DeleteNodeFromPCU
 DeleteNodeGroup
+DeleteNodeTag
+DeleteNodeTagType
 DeletePCU
 DeletePCUProtocolType
 DeletePCUType
@@ -95,6 +99,8 @@ GetMessages
 GetNetworkMethods
 GetNetworkTypes
 GetNodeGroups
+GetNodeTagTypes
+GetNodeTags
 GetNodes
 GetPCUProtocolTypes
 GetPCUTypes
@@ -150,6 +156,8 @@ UpdateKey
 UpdateMessage
 UpdateNode
 UpdateNodeGroup
+UpdateNodeTag
+UpdateNodeTagType
 UpdatePCU
 UpdatePCUProtocolType
 UpdatePCUType
index fdc3ba9..6ec7a4f 100644 (file)
@@ -25,17 +25,17 @@ class NodeGroup(Row):
 
     table_name = 'nodegroups'
     primary_key = 'nodegroup_id'
-    join_tables = ['nodegroup_node', 'conf_file_nodegroup']
+    join_tables = ['conf_file_nodegroup']
+    primary_field = 'nodegroup_id'
     fields = {
         'nodegroup_id': Parameter(int, "Node group identifier"),
-        'name': Parameter(str, "Node group name", max = 50),
-        'tag_name' : Parameter(str, "Tag name that the nodegroup definition is based upon"),
-        'tag_value' : Parameter(str, "value that the nodegroup definition is based upon"),
-#        'node_ids': Parameter([int], "List of nodes in this node group"),
-#        'conf_file_ids': Parameter([int], "List of configuration files specific to this node group"),
+        'groupname': Parameter(str, "Node group name", max = 50),
+        'node_tag_type_id': Parameter (int, "Node tag type id"),
+        'value' : Parameter(str, "value that the nodegroup definition is based upon"),
         }
     related_fields = {
-       'conf_files': [Parameter(int, "ConfFile identifier")],
+        'name' : Parameter(str, "Tag name that the nodegroup definition is based upon"),
+        'conf_file_ids': Parameter([int], "List of configuration files specific to this node group"),
        }
 
     def validate_name(self, name):
@@ -70,9 +70,11 @@ class NodeGroup(Row):
             stale_conf_files = set(self['conf_file_ids']).difference(conf_file_ids)
 
             for new_conf_file in new_conf_files:
-                AddConfFileToNodeGroup.__call__(AddConfFileToNodeGroup(self.api), auth, new_conf_file, self['nodegroup_id'])
+                AddConfFileToNodeGroup.__call__(AddConfFileToNodeGroup(self.api), 
+                                                auth, new_conf_file, self['nodegroup_id'])
             for stale_conf_file in stale_conf_files:
-                DeleteConfFileFromNodeGroup.__call__(DeleteConfFileFromNodeGroup(self.api), auth, stale_conf_file, self['nodegroup_id'])
+                DeleteConfFileFromNodeGroup.__call__(DeleteConfFileFromNodeGroup(self.api), 
+                                                     auth, stale_conf_file, self['nodegroup_id'])
 
 
 class NodeGroups(Table):
@@ -92,7 +94,7 @@ class NodeGroups(Table):
                 # Separate the list into integers and strings
                 ints = filter(lambda x: isinstance(x, (int, long)), nodegroup_filter)
                 strs = filter(lambda x: isinstance(x, StringTypes), nodegroup_filter)
-                nodegroup_filter = Filter(NodeGroup.fields, {'nodegroup_id': ints, 'name': strs})
+                nodegroup_filter = Filter(NodeGroup.fields, {'nodegroup_id': ints, 'groupname': strs})
                 sql += " AND (%s) %s" % nodegroup_filter.sql(api, "OR")
             elif isinstance(nodegroup_filter, dict):
                 nodegroup_filter = Filter(NodeGroup.fields, nodegroup_filter)
diff --git a/PLC/NodeTagTypes.py b/PLC/NodeTagTypes.py
new file mode 100644 (file)
index 0000000..0c05bc8
--- /dev/null
@@ -0,0 +1,83 @@
+#
+# Thierry Parmentelat - INRIA
+#
+# $Revision: 9423 $
+#
+from types import StringTypes
+
+from PLC.Faults import *
+from PLC.Parameter import Parameter
+from PLC.Filter import Filter
+from PLC.Table import Row, Table
+from PLC.Roles import Role, Roles
+
+class NodeTagType (Row):
+
+    """
+    Representation of a row in the node_tag_types table.
+    """
+
+    table_name = 'node_tag_types'
+    primary_key = 'node_tag_type_id'
+    join_tables = ['node_tag']
+    fields = {
+        'node_tag_type_id': Parameter(int, "Node tag type identifier"),
+        'name': Parameter(str, "Node tag type name", max = 100),
+        'description': Parameter(str, "Node tag type description", max = 254),
+        'category' : Parameter (str, "Node tag category", max=64),
+        'min_role_id': Parameter(int, "Minimum (least powerful) role that can set or change this attribute"),
+        }
+
+    # for Cache
+    class_key = 'name'
+    foreign_fields = ['category','description','min_role_id']
+    foreign_xrefs = []
+
+    def validate_name(self, name):
+        if not len(name):
+            raise PLCInvalidArgument, "node tag type name must be set"
+
+        conflicts = NodeTagTypes(self.api, [name])
+        for tag_type in conflicts:
+            if 'node_tag_type_id' not in self or \
+                   self['node_tag_type_id'] != tag_type['node_tag_type_id']:
+                raise PLCInvalidArgument, "node tag type name already in use"
+
+        return name
+
+    def validate_min_role_id(self, role_id):
+        roles = [row['role_id'] for row in Roles(self.api)]
+        if role_id not in roles:
+            raise PLCInvalidArgument, "Invalid role"
+
+        return role_id
+
+class NodeTagTypes(Table):
+    """
+    Representation of row(s) from the node_tag_types table
+    in the database.
+    """
+
+    def __init__(self, api, node_tag_type_filter = None, columns = None):
+        Table.__init__(self, api, NodeTagType, columns)
+
+        sql = "SELECT %s FROM node_tag_types WHERE True" % \
+              ", ".join(self.columns)
+
+        if node_tag_type_filter is not None:
+            if isinstance(node_tag_type_filter, (list, tuple, set)):
+                # Separate the list into integers and strings
+                ints = filter(lambda x: isinstance(x, (int, long)), node_tag_type_filter)
+                strs = filter(lambda x: isinstance(x, StringTypes), node_tag_type_filter)
+                node_tag_type_filter = Filter(NodeTagType.fields, {'node_tag_type_id': ints, 'name': strs})
+                sql += " AND (%s) %s" % node_tag_type_filter.sql(api, "OR")
+            elif isinstance(node_tag_type_filter, dict):
+                node_tag_type_filter = Filter(NodeTagType.fields, node_tag_type_filter)
+                sql += " AND (%s) %s" % node_tag_type_filter.sql(api, "AND")
+            elif isinstance (node_tag_type_filter, StringTypes):
+                node_tag_type_filter = Filter(NodeTagType.fields, {'name':[node_tag_type_filter]})
+                sql += " AND (%s) %s" % node_tag_type_filter.sql(api, "AND")
+            else:
+                raise PLCInvalidArgument, "Wrong node tag type filter %r"%node_tag_type_filter
+
+        self.selectall(sql)
diff --git a/PLC/NodeTags.py b/PLC/NodeTags.py
new file mode 100644 (file)
index 0000000..6577c91
--- /dev/null
@@ -0,0 +1,57 @@
+#
+# Thierry Parmentelat - INRIA
+#
+# $Revision: 9423 $
+#
+from PLC.Faults import *
+from PLC.Parameter import Parameter
+from PLC.Filter import Filter
+from PLC.Table import Row, Table
+from PLC.NodeTagTypes import NodeTagType, NodeTagTypes
+
+class NodeTag(Row):
+    """
+    Representation of a row in the node_tag.
+    To use, instantiate with a dict of values.
+    """
+
+    table_name = 'node_tag'
+    primary_key = 'node_tag_id'
+    fields = {
+        'node_tag_id': Parameter(int, "Node tag identifier"),
+        'node_id': Parameter(int, "Node identifier"),
+        'node_tag_type_id': NodeTagType.fields['node_tag_type_id'],
+        'name': NodeTagType.fields['name'],
+        'description': NodeTagType.fields['description'],
+        'category': NodeTagType.fields['category'],
+        'min_role_id': NodeTagType.fields['min_role_id'],
+        'value': Parameter(str, "Node tag value"),
+       ### relations
+       
+        }
+
+class NodeTags(Table):
+    """
+    Representation of row(s) from the node_tag table in the
+    database.
+    """
+
+    def __init__(self, api, node_tag_filter = None, columns = None):
+        Table.__init__(self, api, NodeTag, columns)
+
+        sql = "SELECT %s FROM view_node_tags WHERE True" % \
+              ", ".join(self.columns)
+
+        if node_tag_filter is not None:
+            if isinstance(node_tag_filter, (list, tuple, set)):
+                node_tag_filter = Filter(NodeTag.fields, {'node_tag_id': node_tag_filter})
+            elif isinstance(node_tag_filter, dict):
+                node_tag_filter = Filter(NodeTag.fields, node_tag_filter)
+            elif isinstance(node_tag_filter, int):
+                node_tag_filter = Filter(NodeTag.fields, {'node_tag_id': [node_tag_filter]})
+            else:
+                raise PLCInvalidArgument, "Wrong node tag filter %r"%node_tag_filter
+            sql += " AND (%s) %s" % node_tag_filter.sql(api)
+
+
+        self.selectall(sql)
index 01328e3..22c6860 100644 (file)
@@ -39,7 +39,9 @@ class Node(Row):
     table_name = 'nodes'
     primary_key = 'node_id'
     # Thierry -- we use delete on interfaces so the related InterfaceSettings get deleted too
-    join_tables = ['nodegroup_node', 'conf_file_node', 'pcu_node', 'slice_node', 'slice_attribute', 'node_session', 'peer_node','node_slice_whitelist']
+    join_tables = [ 'slice_node', 'peer_node', 'slice_attribute', 
+                    'node_session', 'node_slice_whitelist', 
+                    'node_tag', 'conf_file_node', 'pcu_node', ]
     fields = {
         'node_id': Parameter(int, "Node identifier"),
         'hostname': Parameter(str, "Fully qualified hostname", max = 255),
index 48e1618..241b3e2 100644 (file)
@@ -137,8 +137,8 @@ def random_key(key_types):
 
 def random_nodegroup():
     return {
-        'name': randstr(50),
-        'description': randstr(200),
+        'groupname': randstr(50),
+#        'description': randstr(200),
         }
 
 def random_node(boot_states):
index 10f6edf..96c4ec4 100644 (file)
@@ -24,6 +24,8 @@ Method
 NetworkMethods
 NetworkTypes
 NodeGroups
+NodeTagTypes
+NodeTags
 Nodes
 PCUProtocolTypes
 PCUTypes
index 1d04800..b854c79 100755 (executable)
@@ -1,5 +1,9 @@
 #!/bin/bash
 
+COMMAND=$(basename $0)
+BASENAME=$(basename $COMMAND .sh)
+DIRNAME=$(dirname $0)
+
 . /etc/planetlab/plc_config
 
 PLC_DB_USER
@@ -67,161 +71,12 @@ function main () {
 
 function migration_script () {
 
-    cat <<EOF
-
--- $Id$
---
--- this is the script to migrate from 4.2 to 5.0
---
-
-----------------------------------------
--- rename nodenetwork into interface
-----------------------------------------
-
-ALTER TABLE nodenetworks RENAME TO interfaces;
-ALTER TABLE interfaces RENAME COLUMN nodenetwork_id TO interface_id;
-
-ALTER INDEX nodenetworks_node_id_idx RENAME TO interfaces_node_id_idx;
-
-ALTER TABLE nodenetwork_setting_types RENAME TO interface_setting_types;
-ALTER TABLE interface_setting_types RENAME COLUMN nodenetwork_setting_type_id TO interface_setting_type_id;
-
-ALTER TABLE nodenetwork_setting RENAME TO interface_setting;
-
--- views
-ALTER TABLE nodenetworks_ordered RENAME TO interfaces_ordered;
-ALTER TABLE interfaces_ordered RENAME COLUMN nodenetwork_id TO interface_id;
-
-ALTER TABLE node_nodenetworks RENAME TO node_interfaces;
-ALTER TABLE node_interfaces RENAME COLUMN nodenetwork_ids TO interface_ids;
-
-ALTER TABLE nodenetwork_settings RENAME TO interface_settings;
-ALTER TABLE interface_settings RENAME COLUMN nodenetwork_id TO interface_id;
-ALTER TABLE interface_settings RENAME COLUMN nodenetwork_setting_ids TO setting_ids;
-
-ALTER TABLE view_nodenetwork_settings RENAME TO view_interface_settings;
-ALTER TABLE view_interface_settings RENAME COLUMN nodenetwork_setting_id TO interface_setting_id;
-ALTER TABLE view_interface_settings RENAME COLUMN nodenetwork_id TO interface_id;
-ALTER TABLE view_interface_settings RENAME COLUMN nodenetwork_setting_type_id TO interface_setting_type_id;
-
-ALTER TABLE view_nodenetworks RENAME TO view_interfaces;
-ALTER TABLE view_interfaces RENAME COLUMN nodenetwork_id TO interface_id;
-ALTER TABLE view_interfaces RENAME COLUMN nodenetwork_setting_ids TO setting_ids;
-
-ALTER TABLE view_nodes RENAME COLUMN nodenetwork_ids TO interface_ids;
-
-----------------------------------------
--- node tags
-----------------------------------------
-CREATE TABLE node_tag_types (
-
-    node_tag_type_id serial PRIMARY KEY,       -- ID
-    name text UNIQUE NOT NULL,                 -- Tag Name
-    description text,                          -- Optional Description
-    category text NOT NULL,                    -- Free text for grouping tags together
-    min_role_id integer REFERENCES roles       -- set minimal role required
-) WITH OIDS;
-
-CREATE TABLE node_tag (
-    node_tag_id serial PRIMARY KEY,            -- ID
-    node_id integer REFERENCES nodes NOT NULL, -- node id
-    node_tag_type_id integer REFERENCES node_tag_types,
-                                               -- tag type id
-    value text                                 -- value attached
-) WITH OIDS;
-
----------- related views
-CREATE OR REPLACE VIEW node_tags AS
-SELECT node_id,
-array_accum(node_tag_id) AS tag_ids
-FROM node_tag
-GROUP BY node_id;
-
-CREATE OR REPLACE VIEW view_node_tags AS
-SELECT
-node_tag.node_tag_id,
-node_tag.node_id,
-node_tag_types.node_tag_type_id,
-node_tag_types.name,
-node_tag_types.description,
-node_tag_types.category,
-node_tag_types.min_role_id,
-node_tag.value
-FROM node_tag 
-INNER JOIN node_tag_types USING (node_tag_type_id);
-
-DROP VIEW view_nodes;
-CREATE OR REPLACE VIEW view_nodes AS
-SELECT
-nodes.node_id,
-nodes.hostname,
-nodes.site_id,
-nodes.boot_state,
-nodes.deleted,
-nodes.model,
-nodes.boot_nonce,
-nodes.version,
-nodes.ssh_rsa_key,
-nodes.key,
-CAST(date_part('epoch', nodes.date_created) AS bigint) AS date_created,
-CAST(date_part('epoch', nodes.last_updated) AS bigint) AS last_updated,
-CAST(date_part('epoch', nodes.last_contact) AS bigint) AS last_contact,  
-peer_node.peer_id,
-peer_node.peer_node_id,
-COALESCE((SELECT interface_ids FROM node_interfaces WHERE node_interfaces.node_id = nodes.node_id), '{}') AS interface_ids,
-COALESCE((SELECT nodegroup_ids FROM node_nodegroups WHERE node_nodegroups.node_id = nodes.node_id), '{}') AS nodegroup_ids,
-COALESCE((SELECT slice_ids FROM node_slices WHERE node_slices.node_id = nodes.node_id), '{}') AS slice_ids,
-COALESCE((SELECT slice_ids_whitelist FROM node_slices_whitelist WHERE node_slices_whitelist.node_id = nodes.node_id), '{}') AS slice_ids_whitelist,
-COALESCE((SELECT pcu_ids FROM node_pcus WHERE node_pcus.node_id = nodes.node_id), '{}') AS pcu_ids,
-COALESCE((SELECT ports FROM node_pcus WHERE node_pcus.node_id = nodes.node_id), '{}') AS ports,
-COALESCE((SELECT conf_file_ids FROM node_conf_files WHERE node_conf_files.node_id = nodes.node_id), '{}') AS conf_file_ids,
-COALESCE((SELECT tag_ids FROM node_tags WHERE node_tags.node_id = nodes.node_id), '{}') AS tag_ids,
-node_session.session_id AS session
-FROM nodes
-LEFT JOIN peer_node USING (node_id)
-LEFT JOIN node_session USING (node_id);
-
-----------------------------------------
--- nodegroups
--- xxx - todo 
--- a more usable migration script would need to capture more data
-----------------------------------------
-DROP TABLE IF EXISTS nodegroups CASCADE;
-
--- Node groups
-CREATE TABLE nodegroups (
-    nodegroup_id serial PRIMARY KEY,                   -- Group identifier
-    name text UNIQUE NOT NULL,                         -- Group name
-    node_tag_type_id integer REFERENCES node_tag_types,        -- node is in nodegroup if it has this tag defined
-    value text                                         -- with value 'value'
-) WITH OIDS;
-
-CREATE OR REPLACE VIEW nodegroup_node AS
-SELECT nodegroup_id, node_id 
-FROM node_tag_types 
-JOIN node_tag 
-USING (node_tag_type_id) 
-JOIN nodegroups 
-USING (node_tag_type_id,value);
-
-CREATE OR REPLACE VIEW nodegroup_nodes AS
-SELECT nodegroup_id,
-array_accum(node_id) AS node_ids
-FROM nodegroup_node
-GROUP BY nodegroup_id;
-
--- Node groups that each node is a member of
-CREATE OR REPLACE VIEW node_nodegroups AS
-SELECT node_id,
-array_accum(nodegroup_id) AS nodegroup_ids
-FROM nodegroup_node
-GROUP BY node_id;
-
-----------------------------------------
--- update versioning
-----------------------------------------
-UPDATE plc_db_version SET version=5, subversion=0;
-
-EOF
-
+    sql_script=$DIRNAME/${BASENAME}.sql
+    if [ -f $sql_script ] ; then
+       cat $sql_script
+    else
+       echo Cannot locate ${BASENAME}.sql 
+       echo exiting 
+       exit 1
+    fi
 }
diff --git a/migrations/migrate-v4-to-v5.sql b/migrations/migrate-v4-to-v5.sql
new file mode 100644 (file)
index 0000000..0c9fb43
--- /dev/null
@@ -0,0 +1,160 @@
+-- $Id$
+--
+-- this is the script to migrate from 4.2 to 5.0
+--
+
+----------------------------------------
+-- rename nodenetwork into interface
+----------------------------------------
+
+ALTER TABLE nodenetworks RENAME TO interfaces;
+ALTER TABLE interfaces RENAME COLUMN nodenetwork_id TO interface_id;
+
+ALTER INDEX nodenetworks_node_id_idx RENAME TO interfaces_node_id_idx;
+
+ALTER TABLE nodenetwork_setting_types RENAME TO interface_setting_types;
+ALTER TABLE interface_setting_types RENAME COLUMN nodenetwork_setting_type_id TO interface_setting_type_id;
+
+ALTER TABLE nodenetwork_setting RENAME TO interface_setting;
+
+-- views
+ALTER TABLE nodenetworks_ordered RENAME TO interfaces_ordered;
+ALTER TABLE interfaces_ordered RENAME COLUMN nodenetwork_id TO interface_id;
+
+ALTER TABLE node_nodenetworks RENAME TO node_interfaces;
+ALTER TABLE node_interfaces RENAME COLUMN nodenetwork_ids TO interface_ids;
+
+ALTER TABLE nodenetwork_settings RENAME TO interface_settings;
+ALTER TABLE interface_settings RENAME COLUMN nodenetwork_id TO interface_id;
+ALTER TABLE interface_settings RENAME COLUMN nodenetwork_setting_ids TO setting_ids;
+
+ALTER TABLE view_nodenetwork_settings RENAME TO view_interface_settings;
+ALTER TABLE view_interface_settings RENAME COLUMN nodenetwork_setting_id TO interface_setting_id;
+ALTER TABLE view_interface_settings RENAME COLUMN nodenetwork_id TO interface_id;
+ALTER TABLE view_interface_settings RENAME COLUMN nodenetwork_setting_type_id TO interface_setting_type_id;
+
+ALTER TABLE view_nodenetworks RENAME TO view_interfaces;
+ALTER TABLE view_interfaces RENAME COLUMN nodenetwork_id TO interface_id;
+ALTER TABLE view_interfaces RENAME COLUMN nodenetwork_setting_ids TO setting_ids;
+
+ALTER TABLE view_nodes RENAME COLUMN nodenetwork_ids TO interface_ids;
+
+----------------------------------------
+-- node tags
+----------------------------------------
+CREATE TABLE node_tag_types (
+
+    node_tag_type_id serial PRIMARY KEY,       -- ID
+    name text UNIQUE NOT NULL,                 -- Tag Name
+    description text,                          -- Optional Description
+    category text NOT NULL,                    -- Free text for grouping tags together
+    min_role_id integer REFERENCES roles       -- set minimal role required
+) WITH OIDS;
+
+CREATE TABLE node_tag (
+    node_tag_id serial PRIMARY KEY,            -- ID
+    node_id integer REFERENCES nodes NOT NULL, -- node id
+    node_tag_type_id integer REFERENCES node_tag_types,
+                                               -- tag type id
+    value text                                 -- value attached
+) WITH OIDS;
+
+---------- related views
+CREATE OR REPLACE VIEW node_tags AS
+SELECT node_id,
+array_accum(node_tag_id) AS tag_ids
+FROM node_tag
+GROUP BY node_id;
+
+CREATE OR REPLACE VIEW view_node_tags AS
+SELECT
+node_tag.node_tag_id,
+node_tag.node_id,
+node_tag_types.node_tag_type_id,
+node_tag_types.name,
+node_tag_types.description,
+node_tag_types.category,
+node_tag_types.min_role_id,
+node_tag.value
+FROM node_tag 
+INNER JOIN node_tag_types USING (node_tag_type_id);
+
+----------------------------------------
+-- nodegroups
+-- xxx - todo 
+-- a more usable migration script would need to capture more data
+----------------------------------------
+DROP TABLE IF EXISTS nodegroups CASCADE;
+
+-- Node groups
+CREATE TABLE nodegroups (
+    nodegroup_id serial PRIMARY KEY,                   -- Group identifier
+    groupname text UNIQUE NOT NULL,                    -- Group name
+    node_tag_type_id integer REFERENCES node_tag_types,        -- node is in nodegroup if it has this tag defined
+    value text                                         -- with value 'value'
+) WITH OIDS;
+
+CREATE OR REPLACE VIEW nodegroup_node AS
+SELECT nodegroup_id, node_id 
+FROM node_tag_types 
+JOIN node_tag 
+USING (node_tag_type_id) 
+JOIN nodegroups 
+USING (node_tag_type_id,value);
+
+CREATE OR REPLACE VIEW nodegroup_nodes AS
+SELECT nodegroup_id,
+array_accum(node_id) AS node_ids
+FROM nodegroup_node
+GROUP BY nodegroup_id;
+
+-- Node groups that each node is a member of
+CREATE OR REPLACE VIEW node_nodegroups AS
+SELECT node_id,
+array_accum(nodegroup_id) AS nodegroup_ids
+FROM nodegroup_node
+GROUP BY node_id;
+
+CREATE OR REPLACE VIEW view_nodegroups AS
+SELECT
+nodegroups.*,
+COALESCE((SELECT conf_file_ids FROM nodegroup_conf_files WHERE nodegroup_conf_files.nodegroup_id = nodegroups.nodegroup_id), '{}') AS conf_file_ids
+FROM nodegroups;
+----------------------------------------
+-- the nodes view
+----------------------------------------
+DROP VIEW view_nodes;
+CREATE OR REPLACE VIEW view_nodes AS
+SELECT
+nodes.node_id,
+nodes.hostname,
+nodes.site_id,
+nodes.boot_state,
+nodes.deleted,
+nodes.model,
+nodes.boot_nonce,
+nodes.version,
+nodes.ssh_rsa_key,
+nodes.key,
+CAST(date_part('epoch', nodes.date_created) AS bigint) AS date_created,
+CAST(date_part('epoch', nodes.last_updated) AS bigint) AS last_updated,
+CAST(date_part('epoch', nodes.last_contact) AS bigint) AS last_contact,  
+peer_node.peer_id,
+peer_node.peer_node_id,
+COALESCE((SELECT interface_ids FROM node_interfaces WHERE node_interfaces.node_id = nodes.node_id), '{}') AS interface_ids,
+COALESCE((SELECT nodegroup_ids FROM node_nodegroups WHERE node_nodegroups.node_id = nodes.node_id), '{}') AS nodegroup_ids,
+COALESCE((SELECT slice_ids FROM node_slices WHERE node_slices.node_id = nodes.node_id), '{}') AS slice_ids,
+COALESCE((SELECT slice_ids_whitelist FROM node_slices_whitelist WHERE node_slices_whitelist.node_id = nodes.node_id), '{}') AS slice_ids_whitelist,
+COALESCE((SELECT pcu_ids FROM node_pcus WHERE node_pcus.node_id = nodes.node_id), '{}') AS pcu_ids,
+COALESCE((SELECT ports FROM node_pcus WHERE node_pcus.node_id = nodes.node_id), '{}') AS ports,
+COALESCE((SELECT conf_file_ids FROM node_conf_files WHERE node_conf_files.node_id = nodes.node_id), '{}') AS conf_file_ids,
+COALESCE((SELECT tag_ids FROM node_tags WHERE node_tags.node_id = nodes.node_id), '{}') AS tag_ids,
+node_session.session_id AS session
+FROM nodes
+LEFT JOIN peer_node USING (node_id)
+LEFT JOIN node_session USING (node_id);
+
+----------------------------------------
+-- update versioning
+----------------------------------------
+UPDATE plc_db_version SET version=5, subversion=0;
diff --git a/php/.cvsignore b/php/.cvsignore
deleted file mode 100644 (file)
index dd89eef..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-methods.php
-plc_api.php
index a3b3ad0..8062324 100644 (file)
@@ -333,12 +333,12 @@ INNER JOIN node_tag_types USING (node_tag_type_id);
 -- Node groups
 CREATE TABLE nodegroups (
     nodegroup_id serial PRIMARY KEY,                   -- Group identifier
-    name text UNIQUE NOT NULL,                         -- Group name
+    groupname text UNIQUE NOT NULL,                    -- Group name (name & value will refer to the tag)
     node_tag_type_id integer REFERENCES node_tag_types,        -- node is in nodegroup if it has this tag defined
-    value text                                         -- with value 'value'
+    value text NOT NULL                                        -- with value 'value'
 ) WITH OIDS;
 
-
+-- xxx - first rough implem
 CREATE OR REPLACE VIEW nodegroup_node AS
 SELECT nodegroup_id, node_id 
 FROM node_tag_types 
@@ -1053,7 +1053,6 @@ LEFT JOIN node_session USING (node_id);
 CREATE OR REPLACE VIEW view_nodegroups AS
 SELECT
 nodegroups.*,
-COALESCE((SELECT node_ids FROM nodegroup_nodes WHERE nodegroup_nodes.nodegroup_id = nodegroups.nodegroup_id), '{}') AS node_ids,
 COALESCE((SELECT conf_file_ids FROM nodegroup_conf_files WHERE nodegroup_conf_files.nodegroup_id = nodegroups.nodegroup_id), '{}') AS conf_file_ids
 FROM nodegroups;