From 7ba889dd89500f8c0695ef21bfda28291bb4f9da Mon Sep 17 00:00:00 2001 From: Thierry Parmentelat Date: Fri, 23 May 2008 10:25:13 +0000 Subject: [PATCH] step2 : basic functions for handling nodetags and nodegroups - still highly volatile --- PLC/.cvsignore | 1 - PLC/InitScripts.py | 2 +- PLC/Methods/AddConfFileToNodeGroup.py | 2 +- PLC/Methods/AddNodeGroup.py | 5 +- PLC/Methods/AddNodeTag.py | 89 +++++++++++ PLC/Methods/AddNodeTagType.py | 45 ++++++ PLC/Methods/AddSliceAttribute.py | 2 +- PLC/Methods/DeleteConfFileFromNodeGroup.py | 2 +- PLC/Methods/DeleteInitScript.py | 9 +- PLC/Methods/DeleteNodeGroup.py | 2 +- PLC/Methods/DeleteNodeTag.py | 73 +++++++++ PLC/Methods/DeleteNodeTagType.py | 39 +++++ PLC/Methods/GetInterfaceSettings.py | 1 - PLC/Methods/GetNodeGroups.py | 2 +- PLC/Methods/GetNodeTagTypes.py | 33 ++++ PLC/Methods/GetNodeTags.py | 44 ++++++ PLC/Methods/GetSlivers.py | 2 +- PLC/Methods/UpdateNodeGroup.py | 2 +- PLC/Methods/UpdateNodeTag.py | 72 +++++++++ PLC/Methods/UpdateNodeTagType.py | 48 ++++++ PLC/Methods/__init__.py | 8 + PLC/NodeGroups.py | 22 +-- PLC/NodeTagTypes.py | 83 ++++++++++ PLC/NodeTags.py | 57 +++++++ PLC/Nodes.py | 4 +- PLC/Test.py | 4 +- PLC/__init__.py | 2 + migrations/migrate-v4-to-v5.sh | 169 ++------------------- migrations/migrate-v4-to-v5.sql | 160 +++++++++++++++++++ php/.cvsignore | 2 - planetlab5.sql | 7 +- 31 files changed, 800 insertions(+), 193 deletions(-) delete mode 100644 PLC/.cvsignore create mode 100644 PLC/Methods/AddNodeTag.py create mode 100644 PLC/Methods/AddNodeTagType.py create mode 100644 PLC/Methods/DeleteNodeTag.py create mode 100644 PLC/Methods/DeleteNodeTagType.py create mode 100644 PLC/Methods/GetNodeTagTypes.py create mode 100644 PLC/Methods/GetNodeTags.py create mode 100644 PLC/Methods/UpdateNodeTag.py create mode 100644 PLC/Methods/UpdateNodeTagType.py create mode 100644 PLC/NodeTagTypes.py create mode 100644 PLC/NodeTags.py create mode 100644 migrations/migrate-v4-to-v5.sql delete mode 100644 php/.cvsignore diff --git a/PLC/.cvsignore b/PLC/.cvsignore deleted file mode 100644 index 0d20b64..0000000 --- a/PLC/.cvsignore +++ /dev/null @@ -1 +0,0 @@ -*.pyc diff --git a/PLC/InitScripts.py b/PLC/InitScripts.py index 9f864d2..028293b 100644 --- a/PLC/InitScripts.py +++ b/PLC/InitScripts.py @@ -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): diff --git a/PLC/Methods/AddConfFileToNodeGroup.py b/PLC/Methods/AddConfFileToNodeGroup.py index 6ff642c..25ea249 100644 --- a/PLC/Methods/AddConfFileToNodeGroup.py +++ b/PLC/Methods/AddConfFileToNodeGroup.py @@ -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') diff --git a/PLC/Methods/AddNodeGroup.py b/PLC/Methods/AddNodeGroup.py index 34f5f97..9555f73 100644 --- a/PLC/Methods/AddNodeGroup.py +++ b/PLC/Methods/AddNodeGroup.py @@ -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 index 0000000..e6db8ed --- /dev/null +++ b/PLC/Methods/AddNodeTag.py @@ -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 index 0000000..c95f520 --- /dev/null +++ b/PLC/Methods/AddNodeTagType.py @@ -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'] diff --git a/PLC/Methods/AddSliceAttribute.py b/PLC/Methods/AddSliceAttribute.py index ad32437..8102fe1 100644 --- a/PLC/Methods/AddSliceAttribute.py +++ b/PLC/Methods/AddSliceAttribute.py @@ -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') diff --git a/PLC/Methods/DeleteConfFileFromNodeGroup.py b/PLC/Methods/DeleteConfFileFromNodeGroup.py index 5504b0f..7e3eca5 100644 --- a/PLC/Methods/DeleteConfFileFromNodeGroup.py +++ b/PLC/Methods/DeleteConfFileFromNodeGroup.py @@ -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') diff --git a/PLC/Methods/DeleteInitScript.py b/PLC/Methods/DeleteInitScript.py index 47a9993..7c4b01e 100644 --- a/PLC/Methods/DeleteInitScript.py +++ b/PLC/Methods/DeleteInitScript.py @@ -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" diff --git a/PLC/Methods/DeleteNodeGroup.py b/PLC/Methods/DeleteNodeGroup.py index 7650150..7e65ad6 100644 --- a/PLC/Methods/DeleteNodeGroup.py +++ b/PLC/Methods/DeleteNodeGroup.py @@ -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 index 0000000..b7bfd3f --- /dev/null +++ b/PLC/Methods/DeleteNodeTag.py @@ -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 index 0000000..16b750d --- /dev/null +++ b/PLC/Methods/DeleteNodeTagType.py @@ -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 diff --git a/PLC/Methods/GetInterfaceSettings.py b/PLC/Methods/GetInterfaceSettings.py index 779ef0c..c532d7c 100644 --- a/PLC/Methods/GetInterfaceSettings.py +++ b/PLC/Methods/GetInterfaceSettings.py @@ -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 diff --git a/PLC/Methods/GetNodeGroups.py b/PLC/Methods/GetNodeGroups.py index f4927ef..5711217 100644 --- a/PLC/Methods/GetNodeGroups.py +++ b/PLC/Methods/GetNodeGroups.py @@ -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 index 0000000..32a6787 --- /dev/null +++ b/PLC/Methods/GetNodeTagTypes.py @@ -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 index 0000000..fd53dcd --- /dev/null +++ b/PLC/Methods/GetNodeTags.py @@ -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 diff --git a/PLC/Methods/GetSlivers.py b/PLC/Methods/GetSlivers.py index 5624c0c..7f936ea 100644 --- a/PLC/Methods/GetSlivers.py +++ b/PLC/Methods/GetSlivers.py @@ -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': [{ diff --git a/PLC/Methods/UpdateNodeGroup.py b/PLC/Methods/UpdateNodeGroup.py index c84c7f1..dddca60 100644 --- a/PLC/Methods/UpdateNodeGroup.py +++ b/PLC/Methods/UpdateNodeGroup.py @@ -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 index 0000000..55857b2 --- /dev/null +++ b/PLC/Methods/UpdateNodeTag.py @@ -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 index 0000000..952b3ff --- /dev/null +++ b/PLC/Methods/UpdateNodeTagType.py @@ -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 diff --git a/PLC/Methods/__init__.py b/PLC/Methods/__init__.py index 86731b5..8ed5c3e 100644 --- a/PLC/Methods/__init__.py +++ b/PLC/Methods/__init__.py @@ -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 diff --git a/PLC/NodeGroups.py b/PLC/NodeGroups.py index fdc3ba9..6ec7a4f 100644 --- a/PLC/NodeGroups.py +++ b/PLC/NodeGroups.py @@ -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 index 0000000..0c05bc8 --- /dev/null +++ b/PLC/NodeTagTypes.py @@ -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 index 0000000..6577c91 --- /dev/null +++ b/PLC/NodeTags.py @@ -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) diff --git a/PLC/Nodes.py b/PLC/Nodes.py index 01328e3..22c6860 100644 --- a/PLC/Nodes.py +++ b/PLC/Nodes.py @@ -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), diff --git a/PLC/Test.py b/PLC/Test.py index 48e1618..241b3e2 100644 --- a/PLC/Test.py +++ b/PLC/Test.py @@ -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): diff --git a/PLC/__init__.py b/PLC/__init__.py index 10f6edf..96c4ec4 100644 --- a/PLC/__init__.py +++ b/PLC/__init__.py @@ -24,6 +24,8 @@ Method NetworkMethods NetworkTypes NodeGroups +NodeTagTypes +NodeTags Nodes PCUProtocolTypes PCUTypes diff --git a/migrations/migrate-v4-to-v5.sh b/migrations/migrate-v4-to-v5.sh index 1d04800..b854c79 100755 --- a/migrations/migrate-v4-to-v5.sh +++ b/migrations/migrate-v4-to-v5.sh @@ -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 <