4 from types import StringTypes
6 from PLC.Faults import *
7 from PLC.Parameter import Parameter
8 from PLC.Filter import Filter
9 from PLC.Debug import profile
10 from PLC.Table import Row, Table
11 from PLC.KeyTypes import KeyType, KeyTypes
12 from PLC.Storage.AlchemyObject import AlchemyObj
14 class Key(AlchemyObj):
16 Representation of a row in the keys table. To use, instantiate with a
17 dict of values. Update as you would a dict. Commit to the database
23 'key_id': Parameter(str, "Key identifier", primary_key=True),
24 'key_type': Parameter(str, "Key type"),
25 'key': Parameter(str, "Key string", max = 4096), # backwards compatibility
26 'person_id': Parameter(int, "User to which this key belongs", nullok = True),
27 'is_blacklisted': Parameter(bool, "Is the key blacklisted", default=False),
28 'name': Parameter(str, "Key name"),
29 'peer_id': Parameter(int, "Peer to which this key belongs", nullok = True),
30 'peer_key_id': Parameter(int, "Foreign key identifier at peer", nullok = True),
33 def sync(self, commit = True, validate = True):
34 from PLC.Persons import Person
35 # sync the nova record and the plc record
37 AlchemyObj.sync(self, commit=commit, validate=validate)
38 persons = Person().select(filter={'person_id': self['person_id']})
40 raise PLCInvalidArgument, "Invalid person id"
42 if 'key_id' not in self:
43 rand = ''.join(random.choice(string.ascii_letters) for x in range(5))
44 self['name'] = person.email.split('@')[0] + rand
46 'public_key': self['key'],
49 self.object = self.api.client_shell.nova.keypairs.create(**nova_key)
50 AlchemyObj.insert(self, dict(self))
51 key = Key().select(filter={'name': self['name']})[0]
52 self['key_id'] = key.key_id
54 # nova has no way to update existing keys. Just remove the old
55 # key and add a new one
57 'public_key': self['key'],
60 self.api.client_shell.nova.keypairs.delete(self['name'])
61 self.object = self.api.client_shell.nova.keypairs.create(**nova_key)
62 AlchemyObj.update(self, {'key_id': self['key_id']}, dict(self))
65 assert 'key_id' in self
67 self.api.client_shell.nova.keypairs.delete(self['name'])
68 AlchemyObj.delete(self, filter={'key_id': self['key_id']})
71 def validate_key_type(self, key_type):
72 exists = KeyTypes(self.api, key_type)
74 raise PLCInvalidArgument, "Invalid key type"
77 def validate_key(self, key):
78 # Key must not be blacklisted
79 keys = Keys(self.api, {'key': key, 'is_blacklisted': True})
81 raise PLCInvalidArgument, "Key is blacklisted and cannot be used"
87 AlchemyObj.validate(self)
92 if self['key_type'] == 'ssh':
93 # Accept only SSH version 2 keys without options. From
96 # Each protocol version 2 public key consists of: options,
97 # keytype, base64 encoded key, comment. The options field
98 # is optional...The comment field is not used for anything
99 # (but may be convenient for the user to identify the
100 # key). For protocol version 2 the keytype is ``ssh-dss''
103 good_ssh_key = r'^.*(?:ssh-dss|ssh-rsa)[ ]+[A-Za-z0-9+/=]+(?: .*)?$'
104 if not re.match(good_ssh_key, key, re.IGNORECASE):
105 raise PLCInvalidArgument, "Invalid SSH version 2 public key"
107 def blacklist(self, commit = True):
109 Permanently blacklist key (and all other identical keys),
110 preventing it from ever being added again. Because this could
111 affect multiple keys associated with multiple accounts, it
112 should be admin only.
115 assert 'key_id' in self
118 AlchemyObj.updatedb(self, filter={'key': self['key']}, values={'is_blacklisted': True})
123 Representation of row(s) from the keys table in the
127 def __init__(self, api, key_filter = None, columns = None):
131 keys = Key().select()
132 elif isinstance(key_filter, dict):
133 keys = Key().select(filter=key_filter)
134 elif isinstance(key_filter, StringTypes):
135 keys = Key().select(filter={'name': key_filter})
136 elif isinstance(key_filter, int):
137 keys = Key().select(filter={'key_id': key_filter})
138 elif isinstance(key_filter, (list, tuple, set)):
139 ints = filter(lambda x: isinstance(x, (int, long)), key_filter)
140 strs = filter(lambda x: isinstance(x, StringTypes), key_filter)
141 key_filter = {'key_id': ints, 'name': strs}
142 keys = Key().select(filter=key_filter)
144 raise PLCInvalidArgument, "Wrong key filter %s" % key_filter
147 key = Key(api, object=key, columns=columns)