slices were on it and recreate them? do we make some sort of transaction log)
- Registry
-* sfa.plc.api.SfaAPI.fill_record_pl_info() should add the sites PIs to a slice records researchers list
+* sign peer gids
* update call should attempt to push updates to federated peers if
the peer has a record for an object that is updated locally
* api.update_membership() shoudl behave more like resolve when looking up records (attempt to resolve records at federated registeries) instead of only looking in the local registry
-* support generic registry records (dont depend on postgres!)
+* move db tables into db with less overhead (tokyocabinet?)
+* make resolve, fill_record_info more fault tolerent. Skip records with failures
- Auth Service
* develop a simple service where users auth using username/passord and
<description>The type of backend server for this
aggregate. Some aggregates may not be myplc.</description>
</variable>
+
+ <variable id="rspec_schema" type="string">
+ <name>RSpec Schema</name>
+ <value>/etc/sfa/pl.rng</value>
+ <description>The path to the default schema</description>
+ </variable>
<variable id="host" type="hostname">
<name>Hostname</name>
'config/geni_aggregates.xml',
'config/registries.xml',
'config/default_config.xml',
- 'config/sfi_config']),
+ 'config/sfi_config',
+ 'sfa/managers/pl/pl.rng']),
('/etc/sfatables/matches/', glob('sfatables/matches/*.xml')),
('/etc/sfatables/targets/', glob('sfatables/targets/*.xml')),
('/etc/init.d/', ['sfa/init.d/sfa', 'sfa/init.d/sfa-cm'])]
%define name sfa
%define version 0.9
-%define taglevel 10
+%define taglevel 11
%define release %{taglevel}%{?pldistro:.%{pldistro}}%{?date:.%{date}}
%global python_sitearch %( python -c "from distutils.sysconfig import get_python_lib; print get_python_lib(1)" )
%post cm
chkconfig --add sfa-cm
%changelog
+* Thu Apr 08 2010 Tony Mack <tmack@cs.princeton.edu> - sfa-0.9-11
+- SfaServer now uses a pool of threads to handle requests concurrently
+- sfa.util.rspec no longer used to process/manage rspecs (deprecated). This is now handled by sfa.plc.network and is not backwards compatible
+- PIs can now get a slice credential for any slice at their site without having to be a member of the slice
+- Registry records for federated peers (defined in registries.xml, aggregates.xml) updated when sfa service is started
+- Interfaces will try to fetch and install gids from peers listed in registries.xml/aggregates.xml if gid is not found in /etc/sfa/trusted_roots dir
+- Component manager does not install gid files if slice already has them
+
* Thu Jan 21 2010 anil vengalil <avengali@sophia.inria.fr> - sfa-0.9-10
- This tag is quite same as the previous one (sfa-0.9-9) except that the vini and max aggregate managers are also updated for urn support. Other features are:
- - sfa-config-tty now has the same features like plc-config-tty
print "Error: Object credential", object_hrn, "does not have delegate bit set"
return
- records = self.registry.resolve(user_cred, args[0])
+ records = self.registry.resolve(user_cred.save_to_string(save_parents=True), args[0])
records = filter_records("user", records)
if not records:
return
# the gid of the user who will be delegated to
- delegee_gid = records[0].get_gid_object()
+ delegee_gid = GID(string=records[0]['gid'])
delegee_hrn = delegee_gid.get_hrn()
-
+
# the key and hrn of the user who will be delegating
user_key = Keypair(filename = self.get_key_file())
user_hrn = user_cred.get_gid_caller().get_hrn()
-
- dcred = Credential(subject=object_hrn + " delegated to " + delegee_hrn)
+ subject_string = "%s delegated to %s" % (object_hrn, delegee_hrn)
+ dcred = Credential(subject=subject_string)
dcred.set_gid_caller(delegee_gid)
dcred.set_gid_object(object_gid)
privs = object_cred.get_privileges()
from sfa.util.policy import Policy
from sfa.util.record import *
from sfa.util.sfaticket import SfaTicket
-from sfa.server.registry import Registries
from sfa.util.debug import log
from sfa.plc.slices import Slices
import sfa.plc.peers as peers
slices = Slices(api)
peer = slices.get_peer(hrn)
sfa_peer = slices.get_sfa_peer(hrn)
- registries = Registries(api)
- registry = registries[api.hrn]
+ registry = api.registries[api.hrn]
credential = api.getCredential()
site_id, remote_site_id = slices.verify_site(registry, credential, hrn,
peer, sfa_peer)
slice = network.get_slice(api, hrn)
current = __get_hostnames(slice.get_nodes())
- network.addRSpec(xml, "/var/www/html/schemas/pl.rng")
+ network.addRSpec(xml, api.config.SFA_AGGREGATE_RSPEC_SCHEMA)
+
request = __get_hostnames(network.nodesWithSlivers())
# remove nodes not in rspec
def get_ticket(api, xrn, rspec, origin_hrn=None):
slice_hrn, type = urn_to_hrn(xrn)
# the the slice record
- registries = Registries(api)
- registry = registries[api.hrn]
+ registry = api.registries[api.hrn]
credential = api.getCredential()
records = registry.resolve(credential, xrn)
import types
import time
-from sfa.server.registry import Registries
from sfa.util.prefixTree import prefixTree
from sfa.util.record import SfaRecord
from sfa.util.table import SfaTable
# create a dict whre key is an registry hrn and its value is a
# hrns at that registry (determined by the known prefix tree).
xrn_dict = {}
- # XX Preload this into the api module
- registries = Registries(api)
+ registries = api.registries
tree = prefixTree()
registry_hrns = registries.keys()
tree.load(registry_hrns)
# load all know registry names into a prefix tree and attempt to find
# the longest matching prefix
records = []
- registries = Registries(api)
+ registries = api.registries
registry_hrns = registries.keys()
tree = prefixTree()
tree.load(registry_hrns)
type = record['type']
credential = api.getCredential()
- registries = Registries(api)
+ registries = api.registries
# Try to remove the object from the PLCDB of federated agg.
# This is attempted before removing the object from the local agg's PLCDB and sfa table
from sfa.util.rspec import *
from sfa.util.sfaticket import *
from sfa.util.debug import log
-from sfa.server.registry import Registries
-from sfa.server.aggregate import Aggregates
+from sfa.util.sfalogging import logger
import sfa.plc.peers as peers
def delete_slice(api, xrn, origin_hrn=None):
credential = api.getCredential()
- aggregates = Aggregates(api)
+ aggregates = api.aggregates
for aggregate in aggregates:
success = False
# request hash is optional so lets try the call without it
message = "%s (line %s)" % (error.message, error.line)
raise InvalidRSpec(message)
- aggs = Aggregates(api)
+ aggs = api.aggregates
cred = api.getCredential()
for agg in aggs:
if agg not in [api.auth.client_cred.get_gid_caller().get_hrn()]:
rspecs[net_hrn] = temp_rspec.toxml()
# send the rspec to the appropiate aggregate/sm
- aggregates = Aggregates(api)
+ aggregates = api.aggregates
credential = api.getCredential()
tickets = {}
for net_hrn in rspecs:
hrn, type = urn_to_hrn(xrn)
rspec = None
- aggs = Aggregates(api)
+ aggs = api.aggregates
cred = api.getCredential()
+
+ print >> log, "Aggregates = %s" % aggs
for agg in aggs:
if agg not in [api.auth.client_cred.get_gid_caller().get_hrn()]:
try:
# get the rspec from the aggregate
agg_rspec = aggs[agg].get_resources(cred, xrn, origin_hrn)
except:
+
# XX print out to some error log
print >> log, "Error getting resources at aggregate %s" % agg
traceback.print_exc(log)
print >> log, "%s" % (traceback.format_exc())
-
+ continue
try:
tree = etree.parse(StringIO(agg_rspec))
--- /dev/null
+from sfa.util.faults import *
+from sfa.util.namespace import *
+from sfa.util.method import Method
+from sfa.util.parameter import Parameter, Mixed
+from sfa.trust.auth import Auth
+from sfa.trust.gid import GID
+from sfa.trust.certificate import Certificate
+
+class CreateSliver(Method):
+ """
+ Allocate resources to a slice. This operation is expected to
+ start the allocated resources asynchornously after the operation
+ has successfully completed. Callers can check on the status of
+ the resources using SliverStatus.
+
+ @param slice_urn (string) URN of slice to allocate to
+ @param credentials ([string]) of credentials
+ @param rspec (string) rspec to allocate
+
+ """
+ interfaces = ['geni_am']
+ accepts = [
+ Parameter(str, "Slice URN"),
+ Parameter(type([str]), "List of credentials"),
+ Parameter(str, "RSpec")
+ ]
+ returns = Parameter(str, "Allocated RSpec")
+
+ def call(self, slice_xrn, creds, rspec):
+ hrn, type = urn_to_hrn(slice_xrn)
+
+ self.api.logger.info("interface: %s\ttarget-hrn: %s\tcaller-creds: %s\tmethod-name: %s"%(self.api.interface, hrn, creds, self.name))
+
+ # Validate that at least one of the credentials is good enough
+ found = False
+ for cred in creds:
+ try:
+ self.api.auth.check(cred, 'createslice')
+ found = True
+ break
+ except:
+ continue
+
+ if not found:
+ raise InsufficientRights('CreateSliver: Credentials either did not verify, were no longer valid, or did not have appropriate privileges')
+
+
+ manager_base = 'sfa.managers'
+
+ if self.api.interface in ['geni_am']:
+ mgr_type = self.api.config.SFA_GENI_AGGREGATE_TYPE
+ manager_module = manager_base + ".geni_am_%s" % mgr_type
+ manager = __import__(manager_module, fromlist=[manager_base])
+ return manager.CreateSlice(self.api, slice_xrn, creds, rspec)
+
+ return ''
+
--- /dev/null
+from sfa.util.faults import *
+from sfa.util.namespace import *
+from sfa.util.method import Method
+from sfa.util.parameter import Parameter, Mixed
+from sfa.trust.auth import Auth
+from sfa.trust.gid import GID
+from sfa.trust.certificate import Certificate
+
+class ListResources(Method):
+ """
+ Returns information about available resources or resources allocated to this slice
+ @param credential list
+ @param options dictionary
+ @return string
+ """
+ interfaces = ['geni_am']
+ accepts = [
+ Parameter(type([str]), "List of credentials"),
+ Parameter(dict, "Options")
+ ]
+ returns = Parameter(str, "List of resources")
+
+ def call(self, creds, options):
+ self.api.logger.info("interface: %s\tmethod-name: %s" % (self.api.interface, self.name))
+
+ # Validate that at least one of the credentials is good enough
+ found = False
+ for cred in creds:
+ try:
+ self.api.auth.check(cred, 'ListResources')
+ found = True
+ break
+ except:
+ continue
+
+ if not found:
+ raise InsufficientRights('ListResources: Credentials either did not verify, were no longer valid, or did not have appropriate privileges')
+
+ manager_base = 'sfa.managers'
+
+ if self.api.interface in ['geni_am']:
+ mgr_type = self.api.config.SFA_GENI_AGGREGATE_TYPE
+ manager_module = manager_base + ".geni_am_%s" % mgr_type
+ manager = __import__(manager_module, fromlist=[manager_base])
+ return manager.ListResources(self.api, creds, options)
+
+ return ''
+
else:
hrn, type = urn_to_hrn(xrn)
- #log the call
- if not origin_hrn:
+ #log the call
+ if not origin_hrn:
origin_hrn = Credential(string=cred).get_gid_caller().get_hrn()
- self.api.logger.info("interface: %s\tcaller-hrn: %s\ttarget-hrn: %s\tmethod-name: %s"%(self.api.interface, origin_hrn, hrn, self.name))
-
+ self.api.logger.info("interface: %s\tcaller-hrn: %s\ttarget-hrn: %s\tmethod-name: %s"%(self.api.interface, origin_hrn, hrn, self.name))
+ self.api.logger.info("get_credential cred = %s" % cred)
self.api.auth.check(cred, 'getcredential')
self.api.auth.verify_object_belongs_to_me(hrn)
self.api.logger.info("interface: %s\tcaller-hrn: %s\ttarget-hrn: %s\tmethod-name: %s"%(self.api.interface, origin_hrn, hrn, self.name))
# validate the cred
+ self.api.logger.info("Checking for %s" % self.api.interface)
+ #self.api.logger.info("Credential = %s" % cred)
self.api.auth.check(cred, 'listnodes')
+ self.api.logger.info("Checked out!")
+
# send the call to the right manager
manager_base = 'sfa.managers'
class get_trusted_certs(Method):
"""
@param cred credential string specifying the rights of the caller
- @return 1 is successful, faults otherwise
+ @return list of gid strings
"""
- interfaces = ['registry']
+ interfaces = ['registry', 'aggregate', 'slicemgr']
accepts = [
- Parameter(str, "Credential string")
+ Mixed(Parameter(str, "Credential string"),
+ Parameter(None, "Credential not specified"))
]
- returns = Parameter(int, "1 if successful")
+ returns = Parameter([str], "List of GID strings")
- def call(self, cred):
+ def call(self, cred = None):
+ # If cred is not specified just return the gid for this interface.
+ # This is true when when a peer is attempting to initiate federation
+ # with this interface
+ print cred
+ if not cred:
+ gid_strings = []
+ for gid in self.api.auth.trusted_cert_list:
+ if gid.get_hrn() == self.api.config.SFA_INTERFACE_HRN:
+ gid_strings.append(gid.save_to_string(save_parents=True))
+ return gid_strings
+
# authenticate the cred
self.api.auth.check(cred, 'gettrustedcerts')
-
- trusted_cert_strings = [gid.save_to_string(save_parents=True) for \
+ gid_strings = [gid.save_to_string(save_parents=True) for \
gid in self.api.auth.trusted_cert_list]
- return trusted_cert_strings
+ return gid_strings
from sfa.util.sfalogging import *
def list_to_dict(recs, key):
+ """
+ convert a list of dictionaries into a dictionary keyed on the
+ specified dictionary key
+ """
keys = [rec[key] for rec in recs]
return dict(zip(keys, recs))
rspec_type = self.config.get_aggregate_type()
if (rspec_type == 'pl' or rspec_type == 'vini'):
self.plshell = self.getPLCShell()
- self.plshell_version = self.getPLCShellVersion()
+ self.plshell_version = "4.3"
self.hrn = self.config.SFA_INTERFACE_HRN
self.time_format = "%Y-%m-%d %H:%M:%S"
'AuthMethod': 'password',
'AuthString': self.config.SFA_PLC_PASSWORD}
- try:
- sys.path.append(os.path.dirname(os.path.realpath("/usr/bin/plcsh")))
- self.plshell_type = 'direct'
- import PLC.Shell
- shell = PLC.Shell.Shell(globals = globals())
- shell.AuthCheck(self.plauth)
- return shell
- except ImportError:
- self.plshell_type = 'xmlrpc'
- # connect via xmlrpc
- url = self.config.SFA_PLC_URL
- shell = xmlrpclib.Server(url, verbose = 0, allow_none = True)
- shell.AuthCheck(self.plauth)
- return shell
-
- def getPLCShellVersion(self):
- # We need to figure out what version of PLCAPI we are talking to.
- # Some calls we need to make later will be different depending on
- # the api version.
- try:
- # This is probably a bad way to determine api versions
- # but its easy and will work for now. Lets try to make
- # a call that only exists is PLCAPI.4.3. If it fails, we
- # can assume the api version is 4.2
- self.plshell.GetTagTypes(self.plauth)
- return '4.3'
- except:
- return '4.2'
-
+
+ self.plshell_type = 'xmlrpc'
+ # connect via xmlrpc
+ url = self.config.SFA_PLC_URL
+ shell = xmlrpclib.Server(url, verbose = 0, allow_none = True)
+ return shell
def getCredential(self):
if self.interface in ['registry']:
# fill record info
for record in records:
- # records with pointer==-1 do not have plc info associated with them.
+ # records with pointer==-1 do not have plc info.
# for example, the top level authority records which are
# authorities, but not PL "sites"
if record['pointer'] == -1:
def fill_record_sfa_info(self, records):
# get person ids
- has_authority = False
- has_slice = False
person_ids = []
+ site_ids = []
for record in records:
- if record['type'] == 'authority':
- has_authority = True
person_ids.extend(record.get("person_ids", []))
+ site_ids.extend(record.get("site_ids", []))
+ if 'site_id' in record:
+ site_ids.append(record['site_id'])
+
+ # get all pis from the sites we've encountered
+ # and store them in a dictionary keyed on site_id
+ site_pis = {}
+ if site_ids:
+ pi_filter = {'|roles': ['pi'], '|site_ids': site_ids}
+ pi_list = self.plshell.GetPersons(self.plauth, pi_filter, ['person_id', 'site_ids'])
+ for pi in pi_list:
+ # we will need the pi's hrns also
+ person_ids.append(pi['person_id'])
+
+ # we also need to keep track of the sites these pis
+ # belong to
+ for site_id in pi['site_ids']:
+ if site_id in site_pis:
+ site_pis[site_id].append(pi)
+ else:
+ site_pis[site_id] = [pi]
+
+ # get sfa records for all records associated with these records.
+ # we'll replace pl ids (person_ids) with hrns from the sfa records
+ # we obtain
- # get sfa info
+ # get the sfa records
table = self.SfaTable()
person_list, persons = [], {}
- pl_person_list, pl_persons = [], {}
person_list = table.find({'type': 'user', 'pointer': person_ids})
persons = list_to_dict(person_list, 'pointer')
- if has_authority:
- pl_person_list = self.plshell.GetPersons(self.plauth, person_ids, ['person_id', 'roles'])
- pl_persons = list_to_dict(pl_person_list, 'person_id')
-
+
+ # get the pl records
+ pl_person_list, pl_persons = [], {}
+ pl_person_list = self.plshell.GetPersons(self.plauth, person_ids, ['person_id', 'roles'])
+ pl_persons = list_to_dict(pl_person_list, 'person_id')
# fill sfa info
for record in records:
sfa_info = {}
type = record['type']
if (type == "slice"):
+ # slice users
researchers = [persons[person_id]['hrn'] for person_id in record['person_ids'] \
if person_id in persons]
sfa_info['researcher'] = researchers
-
+ # pis at the slice's site
+ pl_pis = site_pis[record['site_id']]
+ pi_ids = [pi['person_id'] for pi in pl_pis]
+ sfa_info['PI'] = [persons[person_id]['hrn'] for person_id in pi_ids]
+
elif (type == "authority"):
pis, techs, admins = [], [], []
for pointer in record['person_ids']:
techs.append(hrn)
if 'admin' in roles:
admins.append(hrn)
-
sfa_info['PI'] = pis
sfa_info['operator'] = techs
sfa_info['owner'] = admins
from sfa.util.rspec import *
from sfa.util.specdict import *
from sfa.util.policy import Policy
-from sfa.server.aggregate import Aggregates
class Nodes(SimpleStorage):
end_time = int(threshold.strftime("%s"))
duration = end_time - start_time
- aggregates = Aggregates(self.api)
+ aggregates = self.api.aggregates
rspecs = {}
networks = []
rspec = RSpec()
from sfa.util.policy import Policy
from sfa.util.prefixTree import prefixTree
from sfa.util.debug import log
-from sfa.server.aggregate import Aggregates
-from sfa.server.registry import Registries
MAXINT = 2L**31-1
def refresh_slices_smgr(self):
slice_hrns = []
- aggregates = Aggregates(self.api)
credential = self.api.getCredential()
- for aggregate in aggregates:
+ for aggregate in self.api.aggregates:
success = False
- # request hash is optional so lets try the call without it
try:
- slices = aggregates[aggregate].get_slices(credential)
+ slices = self.api.aggregates[aggregate].get_slices(credential)
slice_hrns.extend(slices)
success = True
except:
print >> log, "%s" % (traceback.format_exc())
print >> log, "Error calling slices at aggregate %(aggregate)s" % locals()
- # try sending the request hash if the previous call failed
- if not success:
- arg_list = [credential]
- try:
- slices = aggregates[aggregate].get_slices(credential)
- slice_hrns.extend(slices)
- success = True
- except:
- print >> log, "%s" % (traceback.format_exc())
- print >> log, "Error calling slices at aggregate %(aggregate)s" % locals()
-
# update timestamp and threshold
timestamp = datetime.datetime.now()
hr_timestamp = timestamp.strftime(self.api.time_format)
slicename = hrn_to_pl_slicename(hrn)
slice = {}
slice_record = None
- registries = Registries(self.api)
- registry = registries[self.api.hrn]
+ registry = self.api.registries[self.api.hrn]
credential = self.api.getCredential()
site_id, remote_site_id = self.verify_site(registry, credential, hrn, peer, sfa_peer)
### $Id$
### $URL$
-import os
-import sys
-import datetime
-import time
-import xmlrpclib
-from types import StringTypes, ListType
from sfa.util.server import SfaServer
-from sfa.util.storage import *
from sfa.util.faults import *
+from sfa.server.interface import Interfaces
import sfa.util.xmlrpcprotocol as xmlrpcprotocol
import sfa.util.soapprotocol as soapprotocol
-# GeniLight client support is optional
-try:
- from egeni.geniLight_client import *
-except ImportError:
- GeniClientLight = None
-
class Aggregate(SfaServer):
##
# Aggregates is a dictionary of aggregate connections keyed on the aggregate hrn
-class Aggregates(dict):
-
- required_fields = ['hrn', 'addr', 'port']
-
- def __init__(self, api, file = "/etc/sfa/aggregates.xml"):
- dict.__init__(self, {})
- self.api = api
- self.interfaces = []
- # create default connection dict
- connection_dict = {}
- for field in self.required_fields:
- connection_dict[field] = ''
- aggregates_dict = {'aggregates': {'aggregate': [connection_dict]}}
- # get possible config file locations
- loaded = False
- path = os.path.dirname(os.path.abspath(__file__))
- filename = file.split(os.sep)[-1]
- alt_file = path + os.sep + filename
- files = [file, alt_file]
-
- for f in files:
- try:
- if os.path.isfile(f):
- self.aggregate_info = XmlStorage(f, aggregates_dict)
- loaded = True
- except: pass
+class Aggregates(Interfaces):
- # if file is missing, just recreate it in the right place
- if not loaded:
- self.aggregate_info = XmlStorage(file, aggregates_dict)
- self.aggregate_info.load()
- self.connectAggregates()
+ default_dict = {'aggregates': {'aggregate': [Interfaces.default_fields]}}
+
+ def __init__(self, api, conf_file = "/etc/sfa/aggregates.xml"):
+ Interfaces.__init__(self, api, conf_file, 'ma')
- def connectAggregates(self):
+ def get_connections(self, interfaces):
"""
Get connection details for the trusted peer aggregates from file and
create an connection to each.
"""
- aggregates = self.aggregate_info['aggregates']['aggregate']
- if isinstance(aggregates, dict):
- aggregates = [aggregates]
- if isinstance(aggregates, list):
- for aggregate in aggregates:
- # make sure the required fields are present
- if not set(self.required_fields).issubset(aggregate.keys()):
- continue
- hrn, address, port = aggregate['hrn'], aggregate['addr'], aggregate['port']
- if not hrn or not address or not port:
- continue
- self.interfaces.append(aggregate)
- # check which client we should use
- # sfa.util.xmlrpcprotocol is default
- client_type = 'xmlrpcprotocol'
- if aggregate.has_key('client') and aggregate['client'] in ['geniclientlight']:
- client_type = 'geniclientlight'
-
- # create url
- url = 'http://%(address)s:%(port)s' % locals()
-
- # create the client connection
- # make sure module exists before trying to instantiate it
- if client_type in ['geniclientlight'] and GeniClientLight:
- self[hrn] = GeniClientLight(url, self.api.key_file, self.api.cert_file)
- else:
- self[hrn] = xmlrpcprotocol.get_server(url, self.api.key_file, self.api.cert_file)
+ connections = Interfaces.get_connections(self, interfaces)
# set up a connection to the local registry
address = self.api.config.SFA_AGGREGATE_HOST
url = 'http://%(address)s:%(port)s' % locals()
local_aggregate = {'hrn': self.api.hrn, 'addr': address, 'port': port}
self.interfaces.append(local_aggregate)
- self[self.api.hrn] = xmlrpcprotocol.get_server(url, self.api.key_file, self.api.cert_file)
-
+ connections[self.api.hrn] = xmlrpcprotocol.get_server(url, self.api.key_file, self.api.cert_file)
+ return connections
--- /dev/null
+#
+### $Id: interface.py 17583 2010-04-06 15:01:08Z tmack $
+### $URL: https://svn.planet-lab.org/svn/sfa/trunk/sfa/server/interface.py $
+#
+
+
+from sfa.util.faults import *
+from sfa.util.storage import *
+from sfa.util.namespace import *
+from sfa.trust.gid import GID
+from sfa.util.table import SfaTable
+from sfa.util.record import SfaRecord
+import sfa.util.xmlrpcprotocol as xmlrpcprotocol
+import sfa.util.soapprotocol as soapprotocol
+
+
+# GeniLight client support is optional
+try:
+ from egeni.geniLight_client import *
+except ImportError:
+ GeniClientLight = None
+
+
+##
+# In is a dictionary of registry connections keyed on the registry
+# hrn
+
+class Interfaces(dict):
+ """
+ Interfaces is a base class for managing information on the
+ peers we are federated with. It is responsible for the following:
+
+ 1) Makes sure a record exist in the local registry for the each
+ fedeated peer
+ 2) Attepts to fetch and install trusted gids
+ 3) Provides connections (xmlrpc or soap) to federated peers
+ """
+
+ # fields that must be specified in the config file
+ default_fields = {
+ 'hrn': '',
+ 'addr': '',
+ 'port': '',
+ }
+
+ # defined by the class
+ default_dict = {}
+
+ # allowed types
+ types = ['sa', 'ma']
+
+ def __init__(self, api, conf_file, type):
+ if type not in self.types:
+ raise SfaInfaildArgument('Invalid type %s: must be in %s' % (type, self.types))
+ dict.__init__(self, {})
+ self.api = api
+ self.type = type
+ # load config file
+ self.interface_info = XmlStorage(conf_file, self.default_dict)
+ self.interface_info.load()
+ self.interfaces = self.interface_info.values()[0].values()[0]
+ if not isinstance(self.interfaces, list):
+ self.interfaces = [self.interfaces]
+ # get connections
+ self.update(self.get_connections(self.interfaces))
+
+ def sync_interfaces(self):
+ """
+ Install missing trusted gids and db records for our federated
+ interfaces
+ """
+ # Attempt to get any missing peer gids
+ # There should be a gid file in /etc/sfa/trusted_roots for every
+ # peer registry found in in the registries.xml config file. If there
+ # are any missing gids, request a new one from the peer registry.
+ gids_current = self.api.auth.trusted_cert_list
+ hrns_current = [gid.get_hrn() for gid in gids_current]
+ hrns_expected = [interface['hrn'] for interface in self.interfaces]
+ new_hrns = set(hrns_expected).difference(hrns_current)
+ self.get_peer_gids(new_hrns)
+
+ # update the local db records for these registries
+ self.update_db_records(self.type)
+
+ def get_peer_gids(self, new_hrns):
+ """
+ Install trusted gids from the specified interfaces.
+ """
+ if not new_hrns:
+ return
+ trusted_certs_dir = self.api.config.get_trustedroots_dir()
+ for new_hrn in new_hrns:
+ # the gid for this interface should already be installed
+ if new_hrn == self.api.config.SFA_INTERFACE_HRN:
+ continue
+ try:
+ # get gid from the registry
+ interface = self.get_connections(self.interfaces[new_hrn])[new_hrn]
+ trusted_gids = interface.get_trusted_certs()
+ # default message
+ message = "interface: %s\tunable to install trusted gid for %s" % \
+ (self.api.interface, new_hrn)
+ if trusted_gids:
+ # the gid we want shoudl be the first one in the list,
+ # but lets make sure
+ for trusted_gid in trusted_gids:
+ gid = GID(string=trusted_gids[0])
+ if gid.get_hrn() == new_hrn:
+ gid_filename = os.path.join(trusted_certs_dir, '%s.gid' % new_hrn)
+ gid.save_to_file(gid_filename, save_parents=True)
+ message = "interface: %s\tinstalled trusted gid for %s" % \
+ (self.api.interface, new_hrn)
+ # log the message
+ self.api.logger.info(message)
+ except:
+ message = "interface: %s\tunable to install trusted gid for %s" % \
+ (self.api.interface, new_hrn)
+ self.api.logger.info(message)
+
+ # reload the trusted certs list
+ self.api.auth.load_trusted_certs()
+
+ def update_db_records(self, type):
+ """
+ Make sure there is a record in the local db for allowed registries
+ defined in the config file (registries.xml). Removes old records from
+ the db.
+ """
+ # get hrns we expect to find
+ # ignore records for local interfaces
+ ignore_interfaces = [self.api.config.SFA_INTERFACE_HRN]
+ hrns_expected = [interface['hrn'] for interface in self.interfaces \
+ if interface['hrn'] not in ignore_interfaces]
+
+ # get hrns that actually exist in the db
+ table = SfaTable()
+ records = table.find({'type': type})
+ hrns_found = [record['hrn'] for record in records]
+
+ # remove old records
+ for record in records:
+ if record['hrn'] not in hrns_expected:
+ table.remove(record)
+
+ # add new records
+ for hrn in hrns_expected:
+ if hrn not in hrns_found:
+ record = {
+ 'hrn': hrn,
+ 'type': type,
+ 'pointer': -1,
+ 'authority': get_authority(hrn),
+ }
+ record = SfaRecord(dict=record)
+ table.insert(record)
+
+
+ def get_connections(self, interfaces):
+ """
+ read connection details for the trusted peer registries from file return
+ a dictionary of connections keyed on interface hrn.
+ """
+ connections = {}
+ required_fields = self.default_fields.keys()
+ if not isinstance(interfaces, list):
+ interfaces = [interfaces]
+ for interface in interfaces:
+ # make sure the required fields are present and not null
+ if not all([interface.get(key) for key in required_fields]):
+ continue
+
+ hrn, address, port = interface['hrn'], interface['addr'], interface['port']
+ url = 'http://%(address)s:%(port)s' % locals()
+
+ # check which client we should use
+ # sfa.util.xmlrpcprotocol is default
+ client_type = 'xmlrpcprotocol'
+ if interface.has_key('client') and \
+ interface['client'] in ['geniclientlight'] and \
+ GeniClientLight:
+ client_type = 'geniclientlight'
+ connections[hrn] = GeniClientLight(url, self.api.key_file, self.api.cert_file)
+ else:
+ connections[hrn] = xmlrpcprotocol.get_server(url, self.api.key_file, self.api.cert_file)
+
+ return connections
### $URL$
#
-import tempfile
-import os
-import time
-import sys
-
from sfa.util.server import SfaServer
from sfa.util.faults import *
-from sfa.util.storage import *
+from sfa.server.interface import Interfaces
import sfa.util.xmlrpcprotocol as xmlrpcprotocol
import sfa.util.soapprotocol as soapprotocol
-# GeniLight client support is optional
-try:
- from egeni.geniLight_client import *
-except ImportError:
- GeniClientLight = None
##
# Registry is a SfaServer that serves registry and slice operations at PLC.
-
class Registry(SfaServer):
##
# Create a new registry object.
# @param port the port to listen on
# @param key_file private key filename of registry
# @param cert_file certificate filename containing public key (could be a GID file)
-
+
def __init__(self, ip, port, key_file, cert_file):
SfaServer.__init__(self, ip, port, key_file, cert_file)
self.server.interface = 'registry'
# Registries is a dictionary of registry connections keyed on the registry
# hrn
-class Registries(dict):
-
- required_fields = ['hrn', 'addr', 'port']
-
- def __init__(self, api, file = "/etc/sfa/registries.xml"):
- dict.__init__(self, {})
- self.api = api
- self.interfaces = []
-
- # create default connection dict
- connection_dict = {}
- for field in self.required_fields:
- connection_dict[field] = ''
- registries_dict = {'registries': {'registry': [connection_dict]}}
-
- # get possible config file locations
- loaded = False
- path = os.path.dirname(os.path.abspath(__file__))
- filename = file.split(os.sep)[-1]
- alt_file = path + os.sep + filename
- files = [file, alt_file]
+class Registries(Interfaces):
- for f in files:
- try:
- if os.path.isfile(f):
- self.registry_info = XmlStorage(f, registries_dict)
- loaded = True
- except: pass
+ default_dict = {'registries': {'registry': [Interfaces.default_fields]}}
+
+ def __init__(self, api, conf_file = "/etc/sfa/registries.xml"):
+ Interfaces.__init__(self, api, conf_file, 'sa')
- # if file is missing, just recreate it in the right place
- if not loaded:
- self.registry_info = XmlStorage(file, registries_dict)
- self.registry_info.load()
- self.connectRegistries()
-
- def connectRegistries(self):
+ def get_connections(self, interfaces):
"""
- Get connection details for the trusted peer registries from file and
- create a connection to each.
+ read connection details for the trusted peer registries from file return
+ a dictionary of connections keyed on interface hrn.
"""
- registries = self.registry_info['registries']['registry']
- if isinstance(registries, dict):
- registries = [registries]
- if isinstance(registries, list):
- for registry in registries:
- # make sure the required fields are present
- if not set(self.required_fields).issubset(registry.keys()):
- continue
- hrn, address, port = registry['hrn'], registry['addr'], registry['port']
- if not hrn or not address or not port:
- continue
- self.interfaces.append(registry)
- # check which client we should use
- # sfa.util.xmlrpcprotocol is default
- client_type = 'xmlrpcprotocol'
- if registry.has_key('client') and registry['client'] in ['geniclientlight']:
- client_type = 'geniclientlight'
-
- # create url
- url = 'http://%(address)s:%(port)s' % locals()
-
- # create the client connection
- # make sure module exists before trying to instantiate it
- if client_type in ['geniclientlight'] and GeniClientLight:
- self[hrn] = GeniClientLight(url, self.api.key_file, self.api.cert_file)
- else:
- self[hrn] = xmlrpcprotocol.get_server(url, self.api.key_file, self.api.cert_file)
+ connections = Interfaces.get_connections(self, interfaces)
# set up a connection to the local registry
address = self.api.config.SFA_REGISTRY_HOST
port = self.api.config.SFA_REGISTRY_PORT
url = 'http://%(address)s:%(port)s' % locals()
local_registry = {'hrn': self.api.hrn, 'addr': address, 'port': port}
- self.interfaces.append(local_registry)
- self[self.api.hrn] = xmlrpcprotocol.get_server(url, self.api.key_file, self.api.cert_file)
-
+ connections[self.api.hrn] = xmlrpcprotocol.get_server(url, self.api.key_file, self.api.cert_file)
+ return connections
from sfa.trust.hierarchy import Hierarchy
from sfa.util.config import Config
from sfa.util.report import trace
+from sfa.plc.api import SfaAPI
+from sfa.server.registry import Registries
+from sfa.server.aggregate import Aggregates
# after http://www.erlenstar.demon.co.uk/unix/faq_2.html
def daemon():
manager.init_server()
+def sync_interfaces():
+ """
+ Attempt to install missing trusted gids and db records for
+ our federated interfaces
+ """
+ api = SfaAPI()
+ registries = Registries(api)
+ aggregates = Aggregates(api)
+ registries.sync_interfaces()
+ aggregates.sync_interfaces()
+
def main():
# xxx get rid of globals - name consistently CamelCase or under_score
global AuthHierarchy
config = Config()
hierarchy = Hierarchy()
- trusted_roots = TrustedRootList(config.get_trustedroots_dir())
server_key_file = os.path.join(hierarchy.basedir, "server.key")
server_cert_file = os.path.join(hierarchy.basedir, "server.cert")
init_server_key(server_key_file, server_cert_file, config, hierarchy)
- init_server(options, config)
+ init_server(options, config)
+ sync_interfaces()
# start registry server
if (options.registry):
from sfa.util.config import Config
import sfa.util.xmlrpcprotocol as xmlrpcprotocol
from sfa.util.namespace import *
+from sfa.util.faults import *
from sfa.trust.certificate import Keypair, Certificate
from sfa.trust.credential import Credential
from sfa.trust.gid import GID
from sfa.trust.hierarchy import Hierarchy
+KEYDIR = "/var/lib/sfa/"
+CONFDIR = "/etc/sfa/"
+
+def handle_gid_mismatch_exception(f):
+ def wrapper(*args, **kwds):
+ try: return f(*args, **kwds)
+ except ConnectionKeyGIDMismatch:
+ # clean regen server keypair and try again
+ print "cleaning keys and trying again"
+ clean_key_cred()
+ return f(args, kwds)
+
+ return wrapper
+
def get_server(url=None, port=None, keyfile=None, certfile=None,verbose=False):
"""
returns an xmlrpc connection to the service a the specified
for dir in all_dirs:
if not os.path.exists(dir):
os.makedirs(dir)
+
+def has_node_key():
+ key_file = KEYDIR + os.sep + 'server.key'
+ return os.path.exists(key_file)
+
+def clean_key_cred():
+ """
+ remove the existing keypair and cred and generate new ones
+ """
+ files = ["server.key", "server.cert", "node.cred"]
+ for f in files:
+ filepath = KEYDIR + os.sep + f
+ if os.path.isfile(filepath):
+ os.unlink(f)
+
+ # install the new key pair
+ # get_credential will take care of generating the new keypair
+ # and credential
+ get_credential()
+
def get_node_key(registry=None, verbose=False):
# this call requires no authentication,
cert.set_pubkey(key)
cert.sign()
cert.save_to_file(certfile, save_parents=True)
-
+
+@handle_gid_mismatch_exception
def get_credential(registry=None, force=False, verbose=False):
config = Config()
hierarchy = Hierarchy()
return cred
+@handle_gid_mismatch_exception
def get_trusted_certs(registry=None, verbose=False):
"""
refresh our list of trusted certs.
print "Removing old gid ", gid_name
os.unlink(trusted_certs_dir + os.sep + gid_name)
+@handle_gid_mismatch_exception
def get_gids(registry=None, verbose=False):
"""
Get the gid for all instantiated slices on this node and store it
api = ComponentAPI()
xids_tuple = api.nodemanager.GetXIDs()
slices = eval(xids_tuple[1])
- slicenames = slices.keys()
- hrns = [slicename_to_hrn(interface_hrn, slicename) for slicename in slicenames]
-
+ slicenames = slices.keys()
+ # generate a list of slices that dont have gids installed
+ slices_without_gids = []
+ for slicename in slicenames:
+ if not os.path.isfile("/vservers/%s/etc/slice.gid" % slicename) \
+ or not os.path.isfile("/vservers/%s/etc/node.gid" % slicename):
+ slices_without_gids.append(slicename)
+
+ # convert slicenames to hrns
+ hrns = [slicename_to_hrn(interface_hrn, slicename) \
+ for slicename in slices_without_gids]
+
+ # exit if there are no gids to install
+ print hrns
+ if not hrns:
+ return
+
if verbose:
print "Getting gids for slices on this node from registry"
# get the gids
self.hierarchy = Hierarchy()
if not config:
self.config = Config()
- self.trusted_cert_list = TrustedRootList(self.config.get_trustedroots_dir()).get_list()
+ self.load_trusted_certs()
self.trusted_cert_file_list = TrustedRootList(self.config.get_trustedroots_dir()).get_file_list()
-
+ def load_trusted_certs(self):
+ self.trusted_cert_list = TrustedRootList(self.config.get_trustedroots_dir()).get_list()
+
def check(self, cred, operation):
"""
Check the credential against the peer cert (callerGID included
pis = record.get("PI", [])
operators = record.get("operator", [])
if (caller_hrn == self.config.SFA_INTERFACE_HRN):
- rl.add("authority,sa,ma",)
+ rl.add("authority")
+ rl.add("sa")
+ rl.add("ma")
if (caller_hrn in pis):
- rl.add("authority,sa")
+ rl.add("authority")
+ rl.add("sa")
if (caller_hrn in operators):
- rl.add("authority,ma")
+ rl.add("authority")
+ rl.add("ma")
elif type == "user":
rl.add("refresh")
"operator": ["gettrustedcerts", "getgids"]}
+
##
# Determine tje rights that an object should have. The rights are entirely
# dependent on the type of the object. For example, users automatically
rl.add("resolve")
rl.add("info")
elif type == "sa":
- rl.add("authority,sa")
+ rl.add("authority")
+ rl.add("sa")
elif type == "ma":
- rl.add("authority,ma")
+ rl.add("authority")
+ rl.add("ma")
elif type == "authority":
- rl.add("authority,sa,ma")
+ rl.add("authority")
+ rl.add("sa")
+ rl.add("ma")
elif type == "slice":
rl.add("refresh")
rl.add("embed")
# Load configuration
self.config = Config(config)
self.auth = Auth(peer_cert)
+ self.hrn = self.config.SFA_INTERFACE_HRN
self.interface = interface
self.key_file = key_file
self.key = Keypair(filename=self.key_file)
self.source = None
self.time_format = "%Y-%m-%d %H:%M:%S"
self.logger=get_sfa_logger()
+
+ # load registries
+ from sfa.server.registry import Registries
+ self.registries = Registries(self)
+
+ # load aggregates
+ from sfa.server.aggregate import Aggregates
+ self.aggregates = Aggregates(self)
def callable(self, method):
'url': Parameter(str, 'Slice url'),
'expires': Parameter(int, 'Date and time this slice exipres'),
'researcher': Parameter([str], 'List of users for this slice'),
+ 'PI': Parameter([str], 'List of PIs responsible for this slice'),
'description': Parameter([str], 'Description of this slice'),
}
fields.update(SfaRecord.fields)
cert_file = self.server.cert_file)
# get arguments
request = self.rfile.read(int(self.headers["content-length"]))
- # In previous versions of SimpleXMLRPCServer, _dispatch
- # could be overridden in this class, instead of in
- # SimpleXMLRPCDispatcher. To maintain backwards compatibility,
- # check to see if a subclass implements _dispatch and dispatch
- # using that method if present.
- #response = self.server._marshaled_dispatch(request, getattr(self, '_dispatch', None))
remote_addr = (remote_ip, remote_port) = self.connection.getpeername()
self.api.remote_addr = remote_addr
response = self.api.handle(remote_addr, request)
def drop(self):
try:
- self.db.do('DROP TABLE IF EXISTS ' + self.tablename)
+ self.db.do('DROP TABLE IF EXISTS ' + self.tablename)
except:
-
try:
self.db.do('DROP TABLE ' + self.tablename)
except: