From 166443b265a563312a0f8097c7e1b7dd2c6dc12d Mon Sep 17 00:00:00 2001 From: Thierry Parmentelat Date: Tue, 27 May 2008 15:18:55 +0000 Subject: [PATCH] first draft for ilinks : typed and valued links between interfaces e.g. AddIlinkType ('dummynet') AddIlink (interface_id_src, interface_id_dst, 'dummynet', 'a value if that makes sense') TODO: clean up ilinks when nodes or interfaces get deleted --- Makefile | 2 +- PLC/IlinkTypes.py | 77 ++++++++++ PLC/Ilinks.py | 53 +++++++ PLC/Methods/AddIlink.py | 92 ++++++++++++ PLC/Methods/AddIlinkType.py | 45 ++++++ PLC/Methods/AddNodeTag.py | 1 - PLC/Methods/DeleteIlink.py | 73 ++++++++++ PLC/Methods/DeleteIlinkType.py | 39 ++++++ PLC/Methods/GetIlinkTypes.py | 33 +++++ PLC/Methods/GetIlinks.py | 44 ++++++ PLC/Methods/UpdateIlink.py | 63 +++++++++ PLC/Methods/UpdateIlinkType.py | 48 +++++++ PLC/Methods/__init__.py | 8 ++ PLC/__init__.py | 2 + migrations/migrate-v4-to-v5.sql | 115 +++------------ planetlab5.sql | 240 +++++++++++++++++++------------- 16 files changed, 738 insertions(+), 197 deletions(-) create mode 100644 PLC/IlinkTypes.py create mode 100644 PLC/Ilinks.py create mode 100644 PLC/Methods/AddIlink.py create mode 100644 PLC/Methods/AddIlinkType.py create mode 100644 PLC/Methods/DeleteIlink.py create mode 100644 PLC/Methods/DeleteIlinkType.py create mode 100644 PLC/Methods/GetIlinkTypes.py create mode 100644 PLC/Methods/GetIlinks.py create mode 100644 PLC/Methods/UpdateIlink.py create mode 100644 PLC/Methods/UpdateIlinkType.py diff --git a/Makefile b/Makefile index 276cc05..d15af83 100644 --- a/Makefile +++ b/Makefile @@ -51,7 +51,7 @@ index: $(init) index-clean: rm $(init) -#################### +#################### regenerate indexes - not used by the build, as both files are svn added - please update as appropriate # All .py files in PLC/ # the current content of __init__.py diff --git a/PLC/IlinkTypes.py b/PLC/IlinkTypes.py new file mode 100644 index 0000000..bb11979 --- /dev/null +++ b/PLC/IlinkTypes.py @@ -0,0 +1,77 @@ +# +# 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 IlinkType (Row): + + """ + Representation of a row in the ilink_types table. + """ + + table_name = 'ilink_types' + primary_key = 'ilink_type_id' + join_tables = ['ilink'] + fields = { + 'ilink_type_id': Parameter(int, "ilink type identifier"), + 'name': Parameter(str, "ilink type name", max = 100), + 'description': Parameter(str, "ilink type description", max = 254), + 'min_role_id': Parameter(int, "Minimum (least powerful) role that can set or change this attribute"), + } + + def validate_name(self, name): + if not len(name): + raise PLCInvalidArgument, "ilink type name must be set" + + conflicts = IlinkTypes(self.api, [name]) + for tag_type in conflicts: + if 'ilink_type_id' not in self or \ + self['ilink_type_id'] != tag_type['ilink_type_id']: + raise PLCInvalidArgument, "ilink 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 IlinkTypes(Table): + """ + Representation of row(s) from the ilink_types table + in the database. + """ + + def __init__(self, api, ilink_type_filter = None, columns = None): + Table.__init__(self, api, IlinkType, columns) + + sql = "SELECT %s FROM ilink_types WHERE True" % \ + ", ".join(self.columns) + + if ilink_type_filter is not None: + if isinstance(ilink_type_filter, (list, tuple, set)): + # Separate the list into integers and strings + ints = filter(lambda x: isinstance(x, (int, long)), ilink_type_filter) + strs = filter(lambda x: isinstance(x, StringTypes), ilink_type_filter) + ilink_type_filter = Filter(IlinkType.fields, {'ilink_type_id': ints, 'name': strs}) + sql += " AND (%s) %s" % ilink_type_filter.sql(api, "OR") + elif isinstance(ilink_type_filter, dict): + ilink_type_filter = Filter(IlinkType.fields, ilink_type_filter) + sql += " AND (%s) %s" % ilink_type_filter.sql(api, "AND") + elif isinstance (ilink_type_filter, StringTypes): + ilink_type_filter = Filter(IlinkType.fields, {'name':[ilink_type_filter]}) + sql += " AND (%s) %s" % ilink_type_filter.sql(api, "AND") + else: + raise PLCInvalidArgument, "Wrong ilink type filter %r"%ilink_type_filter + + self.selectall(sql) diff --git a/PLC/Ilinks.py b/PLC/Ilinks.py new file mode 100644 index 0000000..f6c9586 --- /dev/null +++ b/PLC/Ilinks.py @@ -0,0 +1,53 @@ +# +# 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.Interfaces import Interface, Interfaces +from PLC.IlinkTypes import IlinkType, IlinkTypes + +class Ilink(Row): + """ + Representation of a row in the ilink table. + To use, instantiate with a dict of values. + """ + + table_name = 'ilink' + primary_key = 'ilink_id' + fields = { + 'ilink_id': Parameter(int, "ilink identifier"), + 'ilink_type_id': IlinkType.fields['ilink_type_id'], + 'src_interface_id': Parameter(int, "source interface identifier"), + 'dst_interface_id': Parameter(int, "destination interface identifier"), + 'value': Parameter( str, "optional ilink value"), + } + +class Ilinks(Table): + """ + Representation of row(s) from the ilink table in the + database. + """ + + def __init__(self, api, ilink_filter = None, columns = None): + Table.__init__(self, api, Ilink, columns) + + sql = "SELECT %s FROM view_ilinks WHERE True" % \ + ", ".join(self.columns) + + if ilink_filter is not None: + if isinstance(ilink_filter, (list, tuple, set)): + ilink_filter = Filter(Ilink.fields, {'ilink_id': ilink_filter}) + elif isinstance(ilink_filter, dict): + ilink_filter = Filter(Ilink.fields, ilink_filter) + elif isinstance(ilink_filter, int): + ilink_filter = Filter(Ilink.fields, {'ilink_id': [ilink_filter]}) + else: + raise PLCInvalidArgument, "Wrong ilink filter %r"%ilink_filter + sql += " AND (%s) %s" % ilink_filter.sql(api) + + + self.selectall(sql) diff --git a/PLC/Methods/AddIlink.py b/PLC/Methods/AddIlink.py new file mode 100644 index 0000000..c2c22c5 --- /dev/null +++ b/PLC/Methods/AddIlink.py @@ -0,0 +1,92 @@ +# +# 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.IlinkTypes import IlinkType, IlinkTypes +from PLC.Ilinks import Ilink, Ilinks +from PLC.Interfaces import Interface, Interfaces + +from PLC.Sites import Sites + +class AddIlink(Method): + """ + Create a link between two interfaces + The link has a type, that needs be created beforehand + and an optional value. + + Returns the new ilink_id (> 0) if successful, faults + otherwise. + """ + + roles = ['admin', 'pi', 'tech', 'user'] + + accepts = [ + Auth(), + # refer to either the id or the type name + Ilink.fields['src_interface_id'], + Ilink.fields['dst_interface_id'], + Mixed(IlinkType.fields['ilink_type_id'], + IlinkType.fields['name']), + Ilink.fields['value'], + ] + + returns = Parameter(int, 'New ilink_id (> 0) if successful') + + def call(self, auth, src_if_id, dst_if_id, ilink_type_id_or_name, value): + + src_if = Interfaces (self.api, [src_if_id],[interface_id]) + if not src_if: + raise PLCInvalidArgument, "No such source interface %r"%src_if_id + dst_if = Interfaces (self.api, [dst_if_id],[interface_id]) + if not dst_if: + raise PLCInvalidArgument, "No such destination interface %r"%dst_if_id + + ilink_types = IlinkTypes(self.api, [ilink_type_id_or_name]) + if not ilink_types: + raise PLCInvalidArgument, "No such ilink type %r"%ilink_type_id_or_name + ilink_type = ilink_types[0] + + # checks for existence - with the same type + conflicts = Ilinks(self.api, + {'ilink_type_id':ilink_type['ilink_type_id'], + 'src_interface_id':src_if_id, + 'dst_interface_id':dst_if_id,}) + + if len(conflicts) : + ilink=conflicts[0] + raise PLCInvalidArgument, "Ilink (%s,%d,%d) already exists and has value %r"\ + %(ilink_type['name'],src_if_id,dst_if_id,ilink['value']) + + if 'admin' not in self.caller['roles']: +# # check permission : it not admin, is the user affiliated with the right site(s) ???? +# # 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 = ilink_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 ilink, requires role %d",required_min_role + + ilink = Ilink(self.api) + ilink['ilink_type_id'] = ilink_type['ilink_type_id'] + ilink['src_interface_id'] = src_if_id + ilink['dst_interface_id'] = dst_if_id + ilink['value'] = value + + ilink.sync() + + self.object_type = 'Interface' + self.object_ids = [src_if_id,dst_if_id] + + return ilink['ilink_id'] diff --git a/PLC/Methods/AddIlinkType.py b/PLC/Methods/AddIlinkType.py new file mode 100644 index 0000000..e4460fe --- /dev/null +++ b/PLC/Methods/AddIlinkType.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.IlinkTypes import IlinkType, IlinkTypes +from PLC.Auth import Auth + +can_update = lambda (field, value): field in \ + ['name', 'description', 'category', 'min_role_id'] + +class AddIlinkType(Method): + """ + Adds a new type of ilink. + Any fields specified are used, otherwise defaults are used. + + Returns the new ilink_id (> 0) if successful, + faults otherwise. + """ + + roles = ['admin'] + + ilink_type_fields = dict(filter(can_update, IlinkType.fields.items())) + + accepts = [ + Auth(), + ilink_type_fields + ] + + returns = Parameter(int, 'New ilink_id (> 0) if successful') + + + def call(self, auth, ilink_type_fields): + ilink_type_fields = dict(filter(can_update, ilink_type_fields.items())) + ilink_type = IlinkType(self.api, ilink_type_fields) + ilink_type.sync() + + self.object_ids = [ilink_type['ilink_type_id']] + + return ilink_type['ilink_type_id'] diff --git a/PLC/Methods/AddNodeTag.py b/PLC/Methods/AddNodeTag.py index 04dae7f..1584e02 100644 --- a/PLC/Methods/AddNodeTag.py +++ b/PLC/Methods/AddNodeTag.py @@ -12,7 +12,6 @@ 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): diff --git a/PLC/Methods/DeleteIlink.py b/PLC/Methods/DeleteIlink.py new file mode 100644 index 0000000..685a9ae --- /dev/null +++ b/PLC/Methods/DeleteIlink.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.Ilinks import Ilink, Ilinks +from PLC.Nodes import Node, Nodes + +from PLC.Nodes import Node, Nodes +from PLC.Sites import Site, Sites + +class DeleteIlink(Method): + """ + Deletes the specified ilink + + Attributes may require the caller to have a particular role in order + to be deleted, depending on the related ilink type. + Admins may delete attributes of any slice or sliver. + + Returns 1 if successful, faults otherwise. + """ + + roles = ['admin', 'pi', 'user'] + + accepts = [ + Auth(), + Ilink.fields['ilink_id'] + ] + + returns = Parameter(int, '1 if successful') + + object_type = 'Node' + + + def call(self, auth, ilink_id): + ilinks = Ilinks(self.api, [ilink_id]) + if not ilinks: + raise PLCInvalidArgument, "No such ilink %r"%ilink_id + ilink = ilinks[0] + + ### reproducing a check from UpdateSliceAttribute, looks dumb though + nodes = Nodes(self.api, [ilink['node_id']]) + if not nodes: + raise PLCInvalidArgument, "No such node %r"%ilink['node_id'] + node = nodes[0] + + assert ilink['ilink_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 = ilink_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 ilink, requires role %d",required_min_role + + ilink.delete() + self.object_ids = [ilink['ilink_id']] + + return 1 diff --git a/PLC/Methods/DeleteIlinkType.py b/PLC/Methods/DeleteIlinkType.py new file mode 100644 index 0000000..5a533ad --- /dev/null +++ b/PLC/Methods/DeleteIlinkType.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.IlinkTypes import IlinkType, IlinkTypes +from PLC.Auth import Auth + +class DeleteIlinkType(Method): + """ + Deletes the specified ilink type. + + Returns 1 if successful, faults otherwise. + """ + + roles = ['admin'] + + accepts = [ + Auth(), + Mixed(IlinkType.fields['ilink_type_id'], + IlinkType.fields['name']), + ] + + returns = Parameter(int, '1 if successful') + + + def call(self, auth, ilink_type_id_or_name): + ilink_types = IlinkTypes(self.api, [ilink_type_id_or_name]) + if not ilink_types: + raise PLCInvalidArgument, "No such ilink type" + ilink_type = ilink_types[0] + + ilink_type.delete() + self.object_ids = [ilink_type['ilink_type_id']] + + return 1 diff --git a/PLC/Methods/GetIlinkTypes.py b/PLC/Methods/GetIlinkTypes.py new file mode 100644 index 0000000..a76807d --- /dev/null +++ b/PLC/Methods/GetIlinkTypes.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.IlinkTypes import IlinkType, IlinkTypes + +class GetIlinkTypes(Method): + """ + Returns an array of structs containing details about + ilink types. + + The usual filtering scheme applies on this method. + """ + + roles = ['admin', 'pi', 'user', 'tech', 'node'] + + accepts = [ + Auth(), + Mixed([Mixed(IlinkType.fields['ilink_type_id'], + IlinkType.fields['name'])], + Filter(IlinkType.fields)), + Parameter([str], "List of fields to return", nullok = True) + ] + + returns = [IlinkType.fields] + + def call(self, auth, ilink_type_filter = None, return_fields = None): + return IlinkTypes(self.api, ilink_type_filter, return_fields) diff --git a/PLC/Methods/GetIlinks.py b/PLC/Methods/GetIlinks.py new file mode 100644 index 0000000..c711e9d --- /dev/null +++ b/PLC/Methods/GetIlinks.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.Ilinks import Ilink, Ilinks +from PLC.Sites import Site, Sites +from PLC.Nodes import Node, Nodes + +class GetIlinks(Method): + """ + Returns an array of structs containing details about + nodes and related tags. + + If ilink_filter is specified and is an array of + ilink identifiers, only ilinks 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([Ilink.fields['ilink_id']], + Parameter(int,"ilink id"), + Filter(Ilink.fields)), + Parameter([str], "List of fields to return", nullok = True) + ] + + returns = [Ilink.fields] + + + def call(self, auth, ilink_filter = None, return_fields = None): + + ilinks = Ilinks(self.api, ilink_filter, return_fields) + + return ilinks diff --git a/PLC/Methods/UpdateIlink.py b/PLC/Methods/UpdateIlink.py new file mode 100644 index 0000000..a0578ba --- /dev/null +++ b/PLC/Methods/UpdateIlink.py @@ -0,0 +1,63 @@ +# +# 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.Ilinks import Ilink, Ilinks +from PLC.Interfaces import Interface, Interfaces + +from PLC.Sites import Sites + +class UpdateIlink(Method): + """ + Updates the value of an existing ilink + + Access rights depend on the ilink type. + + Returns 1 if successful, faults otherwise. + """ + + roles = ['admin', 'pi', 'tech', 'user'] + + accepts = [ + Auth(), + Ilink.fields['ilink_id'], + Ilink.fields['value'] + ] + + returns = Parameter(int, '1 if successful') + + object_type = 'Interface' + + def call(self, auth, ilink_id, value): + ilinks = Ilinks(self.api, [ilink_id]) + if not ilinks: + raise PLCInvalidArgument, "No such ilink %r"%ilink_id + ilink = ilinks[0] + + if 'admin' not in self.caller['roles']: +# # check permission : it not admin, is the user affiliated with the right site +# # 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 = ilink_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 ilink, requires role %d",required_min_role + + ilink['value'] = value + ilink.sync() + + self.object_ids = [ilink['src_interface_id'],ilink['dst_interface_id']] + return 1 diff --git a/PLC/Methods/UpdateIlinkType.py b/PLC/Methods/UpdateIlinkType.py new file mode 100644 index 0000000..38644e6 --- /dev/null +++ b/PLC/Methods/UpdateIlinkType.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.IlinkTypes import IlinkType, IlinkTypes +from PLC.Auth import Auth + +can_update = lambda (field, value): field in \ + ['name', 'description', 'category', 'min_role_id'] + +class UpdateIlinkType(Method): + """ + Updates the parameters of an existing tag type + with the values in ilink_type_fields. + + Returns 1 if successful, faults otherwise. + """ + + roles = ['admin'] + + ilink_type_fields = dict(filter(can_update, IlinkType.fields.items())) + + accepts = [ + Auth(), + Mixed(IlinkType.fields['ilink_type_id'], + IlinkType.fields['name']), + ilink_type_fields + ] + + returns = Parameter(int, '1 if successful') + + def call(self, auth, ilink_type_id_or_name, ilink_type_fields): + ilink_type_fields = dict(filter(can_update, ilink_type_fields.items())) + + ilink_types = IlinkTypes(self.api, [ilink_type_id_or_name]) + if not ilink_types: + raise PLCInvalidArgument, "No such tag type" + ilink_type = ilink_types[0] + + ilink_type.update(ilink_type_fields) + ilink_type.sync() + self.object_ids = [ilink_type['ilink_type_id']] + + return 1 diff --git a/PLC/Methods/__init__.py b/PLC/Methods/__init__.py index 8ed5c3e..6ebe48d 100644 --- a/PLC/Methods/__init__.py +++ b/PLC/Methods/__init__.py @@ -5,6 +5,8 @@ AddBootState AddConfFile AddConfFileToNode AddConfFileToNodeGroup +AddIlink +AddIlinkType AddInitScript AddInterface AddInterfaceSetting @@ -50,6 +52,8 @@ DeleteBootState DeleteConfFile DeleteConfFileFromNode DeleteConfFileFromNodeGroup +DeleteIlink +DeleteIlinkType DeleteInitScript DeleteInterface DeleteInterfaceSetting @@ -89,6 +93,8 @@ GetBootStates GetConfFiles GetEventObjects GetEvents +GetIlinkTypes +GetIlinks GetInitScripts GetInterfaceSettingTypes GetInterfaceSettings @@ -148,6 +154,8 @@ SliceUsersList UpdateAddress UpdateAddressType UpdateConfFile +UpdateIlink +UpdateIlinkType UpdateInitScript UpdateInterface UpdateInterfaceSetting diff --git a/PLC/__init__.py b/PLC/__init__.py index 96c4ec4..a632df0 100644 --- a/PLC/__init__.py +++ b/PLC/__init__.py @@ -13,6 +13,8 @@ Events Faults Filter GPG +IlinkTypes +Ilinks InitScripts InterfaceSettingTypes InterfaceSettings diff --git a/migrations/migrate-v4-to-v5.sql b/migrations/migrate-v4-to-v5.sql index d5ada42..285a01f 100644 --- a/migrations/migrate-v4-to-v5.sql +++ b/migrations/migrate-v4-to-v5.sql @@ -42,42 +42,12 @@ 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; +CREATE TABLE node_tag_types ... +CREATE TABLE node_tag ... ---------- 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); +CREATE OR REPLACE VIEW node_tags AS ... +CREATE OR REPLACE VIEW view_node_tags AS ... ---------------------------------------- -- nodegroups @@ -87,72 +57,27 @@ INNER JOIN node_tag_types USING (node_tag_type_id); 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 TABLE nodegroups ... +CREATE OR REPLACE VIEW nodegroup_node AS ... +CREATE OR REPLACE VIEW nodegroup_nodes AS ... +CREATE OR REPLACE VIEW node_nodegroups AS ... 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); +CREATE OR REPLACE VIEW view_nodes AS ... + +---------------------------------------- +-- ilinks +---------------------------------------- +CREATE TABLE ilink_types ... +CREATE TABLE ilink ... + +CREATE OR REPLACE VIEW ilinks AS ... +CREATE OR REPLACE VIEW ilink_src_node AS ... +CREATE OR REPLACE VIEW ilink_nodes AS ... ---------------------------------------- -- update versioning diff --git a/planetlab5.sql b/planetlab5.sql index 0fd11aa..828d76f 100644 --- a/planetlab5.sql +++ b/planetlab5.sql @@ -328,105 +328,7 @@ INNER JOIN node_tag_types USING (node_tag_type_id) INNER JOIN nodes USING (node_id); -------------------------------------------------------------------------------- --- Node groups --------------------------------------------------------------------------------- - --- 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 - tagvalue text NOT NULL -- with this value attached -) WITH OIDS; - --- xxx - first rough implem -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,tagvalue); - -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; - --------------------------------------------------------------------------------- --- Node configuration files --------------------------------------------------------------------------------- - -CREATE TABLE conf_files ( - conf_file_id serial PRIMARY KEY, -- Configuration file identifier - enabled bool NOT NULL DEFAULT true, -- Configuration file is active - source text NOT NULL, -- Relative path on the boot server - -- where file can be downloaded - dest text NOT NULL, -- Absolute path where file should be installed - file_permissions text NOT NULL DEFAULT '0644', -- chmod(1) permissions - file_owner text NOT NULL DEFAULT 'root', -- chown(1) owner - file_group text NOT NULL DEFAULT 'root', -- chgrp(1) owner - preinstall_cmd text, -- Shell command to execute prior to installing - postinstall_cmd text, -- Shell command to execute after installing - error_cmd text, -- Shell command to execute if any error occurs - ignore_cmd_errors bool NOT NULL DEFAULT false, -- Install file anyway even if an error occurs - always_update bool NOT NULL DEFAULT false -- Always attempt to install file even if unchanged -) WITH OIDS; - -CREATE TABLE conf_file_node ( - conf_file_id integer REFERENCES conf_files NOT NULL, -- Configuration file identifier - node_id integer REFERENCES nodes NOT NULL, -- Node identifier - PRIMARY KEY (conf_file_id, node_id) -); -CREATE INDEX conf_file_node_conf_file_id_idx ON conf_file_node (conf_file_id); -CREATE INDEX conf_file_node_node_id_idx ON conf_file_node (node_id); - --- Nodes linked to each configuration file -CREATE OR REPLACE VIEW conf_file_nodes AS -SELECT conf_file_id, -array_accum(node_id) AS node_ids -FROM conf_file_node -GROUP BY conf_file_id; - --- Configuration files linked to each node -CREATE OR REPLACE VIEW node_conf_files AS -SELECT node_id, -array_accum(conf_file_id) AS conf_file_ids -FROM conf_file_node -GROUP BY node_id; - -CREATE TABLE conf_file_nodegroup ( - conf_file_id integer REFERENCES conf_files NOT NULL, -- Configuration file identifier - nodegroup_id integer REFERENCES nodegroups NOT NULL, -- Node group identifier - PRIMARY KEY (conf_file_id, nodegroup_id) -); -CREATE INDEX conf_file_nodegroup_conf_file_id_idx ON conf_file_nodegroup (conf_file_id); -CREATE INDEX conf_file_nodegroup_nodegroup_id_idx ON conf_file_nodegroup (nodegroup_id); - --- Node groups linked to each configuration file -CREATE OR REPLACE VIEW conf_file_nodegroups AS -SELECT conf_file_id, -array_accum(nodegroup_id) AS nodegroup_ids -FROM conf_file_nodegroup -GROUP BY conf_file_id; - --- Configuration files linked to each node group -CREATE OR REPLACE VIEW nodegroup_conf_files AS -SELECT nodegroup_id, -array_accum(conf_file_id) AS conf_file_ids -FROM conf_file_nodegroup -GROUP BY nodegroup_id; - --------------------------------------------------------------------------------- --- Node network interfaces +-- (network) interfaces -------------------------------------------------------------------------------- -- Valid network addressing schemes @@ -487,7 +389,7 @@ CREATE TABLE interface_setting_types ( name text UNIQUE NOT NULL, -- Setting Name description text, -- Optional Description category text NOT NULL DEFAULT 'general', -- Free text for grouping, e.g. Wifi, or whatever - min_role_id integer REFERENCES roles DEFAULT 10 -- If set, minimal role required + min_role_id integer REFERENCES roles DEFAULT 10 -- minimal role required ) WITH OIDS; CREATE TABLE interface_setting ( @@ -537,6 +439,144 @@ interfaces.hostname, COALESCE((SELECT interface_setting_ids FROM interface_settings WHERE interface_settings.interface_id = interfaces.interface_id), '{}') AS interface_setting_ids FROM interfaces; +-------------------------------------------------------------------------------- +-- ilinks : links between interfaces +-------------------------------------------------------------------------------- +CREATE TABLE ilink_types ( + ilink_type_id serial PRIMARY KEY, -- id + name text UNIQUE NOT NULL, -- link name + description text, -- optional description + min_role_id integer REFERENCES roles DEFAULT 10 -- minimal role required +) WITH OIDS; + +CREATE TABLE ilink ( + ilink_id serial PRIMARY KEY, -- id + ilink_type_id integer REFERENCES ilink_types, -- id of the ilink type + src_interface_id integer REFERENCES interfaces not NULL, -- id of src interface + dst_interface_id integer REFERENCES interfaces NOT NULL, -- id of dst interface + value text -- optional value on the link +) WITH OIDS; + +CREATE OR REPLACE VIEW view_ilinks AS +SELECT * FROM ilink_types +INNER JOIN ilink USING (ilink_type_id); + +-- expose node_ids ??? +-- -- cannot mention the same table twice in a join ? +-- -- CREATE OR REPLACE VIEW ilink_src_node AS +-- SELECT +-- ilink.ilink_type_id, +-- ilink.src_interface_id, +-- interfaces.node_id AS src_node_id, +-- ilink.dst_interface_id +-- FROM ilink +-- INNER JOIN interfaces ON ilink.src_interface_id = interfaces.interface_id; +-- +-- CREATE OR REPLACE VIEW ilink_nodes AS +-- SELECT +-- ilink_src_node.*, +-- interfaces.node_id as dst_node_id +-- FROM ilink_src_node +-- INNER JOIN interfaces ON ilink_src_node.dst_interface_id = interfaces.interface_id; + +-------------------------------------------------------------------------------- +-- Node groups +-------------------------------------------------------------------------------- + +-- 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 + tagvalue text NOT NULL -- with this value attached +) WITH OIDS; + +-- xxx - first rough implem. similar to former semantics but might be slow +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,tagvalue); + +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; + +-------------------------------------------------------------------------------- +-- Node configuration files +-------------------------------------------------------------------------------- + +CREATE TABLE conf_files ( + conf_file_id serial PRIMARY KEY, -- Configuration file identifier + enabled bool NOT NULL DEFAULT true, -- Configuration file is active + source text NOT NULL, -- Relative path on the boot server + -- where file can be downloaded + dest text NOT NULL, -- Absolute path where file should be installed + file_permissions text NOT NULL DEFAULT '0644', -- chmod(1) permissions + file_owner text NOT NULL DEFAULT 'root', -- chown(1) owner + file_group text NOT NULL DEFAULT 'root', -- chgrp(1) owner + preinstall_cmd text, -- Shell command to execute prior to installing + postinstall_cmd text, -- Shell command to execute after installing + error_cmd text, -- Shell command to execute if any error occurs + ignore_cmd_errors bool NOT NULL DEFAULT false, -- Install file anyway even if an error occurs + always_update bool NOT NULL DEFAULT false -- Always attempt to install file even if unchanged +) WITH OIDS; + +CREATE TABLE conf_file_node ( + conf_file_id integer REFERENCES conf_files NOT NULL, -- Configuration file identifier + node_id integer REFERENCES nodes NOT NULL, -- Node identifier + PRIMARY KEY (conf_file_id, node_id) +); +CREATE INDEX conf_file_node_conf_file_id_idx ON conf_file_node (conf_file_id); +CREATE INDEX conf_file_node_node_id_idx ON conf_file_node (node_id); + +-- Nodes linked to each configuration file +CREATE OR REPLACE VIEW conf_file_nodes AS +SELECT conf_file_id, +array_accum(node_id) AS node_ids +FROM conf_file_node +GROUP BY conf_file_id; + +-- Configuration files linked to each node +CREATE OR REPLACE VIEW node_conf_files AS +SELECT node_id, +array_accum(conf_file_id) AS conf_file_ids +FROM conf_file_node +GROUP BY node_id; + +CREATE TABLE conf_file_nodegroup ( + conf_file_id integer REFERENCES conf_files NOT NULL, -- Configuration file identifier + nodegroup_id integer REFERENCES nodegroups NOT NULL, -- Node group identifier + PRIMARY KEY (conf_file_id, nodegroup_id) +); +CREATE INDEX conf_file_nodegroup_conf_file_id_idx ON conf_file_nodegroup (conf_file_id); +CREATE INDEX conf_file_nodegroup_nodegroup_id_idx ON conf_file_nodegroup (nodegroup_id); + +-- Node groups linked to each configuration file +CREATE OR REPLACE VIEW conf_file_nodegroups AS +SELECT conf_file_id, +array_accum(nodegroup_id) AS nodegroup_ids +FROM conf_file_nodegroup +GROUP BY conf_file_id; + +-- Configuration files linked to each node group +CREATE OR REPLACE VIEW nodegroup_conf_files AS +SELECT nodegroup_id, +array_accum(conf_file_id) AS conf_file_ids +FROM conf_file_nodegroup +GROUP BY nodegroup_id; + -------------------------------------------------------------------------------- -- Power control units (PCUs) -------------------------------------------------------------------------------- -- 2.43.0