add site_tags : GetSites() now returns 'site_tag_ids' attribute.
authorStephen Soltesz <soltesz@cs.princeton.edu>
Fri, 13 Nov 2009 22:33:07 +0000 (22:33 +0000)
committerStephen Soltesz <soltesz@cs.princeton.edu>
Fri, 13 Nov 2009 22:33:07 +0000 (22:33 +0000)
added Add/Delete/Get/UpdateSiteTag*() methods
added SiteTags.py to define the site tags table.  Similar to the other Tag tables

PLC/Legacy/__init__.py
PLC/Methods/AddSiteTag.py [new file with mode: 0644]
PLC/Methods/DeleteSiteTag.py [new file with mode: 0644]
PLC/Methods/GetSiteTags.py [new file with mode: 0644]
PLC/Methods/UpdateSiteTag.py [new file with mode: 0644]
PLC/Methods/__init__.py
PLC/SiteTags.py [new file with mode: 0644]
PLC/Sites.py
PLC/TagTypes.py
PLC/__init__.py

index ead4e2a..64f72be 100644 (file)
@@ -10,11 +10,11 @@ DeleteNodeNetworkSetting
 DeleteNodeNetworkSettingType
 DeleteSliceAttribute
 DeleteSliceAttributeType
-GetNodeNetworkSettings
 GetNodeNetworkSettingTypes
+GetNodeNetworkSettings
 GetNodeNetworks
-GetSliceAttributes
 GetSliceAttributeTypes
+GetSliceAttributes
 UpdateNodeNetwork
 UpdateNodeNetworkSetting
 UpdateNodeNetworkSettingType
diff --git a/PLC/Methods/AddSiteTag.py b/PLC/Methods/AddSiteTag.py
new file mode 100644 (file)
index 0000000..dda3f57
--- /dev/null
@@ -0,0 +1,89 @@
+# $Id: AddSiteTag.py 14587 2009-07-19 13:18:50Z thierry $
+# $URL: http://svn.planet-lab.org/svn/PLCAPI/tags/PLCAPI-4.3-27/PLC/Methods/AddSiteTag.py $
+#
+# Thierry Parmentelat - INRIA
+#
+# $Revision: 14587 $
+#
+from PLC.Faults import *
+from PLC.Method import Method
+from PLC.Parameter import Parameter, Mixed
+from PLC.Auth import Auth
+
+from PLC.TagTypes import TagType, TagTypes
+from PLC.SiteTags import SiteTag, SiteTags
+from PLC.Sites import Site, Sites
+
+from PLC.Nodes import Nodes
+from PLC.Sites import Sites
+
+class AddSiteTag(Method):
+    """
+    Sets the specified setting for the specified site
+    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 tag type object.
+
+    Returns the new site_tag_id (> 0) if successful, faults
+    otherwise.
+    """
+
+    roles = ['admin', 'pi', 'tech', 'user']
+
+    accepts = [
+        Auth(),
+        # no other way to refer to a site
+        SiteTag.fields['site_id'],
+        Mixed(TagType.fields['tag_type_id'],
+              TagType.fields['tagname']),
+        SiteTag.fields['value'],
+        ]
+
+    returns = Parameter(int, 'New site_tag_id (> 0) if successful')
+
+    object_type = 'Site'
+
+
+    def call(self, auth, site_id, tag_type_id_or_name, value):
+        sites = Sites(self.api, [site_id])
+        if not sites:
+            raise PLCInvalidArgument, "No such site %r"%site_id
+        site = sites[0]
+
+        tag_types = TagTypes(self.api, [tag_type_id_or_name])
+        if not tag_types:
+            raise PLCInvalidArgument, "No such tag type %r"%tag_type_id_or_name
+        tag_type = tag_types[0]
+
+       # checks for existence - does not allow several different settings
+        conflicts = SiteTags(self.api,
+                                        {'site_id':site['site_id'],
+                                         'tag_type_id':tag_type['tag_type_id']})
+
+        if len(conflicts) :
+            raise PLCInvalidArgument, "Site %d already has setting %d"%(site['site_id'],
+                                                                               tag_type['tag_type_id'])
+
+       # check permission : it not admin, is the user affiliated with the right site
+       if 'admin' not in self.caller['roles']:
+           # locate site
+           site = Sites (self.api, 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 = 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 site setting, requires role %d",required_min_role
+
+        site_tag = SiteTag(self.api)
+        site_tag['site_id'] = site['site_id']
+        site_tag['tag_type_id'] = tag_type['tag_type_id']
+        site_tag['value'] = value
+
+        site_tag.sync()
+       self.object_ids = [site_tag['site_tag_id']]
+
+        return site_tag['site_tag_id']
diff --git a/PLC/Methods/DeleteSiteTag.py b/PLC/Methods/DeleteSiteTag.py
new file mode 100644 (file)
index 0000000..f1e06aa
--- /dev/null
@@ -0,0 +1,71 @@
+# $Id: DeleteSiteTag.py 14587 2009-07-19 13:18:50Z thierry $
+# $URL: http://svn.planet-lab.org/svn/PLCAPI/tags/PLCAPI-4.3-27/PLC/Methods/DeleteSiteTag.py $
+#
+# Thierry Parmentelat - INRIA
+#
+# $Revision: 14587 $
+#
+
+from PLC.Faults import *
+from PLC.Method import Method
+from PLC.Parameter import Parameter, Mixed
+from PLC.Auth import Auth
+
+from PLC.SiteTags import SiteTag, SiteTags
+from PLC.Sites import Site, Sites
+
+from PLC.Nodes import Node, Nodes
+from PLC.Sites import Site, Sites
+
+class DeleteSiteTag(Method):
+    """
+    Deletes the specified site setting
+
+    Attributes may require the caller to have a particular role in order
+    to be deleted, depending on the related tag type.
+    Admins may delete attributes of any slice or sliver.
+
+    Returns 1 if successful, faults otherwise.
+    """
+
+    roles = ['admin', 'pi', 'user']
+
+    accepts = [
+        Auth(),
+        SiteTag.fields['site_tag_id']
+        ]
+
+    returns = Parameter(int, '1 if successful')
+
+    object_type = 'Site'
+
+
+    def call(self, auth, site_tag_id):
+        site_tags = SiteTags(self.api, [site_tag_id])
+        if not site_tags:
+            raise PLCInvalidArgument, "No such site tag %r"%site_tag_id
+        site_tag = site_tags[0]
+
+        ### reproducing a check from UpdateSliceTag, looks dumb though
+        sites = Sites(self.api, [site_tag['site_id']])
+        if not sites:
+            raise PLCInvalidArgument, "No such site %r"%site_tag['site_id']
+        site = sites[0]
+
+        assert site_tag['site_tag_id'] in site['site_tag_ids']
+
+       # check permission : it not admin, is the user affiliated with the right site
+       if 'admin' not in self.caller['roles']:
+           # 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 = 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 site setting, requires role %d",required_min_role
+
+        site_tag.delete()
+       self.object_ids = [site_tag['site_tag_id']]
+
+        return 1
diff --git a/PLC/Methods/GetSiteTags.py b/PLC/Methods/GetSiteTags.py
new file mode 100644 (file)
index 0000000..4663e8a
--- /dev/null
@@ -0,0 +1,45 @@
+# $Id: GetSiteTags.py 14587 2009-07-19 13:18:50Z thierry $
+# $URL: http://svn.planet-lab.org/svn/PLCAPI/tags/PLCAPI-4.3-27/PLC/Methods/GetSiteTags.py $
+#
+# Thierry Parmentelat - INRIA
+#
+# $Revision: 14587 $
+#
+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.SiteTags import SiteTag, SiteTags
+from PLC.Sites import Site, Sites
+
+class GetSiteTags(Method):
+    """
+    Returns an array of structs containing details about
+    sites and related settings.
+
+    If site_tag_filter is specified and is an array of
+    site setting identifiers, only site settings 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([SiteTag.fields['site_tag_id']],
+              Parameter(int,"Site setting id"),
+              Filter(SiteTag.fields)),
+        Parameter([str], "List of fields to return", nullok = True)
+        ]
+
+    returns = [SiteTag.fields]
+    
+
+    def call(self, auth, site_tag_filter = None, return_fields = None):
+
+        site_tags = SiteTags(self.api, site_tag_filter, return_fields)
+
+        return site_tags
diff --git a/PLC/Methods/UpdateSiteTag.py b/PLC/Methods/UpdateSiteTag.py
new file mode 100644 (file)
index 0000000..c7f63a3
--- /dev/null
@@ -0,0 +1,68 @@
+# $Id: UpdateSiteTag.py 14587 2009-07-19 13:18:50Z thierry $
+# $URL: http://svn.planet-lab.org/svn/PLCAPI/tags/PLCAPI-4.3-27/PLC/Methods/UpdateSiteTag.py $
+#
+# $Revision: 14587 $
+#
+
+from PLC.Faults import *
+from PLC.Method import Method
+from PLC.Parameter import Parameter, Mixed
+from PLC.Auth import Auth
+
+from PLC.SiteTags import SiteTag, SiteTags
+from PLC.Sites import Site, Sites
+
+from PLC.Nodes import Nodes
+from PLC.Sites import Sites
+
+class UpdateSiteTag(Method):
+    """
+    Updates the value of an existing site setting
+
+    Access rights depend on the tag type.
+
+    Returns 1 if successful, faults otherwise.
+    """
+
+    roles = ['admin', 'pi', 'tech', 'user']
+
+    accepts = [
+        Auth(),
+        SiteTag.fields['site_tag_id'],
+        SiteTag.fields['value']
+        ]
+
+    returns = Parameter(int, '1 if successful')
+
+    object_type = 'Site'
+
+    def call(self, auth, site_tag_id, value):
+        site_tags = SiteTags(self.api, [site_tag_id])
+        if not site_tags:
+            raise PLCInvalidArgument, "No such site setting %r"%site_tag_id
+        site_tag = site_tags[0]
+
+        ### reproducing a check from UpdateSliceTag, looks dumb though
+        sites = Sites(self.api, [site_tag['site_id']])
+        if not sites:
+            raise PLCInvalidArgument, "No such site %r"%site_tag['site_id']
+        site = sites[0]
+
+        assert site_tag['site_tag_id'] in site['site_tag_ids']
+
+       # check permission : it not admin, is the user affiliated with the right site
+       if 'admin' not in self.caller['roles']:
+           # 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 = 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 site setting, requires role %d",required_min_role
+
+        site_tag['value'] = value
+        site_tag.sync()
+
+       self.object_ids = [site_tag['site_tag_id']]
+        return 1
index 5912fc1..50c1dea 100644 (file)
@@ -4,8 +4,8 @@ AddAddressType
 AddAddressTypeToAddress
 AddBootState
 AddConfFile
-AddConfFileToNodeGroup
 AddConfFileToNode
+AddConfFileToNodeGroup
 AddIlink
 AddInitScript
 AddInterface
@@ -14,26 +14,27 @@ AddKeyType
 AddMessage
 AddNetworkMethod
 AddNetworkType
-AddNodeGroup
 AddNode
+AddNodeGroup
 AddNodeTag
 AddNodeToPCU
 AddNodeType
-AddPCUProtocolType
 AddPCU
+AddPCUProtocolType
 AddPCUType
 AddPeer
-AddPersonKey
 AddPerson
+AddPersonKey
 AddPersonToSite
 AddPersonToSlice
 AddRole
 AddRoleToPerson
 AddSession
-AddSiteAddress
 AddSite
-AddSliceInstantiation
+AddSiteAddress
+AddSiteTag
 AddSlice
+AddSliceInstantiation
 AddSliceTag
 AddSliceToNodes
 AddSliceToNodesWhitelist
@@ -46,12 +47,12 @@ BootGetNodeDetails
 BootNotifyOwners
 BootUpdateNode
 DeleteAddress
-DeleteAddressTypeFromAddress
 DeleteAddressType
+DeleteAddressTypeFromAddress
 DeleteBootState
-DeleteConfFileFromNodeGroup
-DeleteConfFileFromNode
 DeleteConfFile
+DeleteConfFileFromNode
+DeleteConfFileFromNodeGroup
 DeleteEmulationLink
 DeleteIlink
 DeleteInitScript
@@ -62,31 +63,32 @@ DeleteKeyType
 DeleteMessage
 DeleteNetworkMethod
 DeleteNetworkType
+DeleteNode
 DeleteNodeFromPCU
 DeleteNodeGroup
-DeleteNode
 DeleteNodeTag
 DeleteNodeType
-DeletePCUProtocolType
 DeletePCU
+DeletePCUProtocolType
 DeletePCUType
 DeletePeer
+DeletePerson
 DeletePersonFromSite
 DeletePersonFromSlice
-DeletePerson
-DeleteRoleFromPerson
 DeleteRole
+DeleteRoleFromPerson
 DeleteSession
 DeleteSite
+DeleteSiteTag
+DeleteSlice
 DeleteSliceFromNodes
 DeleteSliceFromNodesWhitelist
 DeleteSliceInstantiation
-DeleteSlice
 DeleteSliceTag
 DeleteTagType
 GenerateNodeConfFile
-GetAddresses
 GetAddressTypes
+GetAddresses
 GetBootMedium
 GetBootStates
 GetConfFiles
@@ -94,20 +96,20 @@ GetEventObjects
 GetEvents
 GetIlinks
 GetInitScripts
-GetInterfaces
 GetInterfaceTags
-GetKeys
+GetInterfaces
 GetKeyTypes
+GetKeys
 GetMessages
 GetNetworkMethods
 GetNetworkTypes
 GetNodeGroups
-GetNodes
 GetNodeTags
 GetNodeTypes
+GetNodes
 GetPCUProtocolTypes
-GetPCUs
 GetPCUTypes
+GetPCUs
 GetPeerData
 GetPeerName
 GetPeers
@@ -116,12 +118,13 @@ GetPlcRelease
 GetRoles
 GetSession
 GetSessions
+GetSiteTags
 GetSites
 GetSliceInstantiations
 GetSliceKeys
-GetSlices
 GetSliceTags
 GetSliceTicket
+GetSlices
 GetSlivers
 GetTagTypes
 GetWhitelist
@@ -150,10 +153,6 @@ SliceUpdate
 SliceUserAdd
 SliceUserDel
 SliceUsersList
-system.listMethods
-system.methodHelp
-system.methodSignature
-system.multicall
 UnBindObjectFromPeer
 UpdateAddress
 UpdateAddressType
@@ -165,17 +164,22 @@ UpdateInterface
 UpdateInterfaceTag
 UpdateKey
 UpdateMessage
-UpdateNodeGroup
 UpdateNode
+UpdateNodeGroup
 UpdateNodeTag
-UpdatePCUProtocolType
 UpdatePCU
+UpdatePCUProtocolType
 UpdatePCUType
 UpdatePeer
 UpdatePerson
 UpdateSite
+UpdateSiteTag
 UpdateSlice
 UpdateSliceTag
 UpdateTagType
 VerifyPerson
+system.listMethods
+system.methodHelp
+system.methodSignature
+system.multicall
 """.split()
diff --git a/PLC/SiteTags.py b/PLC/SiteTags.py
new file mode 100644 (file)
index 0000000..8ccf5bb
--- /dev/null
@@ -0,0 +1,61 @@
+# $Id: SiteTags.py 14587 2009-07-19 13:18:50Z thierry $
+# $URL: http://svn.planet-lab.org/svn/PLCAPI/tags/PLCAPI-4.3-27/PLC/SiteTags.py $
+#
+# Thierry Parmentelat - INRIA
+#
+# $Revision: 14587 $
+#
+from PLC.Faults import *
+from PLC.Parameter import Parameter
+from PLC.Filter import Filter
+from PLC.Table import Row, Table
+from PLC.TagTypes import TagType, TagTypes
+from PLC.Sites import Site
+
+class SiteTag(Row):
+    """
+    Representation of a row in the site_tag.
+    To use, instantiate with a dict of values.
+    """
+
+    table_name = 'site_tag'
+    primary_key = 'site_tag_id'
+    fields = {
+        'site_tag_id': Parameter(int, "Site setting identifier"),
+        'site_id': Site.fields['site_id'],
+        'login_base': Site.fields['login_base'],
+        'tag_type_id': TagType.fields['tag_type_id'],
+        'tagname': TagType.fields['tagname'],
+        'description': TagType.fields['description'],
+        'category': TagType.fields['category'],
+        'min_role_id': TagType.fields['min_role_id'],
+        'value': Parameter(str, "Site setting value"),
+       ### relations
+       
+        }
+
+class SiteTags(Table):
+    """
+    Representation of row(s) from the site_tag table in the
+    database.
+    """
+
+    def __init__(self, api, site_tag_filter = None, columns = None):
+        Table.__init__(self, api, SiteTag, columns)
+
+        sql = "SELECT %s FROM view_site_tags WHERE True" % \
+              ", ".join(self.columns)
+
+        if site_tag_filter is not None:
+            if isinstance(site_tag_filter, (list, tuple, set)):
+                site_tag_filter = Filter(SiteTag.fields, {'site_tag_id': site_tag_filter})
+            elif isinstance(site_tag_filter, dict):
+                site_tag_filter = Filter(SiteTag.fields, site_tag_filter)
+            elif isinstance(site_tag_filter, int):
+                site_tag_filter = Filter(SiteTag.fields, {'site_tag_id': [site_tag_filter]})
+            else:
+                raise PLCInvalidArgument, "Wrong site setting filter %r"%site_tag_filter
+            sql += " AND (%s) %s" % site_tag_filter.sql(api)
+
+
+        self.selectall(sql)
index d31f4ea..ef9f02a 100644 (file)
@@ -45,6 +45,7 @@ class Site(Row):
         'node_ids': Parameter([int], "List of site node identifiers"),
         'peer_id': Parameter(int, "Peer to which this site belongs", nullok = True),
         'peer_site_id': Parameter(int, "Foreign site identifier at peer", nullok = True),
+        'site_tag_ids' : Parameter ([int], "List of tags attached to this site"),
        'ext_consortium_id': Parameter(int, "external consortium id", nullok = True)
         }
     related_fields = {
@@ -53,6 +54,10 @@ class Site(Row):
        'addresses': [Mixed(Parameter(int, "Address identifer"),
                             Filter(Address.fields))]
        }
+    view_tags_name = "view_site_tags"
+    # tags are used by the Add/Get/Update methods to expose tags
+    # this is initialized here and updated by the accessors factory
+    tags = { }
 
     def validate_name(self, name):
         if not len(name):
@@ -238,9 +243,14 @@ class Sites(Table):
 
     def __init__(self, api, site_filter = None, columns = None):
         Table.__init__(self, api, Site, columns)
-
-        sql = "SELECT %s FROM view_sites WHERE deleted IS False" % \
-              ", ".join(self.columns)
+       
+        view = "view_sites"
+        for tagname in self.tag_columns:
+            view= "%s left join %s using (%s)"%(view,Site.tagvalue_view_name(tagname),
+                                                Site.primary_key)
+            
+        sql = "SELECT %s FROM %s WHERE deleted IS False" % \
+            (", ".join(self.columns.keys()+self.tag_columns.keys()),view)
 
         if site_filter is not None:
             if isinstance(site_filter, (list, tuple, set)):
index 48a9f19..39306f5 100644 (file)
@@ -23,7 +23,7 @@ class TagType (Row):
 
     table_name = 'tag_types'
     primary_key = 'tag_type_id'
-    join_tables = ['node_tag', 'interface_tag', 'slice_tag' ]
+    join_tables = ['node_tag', 'interface_tag', 'slice_tag', 'site_tag' ]
     fields = {
         'tag_type_id': Parameter(int, "Node tag type identifier"),
         'tagname': Parameter(str, "Node tag type name", max = 100),
index 73385d7..5ee17c4 100644 (file)
@@ -1,8 +1,8 @@
 ## Please use make index to update this file
 all = """
-Addresses
-AddressTypes
 API
+AddressTypes
+Addresses
 Auth
 Boot
 BootStates
@@ -16,42 +16,42 @@ Filter
 GPG
 Ilinks
 InitScripts
-Interfaces
 InterfaceTags
-Keys
+Interfaces
 KeyTypes
+Keys
 Messages
 Method
 NetworkMethods
 NetworkTypes
 NodeGroups
-Nodes
 NodeTags
 NodeTypes
-Parameter
+Nodes
 PCUProtocolTypes
-PCUs
 PCUTypes
+PCUs
+POD
+Parameter
 Peers
 Persons
-POD
 PostgreSQL
 PyCurl
 Roles
-sendmail
 Sessions
-SFA
 Shell
+SiteTags
 Sites
 SliceInstantiations
-Slices
 SliceTags
+Slices
 Table
 TagTypes
-v42LegacyNodeNetworkSettings
+sendmail
+v42Legacy
 v42LegacyNodeNetworkSettingTypes
+v42LegacyNodeNetworkSettings
 v42LegacyNodeNetworks
-v42Legacy
-v42LegacySliceAttributes
 v42LegacySliceAttributeTypes
+v42LegacySliceAttributes
 """.split()