# # Functions for interacting with the persons peers in the database # # # import re from types import StringTypes import traceback from urlparse import urlparse import PLC.Auth from PLC.Debug import log from PLC.Faults import * from PLC.Namespace import hostname_to_hrn from PLC.Parameter import Parameter, Mixed from PLC.Filter import Filter from PLC.Table import Row, Table from PLC.Sites import Site, Sites from PLC.Persons import Person, Persons from PLC.Keys import Key, Keys from PLC.Nodes import Node, Nodes from PLC.TagTypes import TagType, TagTypes from PLC.NodeTags import NodeTag, NodeTags from PLC.SliceTags import SliceTag, SliceTags from PLC.Slices import Slice, Slices from PLC.Storage.AlchemyObject import AlchemyObj class Peer(AlchemyObj): """ Representation of a row in the peers table. To use, optionally instantiate with a dict of values. Update as you would a dict. Commit to the database with sync(). """ tablename = 'peers' fields = { 'peer_id': Parameter (int, "Peer identifier", primary_key=True), 'peername': Parameter (str, "Peer name"), 'peer_url': Parameter (str, "Peer API URL"), 'key': Parameter(str, "Peer GPG public key"), 'cacert': Parameter(str, "Peer SSL public certificate"), 'shortname' : Parameter(str, "Peer short name"), 'hrn_root' : Parameter(str, "Root of this peer in a hierarchical naming space"), ### cross refs 'site_ids': Parameter([int], "List of sites for which this peer is authoritative", joined=True), 'person_ids': Parameter([int], "List of users for which this peer is authoritative", joined=True), 'key_ids': Parameter([int], "List of keys for which this peer is authoritative", joined=True), 'node_ids': Parameter([int], "List of nodes for which this peer is authoritative", joined=True), 'slice_ids': Parameter([int], "List of slices for which this peer is authoritative", joined=True), } def validate_peer_url(self, url): """ Validate URL. Must be HTTPS. """ (scheme, netloc, path, params, query, fragment) = urlparse(url) if scheme != "https": raise PLCInvalidArgument, "Peer URL scheme must be https" if path[-1] != '/': raise PLCInvalidArgument, "Peer URL should end with /" return url def validate_peername(self, peername): if not len(peername): raise PLCInvalidArgument, "Peer name must be specified" conflicts = Peer().select(filter={'peername': peername}) for peer in conflicts: if 'peer_id' not in self or self['peer_id'] != peer.peer_id: raise PLCInvalidArgument, "Peer name already in use" return peername def add_site(self, site, peer_site_id, commit=True): assert 'peer_id' in self fields = {'peer_id': self['peer_id'], 'site_id': site['site_id'], 'peer_site_id': peer_site_id} peer_site = PeerSite(self.api, fields) peer_site.sync() def remove_site(self, site_filter, commit = True): assert 'peer_id' in self assert 'site_id' in site_filter PeerSite().delete(self, site_filter) def add_person(self, person, peer_person_id, commit = True): assert 'peer_id' in self fields = {'peer_id': self['peer_id'], 'person_id': person['person_id'], 'peer_person_id': peer_person_id} peer_person = PeerPerson(self.api, fields) peer_person.sync() def remove_person(self, person_filter, commit = True): assert 'peer_id' in self assert 'person_id' in person_filter PeerPerson().delete(self, person_filter) def add_key(self, key, peer_key_id, commit = True): assert 'peer_id' in self fields = {'peer_id': self['peer_id'], 'key_id': key['key_id'], 'peer_key_id': peer_key_id} peer_key = PeerKey(self.api, fields) peer_key.sync() def remove_key(self, key_filter, commit = True): assert 'peer_id' in self assert 'key_id' in key_filter PeerKey().delete(self, key_filter) def add_node(self, node, peer_node_id, commit = True): assert 'peer_id' in self fields = {'peer_id': self['peer_id'], 'node_id': node['node_id'], 'peer_person_id': peer_node_id} peer_node = PeerNode(self.api, fields) peer_node.sync() def remove_node(self, node_filter, commit = True): assert 'peer_id' in self assert 'node_id' in node_filter PeerNode().delete(self, node_filter) def add_slice(self, slice, peer_slice_id, commit = True): assert 'peer_id' in self fields = {'peer_id': self['peer_id'], 'slice_id': slice['slice_id'], 'peer_person_id': peer_person_id} peer_slice = PeerSlice(self.api, fields) peer_slice.sync() def remove_slice(self, slice_filter, commit = True): assert 'peer_id' in self assert 'slice_id' in slice_filter PeerSlice().delete(self, slice_filter) def sync(self, commit=True, validate=True): AlchemyObj.sync(self, commit=commit, validate=validate) # filter out fields that are not supported in keystone if 'peer_id' not in self: AlchemyObj.insert(self, dict(self)) else: AlchemyObj.update(self, {'peer_id': self['peer_id']}, dict(self)) def delete(self): assert 'peer_id' in self # delete relationships Slices().delete.filter({'peer_id': self['peer_id']}) Keys().delete.filter({'peer_id': self['peer_id']}) Persons().delete.filter({'peer_id': self['peer_id']}) Nodes().delete.filter({'peer_id': self['peer_id']}) Sites().delete.filter({'peer_id': self['peer_id']}) # delete peer AlchemyObj.delete(self, dict(self)) def connect(self, **kwds): """ Connect to this peer via XML-RPC. """ import xmlrpclib from PLC.PyCurl import PyCurlTransport self.server = xmlrpclib.ServerProxy(self['peer_url'], PyCurlTransport(self['peer_url'], self['cacert']), allow_none = 1, **kwds) def add_auth(self, function, methodname, **kwds): """ Sign the specified XML-RPC call and add an auth struct as the first argument of the call. """ def wrapper(*args, **kwds): from PLC.GPG import gpg_sign signature = gpg_sign(args, self.api.config.PLC_ROOT_GPG_KEY, self.api.config.PLC_ROOT_GPG_KEY_PUB, methodname) auth = {'AuthMethod': "gpg", 'name': self.api.config.PLC_NAME, 'signature': signature} # Automagically add auth struct to every call args = (auth,) + args return function(*args) return wrapper def __getattr__(self, attr): """ Returns a callable API function if attr is the name of a PLCAPI function; otherwise, returns the specified attribute. """ try: # Figure out if the specified attribute is the name of a # PLCAPI function. If so and the function requires an # authentication structure as its first argument, return a # callable that automagically adds an auth struct to the # call. methodname = attr api_function = self.api.callable(methodname) if api_function.accepts and \ (isinstance(api_function.accepts[0], PLC.Auth.Auth) or \ (isinstance(api_function.accepts[0], Mixed) and \ filter(lambda param: isinstance(param, Auth), api_function.accepts[0]))): function = getattr(self.server, methodname) return self.add_auth(function, methodname) except Exception, err: pass if hasattr(self, attr): return getattr(self, attr) else: raise AttributeError, "type object 'Peer' has no attribute '%s'" % attr class Peers(list): """ Representation of row(s) from the persons table in the database. """ def __init__(self, api, peer_filter = None, columns = None): if not peer_filter: #persons = self.api.client_shell.keystone.users.findall() peers = Peer().select() elif isinstance(peer_filter, (list, tuple, set)): ints = filter(lambda x: isinstance(x, (int, long)), peer_filter) strs = filter(lambda x: isinstance(x, StringTypes), peer_filter) peer_filter = {'peer_id': ints, 'peername': strs} peers = Peer().select(filter=peer_filter) elif isinstance(peer_filter, dict): peers = Peer().select(filter=peer_filter) elif isinstance (peer_filter, StringTypes): peers = Peer().select(filter={'peername': peer_filter}) elif isinstance (peer_filter, (int, long)): peers = Peer().select(filter={'peer_id': peer_filter}) else: raise PLCInvalidArgument, "Wrong peer filter %r"%peer_filter for peer in peers: peer = Peer(self.api, object=peer) self.append(peer)