module = __import__(fullpath, globals(), locals(), [classname])
return getattr(module, classname)(self)
except ImportError, AttributeError:
- raise PLCInvalidAPIMethod, "import error %s for %s" % (AttributeError,fullpath)
+ raise
+ #raise PLCInvalidAPIMethod, "import error %s for %s" % (AttributeError,fullpath)
def call(self, source, method, *args):
"""
from types import StringTypes
from StringIO import StringIO
from xml.dom import minidom
-from xml.dom.ext import Canonicalize
+import lxml
+from lxml import etree
from subprocess import Popen, PIPE, call
from tempfile import NamedTemporaryFile, mkdtemp
"""
xml = xmlrpclib.dumps(args, methodname, methodresponse, encoding = 'utf-8', allow_none = 1)
- dom = minidom.parseString(xml)
-
- # Canonicalize(), though it claims to, does not encode unicode
- # nodes to UTF-8 properly and throws an exception unless you write
- # the stream to a file object, so just encode it ourselves.
- buf = StringIO()
- Canonicalize(dom, output = buf)
- xml = buf.getvalue().encode('utf-8')
-
- return xml
-
+ parser = etree.XMLParser(remove_blank_text=True)
+ tree = etree.parse(StringIO(xml), parser)
+ return etree.tostring(tree.getroot(), method='c14n')
+
def gpg_export(keyring, armor = True):
"""
Exports the specified public keyring file.
if isinstance(interface_tag_filter, (list, tuple, set, int, long)):
interface_tags = InterfaceTag().select(filter={'interface_tag_id': interface_tag_filter})
elif isinstance(interface_tag_filter, dict):
- interface_tags = InterfaceTag().select(filter=interface_tag_filter})
+ interface_tags = InterfaceTag().select(filter=interface_tag_filter)
else:
raise PLCInvalidArgument, "Wrong interface setting filter %r"%interface_tag_filter
from PLC.Debug import profile
from PLC.Table import Row, Table
from PLC.KeyTypes import KeyType, KeyTypes
-from PLC.NovaTable import NovaObject, NovaTable
+from PLC.Storage.AlchemyObject import AlchemyObj
-class Key(NovaObject):
+class Key(AlchemyObj):
"""
Representation of a row in the keys table. To use, instantiate with a
dict of values. Update as you would a dict. Commit to the database
"""
tablename = 'keys'
- join_tables = ['person_key', 'peer_key']
fields = {
- 'id': Parameter(str, "Key identifier", primary_key=True),
- #'key_type': Parameter(str, "Key type"),
- 'public_key': Parameter(str, "Key string", max = 4096),
- 'name': Parameter(str, "Key name",)
+ 'key_id': Parameter(str, "Key identifier", primary_key=True),
+ 'key_type': Parameter(str, "Key type"),
+ 'key': Parameter(str, "Key string", max = 4096), # backwards compatibility
+ 'name': Parameter(str, "Key name"),
+ 'uuid': Parameter(str, "Key name", ro=True)
}
- def sync(self, insert = False, validate = True):
- NovaObject.sync(self, insert, validate)
- if insert == True or 'id' not in self:
- self.object = self.api.client_shell.nova.keypairs.create(self.id,
- self.key)
- else:
- self.object = self.api.client_shell.nova.keypairs.update(self.id, dict(self))
+ def sync(self, commit = True, validate = True):
+ # sync the nova record and the plc record
+ assert 'key' in self
+ assert 'name' in self
+ AlchemyObj.sync(self, commit=commit, validate=validate)
+ nova_key = {
+ 'public_key': self['key'],
+ 'name': self['name']
+ }
+ self.object = self.api.client_shell.nova.keypairs.create(**nova_key)
+ self['uuid'] = self.object.uuid
+ AlchemyObj.insert(self, dict(self))
def delete(self):
- assert 'id' in self
+ assert 'key_id' in self
+ assert 'name' in self
self.api.client_shell.nova.keypairs.delete(self.id)
def validate_public_key(self, key):
assert 'key_id' in self
assert 'key' in self
- # Get all matching keys
- rows = self.api.db.selectall("SELECT key_id FROM keys WHERE key = %(key)s",
- self)
- key_ids = [row['key_id'] for row in rows]
- assert key_ids
- assert self['key_id'] in key_ids
-
- # Keep the keys in the table
- self.api.db.do("UPDATE keys SET is_blacklisted = True" \
- " WHERE key_id IN (%s)" % ", ".join(map(str, key_ids)))
-
- # But disassociate them from all join tables
- for table in self.join_tables:
- self.api.db.do("DELETE FROM %s WHERE key_id IN (%s)" % \
- (table, ", ".join(map(str, key_ids))))
-
- if commit:
- self.api.db.commit()
+ pass
-class Keys(NovaTable):
+class Keys(list):
"""
Representation of row(s) from the keys table in the
database.
from PLC.Methods.AddNodeTag import AddNodeTag
from PLC.Methods.UpdateNodeTag import UpdateNodeTag
-can_update = ['hostname', 'node_type', 'boot_state', 'model', 'version']
+can_update = lambda (field, value): field in \
+ ['hostname', 'node_type', 'boot_state', 'model', 'version']
class AddNode(Method):
"""
roles = ['admin', 'pi', 'tech']
- accepted_fields = Row.accepted_fields(can_update,Node.fields)
- accepted_fields.update(Node.tags)
+ accepted_fields = dict(filter(can_update, Node.fields.items()))
accepts = [
Auth(),
def call(self, auth, site_id_or_login_base, node_fields):
- [native,tags,rejected]=Row.split_fields(node_fields,[Node.fields,Node.tags])
-
- # type checking
- native = Row.check_fields(native, self.accepted_fields)
- if rejected:
- raise PLCInvalidArgument, "Cannot add Node with column(s) %r"%rejected
-
# Get site information
sites = Sites(self.api, [site_id_or_login_base])
if not sites:
else:
assert self.caller['person_id'] in site['person_ids']
- node = Node(self.api, native)
+ node_fields = dict(filter(can_update, node_fields.items()))
+ node = Node(self.api, node_fields)
node['site_id'] = site['site_id']
node.sync()
# since hostname was specified lets add the 'hrn' node tag
root_auth = self.api.config.PLC_HRN_ROOT
login_base = site['login_base']
+ tags={}
tags['hrn'] = hostname_to_hrn(root_auth, login_base, node['hostname'])
for (tagname,value) in tags.iteritems():
node_tag['value'] = value
node_tag.sync()
- self.event_objects = {'Site': [site['site_id']],
- 'Node': [node['node_id']]}
- self.message = "Node %d=%s created" % (node['node_id'],node['hostname'])
return node['node_id']
from PLC.TagTypes import TagType, TagTypes
from PLC.NodeTags import NodeTag, NodeTags
-can_update = lambda (field, value): field in NodeGroup.fields.keys() and field != NodeGroup.primary_field
+can_update = lambda (field, value): field in NodeGroup.fields.keys() and field not in [NodeGroup.fields['nodegroup_id']]
class AddNodeGroup(Method):
"""
pcu_type_fields = dict(filter(can_update, pcu_type_fields.items()))
pcu_type = PCUType(self.api, pcu_type_fields)
pcu_type.sync()
- self.event_object = {'PCUType': [pcu_type['pcu_type_id']]}
return pcu_type['pcu_type_id']
accepts = [
Auth(),
- Mixed(Role.fields['role_id'],
+ Mixed(Role.fields['id'],
Role.fields['name']),
Mixed(Person.fields['person_id'],
Person.fields['email']),
raise PLCInvalidArgument, "No such account"
person = persons[0]
- if person['peer_id'] is not None:
- raise PLCInvalidArgument, "Not a local account"
-
# Authenticated function
assert self.caller is not None
if role['role_id'] not in person['role_ids']:
person.add_role(role)
- self.event_objects = {'Person': [person['person_id']],
- 'Role': [role['role_id']]}
- self.message = "Role %d granted to person %d" % \
- (role['role_id'], person['person_id'])
-
return 1
from PLC.Method import Method
from PLC.Parameter import Parameter, Mixed
-from PLC.Auth import Auth, BootAuth, SessionAuth
+from PLC.Auth import Auth
from PLC.Nodes import Node, Nodes
from PLC.Messages import Message, Messages
accepts = [
Auth(),
- Mixed(Role.fields['role_id'],
+ Mixed(Role.fields['id'],
Role.fields['name']),
Mixed(TagType.fields['tag_type_id'],
TagType.fields['tagname']),
from PLC.InterfaceTags import InterfaceTag, InterfaceTags
from PLC.NodeTags import NodeTag, NodeTags
-from PLC.Accessors.Accessors_standard import * # import node accessors
+#from PLC.Accessors.Accessors_standard import * # import node accessors
# could not define this in the class..
# create a dict with the allowed actions for each type of node
from PLC.Parameter import *
from PLC.Nodes import Node, Nodes
-from PLC.Accessors.Accessors_standard import * # import node accessors
-
class GetNodeFlavour(Method):
"""
Returns detailed information on a given node's flavour, i.e. its
accepts = [
Auth(),
- Mixed(Person.fields['id'],
+ Mixed(Person.fields['person_id'],
Parameter(str,"email"),
Filter(Person.fields)),
Parameter([str], "List of fields to return", nullok = True)
from PLC.Faults import *
from PLC.Parameter import *
from PLC.Slices import Slice, Slices
-
-from PLC.Accessors.Accessors_standard import * # import slice accessors
-from PLC.Accessors.Accessors_sliverauth import * # import slice accessors
+from PLC.SliceTags import SliceTag, SliceTags
class GetSliceFamily(Method):
"""
### system slices - at least planetflow - still rely on 'vref'
#
def call(self, auth, slice_id_or_name):
+ # default values
+ arch = self.api.config.PLC_FLAVOUR_SLICE_ARCH
+ pldistro = self.api.config.PLC_FLAVOUR_SLICE_PLDISTRO
+ fcdistro = self.api.config.PLC_FLAVOUR_SLICE_FCDISTRO
+ vref = None
+
# Get slice information
slices = Slices(self.api, [slice_id_or_name])
if not slices:
raise PLCInvalidArgument, "No such slice %r"%slice_id_or_name
slice = slices[0]
- slice_id = slice['slice_id']
-
- arch = GetSliceArch (self.api,self.caller).call(auth,slice_id)
- if not arch: arch = self.api.config.PLC_FLAVOUR_SLICE_ARCH
-
- pldistro = GetSlicePldistro (self.api,self.caller).call(auth, slice_id)
- if not pldistro: pldistro = self.api.config.PLC_FLAVOUR_SLICE_PLDISTRO
-
- fcdistro = GetSliceFcdistro (self.api,self.caller).call(auth, slice_id)
- if not fcdistro: fcdistro = self.api.config.PLC_FLAVOUR_SLICE_FCDISTRO
-
- # the vref tag, if set, wins over pldistro
- vref = GetSliceVref (self.api,self.caller).call(auth,slice_id)
+ slice_tags = SliceTags(self.api, slice['slice_tag_ids'])
+
+ for slice_tag in slice_tags:
+ if slice_tag['tagname'] == 'arch':
+ arch = slice_tag['value']
+ elif slice_tag['tagname'] == 'pldistro':
+ pldistro = slice_tag['value']
+ elif slice_tag['tagname'] == 'fcdistro':
+ fcdistro = slice_tag['value']
+ elif slice_tag['tagname'] == 'vref':
+ vref = slice_tag['value']
# omf-control'ed slivers need the omf vserver reference image
# we used to issue SetSliceVref (self.api) (auth,slice_id,'omf')
from PLC.Methods.GetSliceFamily import GetSliceFamily
from PLC.PersonTags import PersonTag,PersonTags
-from PLC.Accessors.Accessors_standard import *
-
# XXX used to check if slice expiration time is sane
MAXINT = 2L**31-1
returns = Parameter(int, '1 if successful')
def call(self, auth, key_id, key_fields):
- key_fields = dict(filter(can_update, key_fields.items()))
-
- # Get key information
- keys = Keys(self.api, [key_id])
- if not keys:
- raise PLCInvalidArgument, "No such key"
- key = keys[0]
-
- if key['peer_id'] is not None:
- raise PLCInvalidArgument, "Not a local key"
-
- if 'admin' not in self.caller['roles']:
- if key['key_id'] not in self.caller['key_ids']:
- raise PLCPermissionDenied, "Key must be associated with one of your accounts"
-
- key.update(key_fields)
- key.sync()
-
- # Logging variables
- self.event_objects = {'Key': [key['key_id']]}
- self.message = 'key %d updated: %s' % \
- (key['key_id'], ", ".join(key_fields.keys()))
return 1
roles = ['admin', 'pi', 'tech']
accepted_fields = Row.accepted_fields(can_update,Node.fields)
- # xxx check the related_fields feature
- accepted_fields.update(Node.related_fields)
- accepted_fields.update(Node.tags)
accepts = [
Auth(),
def call(self, auth, node_id_or_hostname, node_fields):
- # split provided fields
- [native,related,tags,rejected] = Row.split_fields(node_fields,[Node.fields,Node.related_fields,Node.tags])
-
- # type checking
- native = Row.check_fields (native, self.accepted_fields)
- if rejected:
- raise PLCInvalidArgument, "Cannot update Node column(s) %r"%rejected
-
# Authenticated function
assert self.caller is not None
# Remove admin only fields
if 'admin' not in self.caller['roles']:
for key in admin_only:
- if native.has_key(key):
- del native[key]
+ if node_fields.has_key(key):
+ del node_fields[key]
# Get account information
nodes = Nodes(self.api, [node_id_or_hostname])
raise PLCInvalidArgument, "No such node %r"%node_id_or_hostname
node = nodes[0]
- if node['peer_id'] is not None:
- raise PLCInvalidArgument, "Not a local node %r"%node_id_or_hostname
-
# If we are not an admin, make sure that the caller is a
# member of the site at which the node is located.
if 'admin' not in self.caller['roles']:
if node['site_id'] not in self.caller['site_ids']:
raise PLCPermissionDenied, "Not allowed to delete nodes from specified site"
- # Make requested associations
- for (k,v) in related.iteritems():
- node.associate(auth, k,v)
-
- node.update(native)
- node.update_last_updated(commit=False)
+ node.update(node_fields)
node.sync(commit=True)
# if hostname was modifed make sure to update the hrn
node_tag = node_tags[0]
node_tag['value'] = value
node_tag.sync()
- # Logging variables
- self.event_objects = {'Node': [node['node_id']]}
- if 'hostname' in node:
- self.message = 'Node %s updated'%node['hostname']
- else:
- self.message = 'Node %d updated'%node['node_id']
- self.message += " [%s]." % (", ".join(node_fields.keys()),)
- if 'boot_state' in node_fields.keys():
- self.message += ' boot_state updated to %s' % node_fields['boot_state']
-
return 1
from PLC.Auth import Auth
from PLC.sendmail import sendmail
-related_fields = Person.related_fields.keys()
can_update = lambda (field, value): field in \
['first_name', 'last_name', 'title', 'email',
'password', 'phone', 'url', 'bio', 'accepted_aup',
- 'enabled'] + related_fields
+ 'enabled']
class UpdatePerson(Method):
"""
roles = ['admin', 'pi', 'user', 'tech']
- person_fields = dict(filter(can_update, Person.fields.items() + Person.related_fields.items()))
+ person_fields = dict(filter(can_update, Person.fields.items() ))
accepts = [
Auth(),
accepts = [
Auth(),
- Site.fields['id'],
+ Site.fields['site_id'],
site_fields
]
# If we are not an admin, make sure that the caller is a
# member of the site.
if 'admin' not in self.caller['roles']:
- if site['id'] not in self.caller['tenant_id']:
+ if site['site_id'] not in self.caller['site_id']:
raise PLCPermissionDenied, "Not allowed to modify specified site"
# Remove admin only fields
roles = ['admin', 'pi', 'user']
accepted_fields = Row.accepted_fields(can_update, Slice.fields)
- # xxx check the related_fields feature
- accepted_fields.update(Slice.related_fields)
- accepted_fields.update(Slice.tags)
accepts = [
Auth(),
def call(self, auth, slice_id_or_name, slice_fields):
- # split provided fields
- [native,related,tags,rejected] = Row.split_fields(slice_fields,[Slice.fields,Slice.related_fields,Slice.tags])
-
- # type checking
- native = Row.check_fields (native, self.accepted_fields)
- if rejected:
- raise PLCInvalidArgument, "Cannot update Slice column(s) %r"%rejected
-
slices = Slices(self.api, [slice_id_or_name])
if not slices:
raise PLCInvalidArgument, "No such slice %r"%slice_id_or_name
'pi' not in self.caller['roles']:
raise PLCInvalidArgument, "Only admins and PIs may update max_nodes"
- # Make requested associations
- for (k,v) in related.iteritems():
- slice.associate(auth,k,v)
-
slice.update(slice_fields)
slice.sync(commit=True)
- for (tagname,value) in tags.iteritems():
- # the tagtype instance is assumed to exist, just check that
- if not TagTypes(self.api,{'tagname':tagname}):
- raise PLCInvalidArgument,"No such TagType %s"%tagname
- slice_tags=SliceTags(self.api,{'tagname':tagname,'slice_id':slice['slice_id']})
- if not slice_tags:
- AddSliceTag(self.api).__call__(auth,slice['slice_id'],tagname,value)
- else:
- UpdateSliceTag(self.api).__call__(auth,slice_tags[0]['slice_tag_id'],value)
-
- self.event_objects = {'Slice': [slice['slice_id']]}
- if 'name' in slice:
- self.message='Slice %s updated'%slice['name']
- else:
- self.message='Slice %d updated'%slice['slice_id']
- if renewing:
- # it appears that slice['expires'] may be either an int, or a formatted string
- try:
- expire_date=time.strftime('%Y-%m-%d:%H:%M',time.localtime(float(slice['expires'])))
- except:
- expire_date=slice['expires']
- self.message += ' renewed until %s'%expire_date
-
return 1
from PLC.Storage.AlchemyObject import AlchemyObj
from PLC.Nodes import Node, Nodes
-class NodeGroup(Row):
+class NodeGroup(AlchemyObj):
"""
Representation of a row in the nodegroups table. To use, optionally
instantiate with a dict of values. Update as you would a
tablename = 'nodegroups'
join_tables = ['conf_file_nodegroup']
fields = {
- 'nodegroup_id': Parameter(int, "Node group identifier", primary_key),
+ 'nodegroup_id': Parameter(int, "Node group identifier", primary_key=True),
'groupname': Parameter(str, "Node group name", max = 50),
'tag_type_id': Parameter (int, "Node tag type id"),
'value' : Parameter(str, "value that the nodegroup definition is based upon"),
'conf_file_ids': Parameter([int], "List of configuration files specific to this node group", joined=True),
'node_ids' : Parameter([int], "List of node_ids that belong to this nodegroup", joined=True),
}
- related_fields = {
- }
def validate_name(self, name):
# Make sure name is not blank
"""
Update col_name field with current time
"""
-
assert 'node_id' in self
- assert self.table_name
-
- self.api.db.do("UPDATE %s SET %s = CURRENT_TIMESTAMP " % (self.table_name, col_name) + \
- " where node_id = %d" % (self['node_id']) )
- self.sync(commit)
-
+ self[col_name] = datetime.now()
+ fields = {
+ 'node_id': self['node_id'],
+ col_name: datetime.now()
+ }
+ Node(self.api, fields).sync()
+
def update_last_boot(self, commit = True):
self.update_timestamp('last_boot', commit)
def update_last_download(self, commit = True):
from PLC.Faults import *
from PLC.Parameter import Parameter
-from PLC.Storage.AlchemyObj import AlchemyObj
+from PLC.Storage.AlchemyObject import AlchemyObj
class PCUType(AlchemyObj):
"""
tablename = 'person_tag'
fields = {
'person_tag_id': Parameter(int, "Person setting identifier", primary_key=True),
- 'person_id': Person.fields['id'],
+ 'person_id': Person.fields['person_id'],
'email': Person.fields['email'],
'tag_type_id': TagType.fields['tag_type_id'],
'tagname': TagType.fields['tagname'],
if role is not None:
self.auth['Role'] = role
- self.load_methods()
-
- def load_methods(self):
-
for method in PLC.API.PLCAPI.all_methods:
api_function = self.api.callable(method)
check_future = not ('is_deleted' in self and self['is_deleted'])
return Timestamp.sql_validate( expires, check_future = check_future)
- #add_person = Row.add_object(Person, 'slice_person')
- #remove_person = Row.remove_object(Person, 'slice_person')
+ def add_person(self, person_filter, role_name=None):
+ assert 'slice_id' in self
+ assert 'tenant_id' in self
+ if not role_name:
+ role_name = 'user'
+ roles = Roles(self.api, role_name)
+ if not roles:
+ raise PLCInvalidArgument, "No such role %s" % role_name
+ role = roles[0]
+ tenant = self.api.client_shell.keystone.tenants.find(id=self['tenant_id'])
+ persons = Persons(self.api, person_filter)
+ for person in persons:
+ keystone_user = self.api.client_shell.keystone.users.find(id=person['keystone_id'])
+ tenant.add_user(keystone_user, role.object)
+ slice_person = SlicePerson(self.api, {'slice_id': self['slice_id'],
+ 'person_id': person['person_id']})
+ slice_person.sync()
+
+ def remove_person(self, person_filter, role=None):
+ assert 'slice_id' in self
+ assert 'tenant_id' in self
+ if not role_name:
+ role_name = 'user'
+ roles = Roles(self.api, role_name)
+ if not roles:
+ raise PLCInvalidArgument, "No such role %s" % role_name
+ role = roles[0]
+ tenant = self.api.client_shell.keystone.tenants.find(id=self['tenant_id'])
+ persons = Persons(self.api, person_filter)
+ for person in persons:
+ keystone_user = self.api.client_shell.keystone.users.find(id=person['keystone_id'])
+ tenant.remove_user(keystone_user, role.object)
+ slice_person = SlicePerson(self.api, {'slice_id': self['slice_id'],
+ 'person_id': person['person_id']})
+ slice_person.delete()
+
- #add_node = Row.add_object(Node, 'slice_node')
- #remove_node = Row.remove_object(Node, 'slice_node')
+ def add_node(self, node_filter):
+ assert 'slice_id' in self
+ nodes = Nodes(self.api, node_filter)
+ for node in nodes:
+ slice_node = SliceNode(self.api, {'slice_id': self['slice_id'],
+ 'node_id': node['node_id']})
+ slice_node.sync()
+
+ def remove_node(self, node_filter):
+ assert 'slice_id' in self
+ nodes = Nodes(self.api, node_filter)
+ for node in nodes:
+ slice_node = SliceNode(self.api, {'slice_id': self['slice_id'],
+ 'node_id': node['node_id']})
+ slice_node.delete()
#add_to_node_whitelist = Row.add_object(Node, 'node_slice_whitelist')
#delete_from_node_whitelist = Row.remove_object(Node, 'node_slice_whitelist')
Requires: python-pycurl
Requires: httpd
Requires: python-gevent
+Requires: python-lxml
# mod_wsgi will replace mod_python when we are ready
Requires: mod_wsgi
Requires: mod_ssl