5 from PLC.Faults import *
6 from PLC.Method import Method
7 from PLC.Parameter import Parameter, Mixed
8 from PLC.Filter import Filter
9 from PLC.Auth import Auth
10 from PLC.Nodes import Node, Nodes
11 from PLC.Interfaces import Interface, Interfaces
12 from PLC.NodeGroups import NodeGroup, NodeGroups
13 from PLC.ConfFiles import ConfFile, ConfFiles
14 from PLC.Slices import Slice, Slices
15 from PLC.Persons import Person, Persons
16 from PLC.Sites import Sites
17 from PLC.Roles import Roles
18 from PLC.Keys import Key, Keys
19 from PLC.SliceTags import SliceTag, SliceTags
20 from PLC.InitScripts import InitScript, InitScripts
22 # XXX used to check if slice expiration time is sane
25 def get_slivers(api, slice_filter, node = None):
26 # Get slice information
27 slices = Slices(api, slice_filter, ['slice_id', 'name', 'instantiation', 'expires', 'person_ids', 'slice_tag_ids'])
29 # Build up list of users and slice attributes
33 person_ids.update(slice['person_ids'])
34 slice_tag_ids.update(slice['slice_tag_ids'])
36 # Get user information
37 all_persons = Persons(api, {'person_id':person_ids,'enabled':True}, ['person_id', 'enabled', 'key_ids']).dict()
39 # Build up list of keys
41 for person in all_persons.values():
42 key_ids.update(person['key_ids'])
44 # Get user account keys
45 all_keys = Keys(api, key_ids, ['key_id', 'key', 'key_type']).dict()
47 # Get slice attributes
48 all_slice_tags = SliceTags(api, slice_tag_ids).dict()
53 for person_id in slice['person_ids']:
54 if person_id in all_persons:
55 person = all_persons[person_id]
56 if not person['enabled']:
58 for key_id in person['key_ids']:
59 if key_id in all_keys:
60 key = all_keys[key_id]
61 keys += [{'key_type': key['key_type'],
66 # All (per-node and global) attributes for this slice
68 for slice_tag_id in slice['slice_tag_ids']:
69 if slice_tag_id in all_slice_tags:
70 slice_tags.append(all_slice_tags[slice_tag_id])
72 # Per-node sliver attributes take precedence over global
73 # slice attributes, so set them first.
74 # Then comes nodegroup slice attributes
75 # Followed by global slice attributes
76 sliver_attributes = []
79 for sliver_attribute in filter(lambda a: a['node_id'] == node['node_id'], slice_tags):
80 sliver_attributes.append(sliver_attribute['tagname'])
81 attributes.append({'tagname': sliver_attribute['tagname'],
82 'value': sliver_attribute['value']})
84 # set nodegroup slice attributes
85 for slice_tag in filter(lambda a: a['nodegroup_id'] in node['nodegroup_ids'], slice_tags):
86 # Do not set any nodegroup slice attributes for
87 # which there is at least one sliver attribute
89 if slice_tag not in slice_tags:
90 attributes.append({'tagname': slice_tag['tagname'],
91 'value': slice_tag['value']})
93 for slice_tag in filter(lambda a: a['node_id'] is None, slice_tags):
94 # Do not set any global slice attributes for
95 # which there is at least one sliver attribute
97 if slice_tag['tagname'] not in sliver_attributes:
98 attributes.append({'tagname': slice_tag['tagname'],
99 'value': slice_tag['value']})
101 # XXX Sanity check; though technically this should be a system invariant
102 # checked with an assertion
103 if slice['expires'] > MAXINT: slice['expires']= MAXINT
106 'name': slice['name'],
107 'slice_id': slice['slice_id'],
108 'instantiation': slice['instantiation'],
109 'expires': slice['expires'],
111 'attributes': attributes
116 class v43GetSlivers(Method):
118 Returns a struct containing information about the specified node
119 (or calling node, if called by a node and node_id_or_hostname is
120 not specified), including the current set of slivers bound to the
123 All of the information returned by this call can be gathered from
124 other calls, e.g. GetNodes, GetInterfaces, GetSlices, etc. This
125 function exists almost solely for the benefit of Node Manager.
128 roles = ['admin', 'node']
132 Mixed(Node.fields['node_id'],
133 Node.fields['hostname']),
137 'timestamp': Parameter(int, "Timestamp of this call, in seconds since UNIX epoch"),
138 'node_id': Node.fields['node_id'],
139 'hostname': Node.fields['hostname'],
140 'networks': [Interface.fields],
141 'groups': [NodeGroup.fields['groupname']],
142 'conf_files': [ConfFile.fields],
143 'initscripts': [InitScript.fields],
145 'name': Parameter(str, "unix style account name", max = 254),
147 'key_type': Key.fields['key_type'],
148 'key': Key.fields['key']
152 'name': Slice.fields['name'],
153 'slice_id': Slice.fields['slice_id'],
154 'instantiation': Slice.fields['instantiation'],
155 'expires': Slice.fields['expires'],
157 'key_type': Key.fields['key_type'],
158 'key': Key.fields['key']
161 'tagname': SliceTag.fields['tagname'],
162 'value': SliceTag.fields['value']
167 def call(self, auth, node_id_or_hostname = None):
168 timestamp = int(time.time())
171 if node_id_or_hostname is None:
172 if isinstance(self.caller, Node):
175 raise PLCInvalidArgument, "'node_id_or_hostname' not specified"
177 nodes = Nodes(self.api, [node_id_or_hostname])
179 raise PLCInvalidArgument, "No such node"
182 if node['peer_id'] is not None:
183 raise PLCInvalidArgument, "Not a local node"
185 # Get interface information
186 networks = Interfaces(self.api, node['interface_ids'])
188 # Get node group information
189 nodegroups = NodeGroups(self.api, node['nodegroup_ids']).dict('groupname')
190 groups = nodegroups.keys()
192 # Get all (enabled) configuration files
193 all_conf_files = ConfFiles(self.api, {'enabled': True}).dict()
196 # Global configuration files are the default. If multiple
197 # entries for the same global configuration file exist, it is
198 # undefined which one takes precedence.
199 for conf_file in all_conf_files.values():
200 if not conf_file['node_ids'] and not conf_file['nodegroup_ids']:
201 conf_files[conf_file['dest']] = conf_file
203 # Node group configuration files take precedence over global
204 # ones. If a node belongs to multiple node groups for which
205 # the same configuration file is defined, it is undefined
206 # which one takes precedence.
207 for nodegroup in nodegroups.values():
208 for conf_file_id in nodegroup['conf_file_ids']:
209 if conf_file_id in all_conf_files:
210 conf_file = all_conf_files[conf_file_id]
211 conf_files[conf_file['dest']] = conf_file
213 # Node configuration files take precedence over node group
214 # configuration files.
215 for conf_file_id in node['conf_file_ids']:
216 if conf_file_id in all_conf_files:
217 conf_file = all_conf_files[conf_file_id]
218 conf_files[conf_file['dest']] = conf_file
220 # Get all (enabled) initscripts
221 initscripts = InitScripts(self.api, {'enabled': True})
224 system_slice_tags = SliceTags(self.api, {'tagname': 'system', 'value': '1'}).dict('slice_id')
225 system_slice_ids = system_slice_tags.keys()
227 # Get nm-controller slices
228 controller_and_delegated_slices = Slices(self.api, {'instantiation': ['nm-controller', 'delegated']}, ['slice_id']).dict('slice_id')
229 controller_and_delegated_slice_ids = controller_and_delegated_slices.keys()
230 slice_ids = system_slice_ids + controller_and_delegated_slice_ids + node['slice_ids']
232 slivers = get_slivers(self.api, slice_ids, node)
234 # get the special accounts and keys needed for the node
238 if False and 'site_id' not in node:
239 nodes = Nodes(self.api, node['node_id'])
242 # used in conjunction with reduce to flatten lists, like in
243 # reduce ( reduce_flatten_list, [ [1] , [2,3] ], []) => [ 1,2,3 ]
244 def reduce_flatten_list (x,y): return x+y
246 def get_site_roles_keys(site_id_or_name,roles):
247 site = Sites (self.api,site_id_or_name,['person_ids'])[0]
248 persons = Persons(self.api,{'person_id':site['person_ids'], 'enabled':True},
249 ['roles','key_ids','enabled'] )
252 key_ids.extend(reduce (reduce_flatten_list, [ p['key_ids'] for p in persons if role in p['roles'] ], []))
253 return [ key['key'] for key in Keys (self.api, key_ids) if key['key_type']=='ssh']
255 def get_all_admin_keys():
256 # get all admins key_ids and flatten them into a list of key_ids
257 key_ids = reduce (reduce_flatten_list,
258 [ p['key_ids'] for p in \
259 Persons(self.api,{'peer_id':None,'enabled':True}, \
260 ['roles','key_ids','enabled']) \
261 if 'admin' in p['roles'] ],
262 # starting point for reduce in case there's no admin - I know..
264 # fetch the corresponding keys, and extract the 'key' part into a list
265 # this does not return duplicates
266 return [ key['key'] for key in Keys (self.api, key_ids) if key['key_type']=='ssh']
268 # 'site_admin' account setup
269 personsitekeys=get_site_roles_keys(node['site_id'],['pi','tech'])
270 accounts.append({'name':'site_admin','keys':personsitekeys})
272 # 'root' account setup on nodes from all 'admin' users
273 personsitekeys=get_all_admin_keys()
274 accounts.append({'name':'root','keys':personsitekeys})
276 node.update_last_contact()
279 'timestamp': timestamp,
280 'node_id': node['node_id'],
281 'hostname': node['hostname'],
282 'networks': networks,
284 'conf_files': conf_files.values(),
285 'initscripts': initscripts,
290 class v42GetSlivers(v43GetSlivers):
292 Legacy wrapper for v43GetSlivers.
295 def call(self, auth, node_id_or_hostname = None):
296 result = v43GetSlivers.call(self,auth,node_id_or_hostname)
297 networks = result['networks']
299 for i in range(0,len(networks)):
300 network = networks[i]
301 if network.has_key("interface_id"):
302 network['nodenetwork_id']=network['interface_id']
303 if network.has_key("interface_tag_ids"):
304 network['nodenetwork_setting_ids']=network['interface_tag_ids']
307 result['networks']=networks
310 class GetSlivers(v42GetSlivers):
312 Returns a struct containing information about the specified node
313 (or calling node, if called by a node and node_id_or_hostname is
314 not specified), including the current set of slivers bound to the
317 All of the information returned by this call can be gathered from
318 other calls, e.g. GetNodes, GetInterfaces, GetSlices, etc. This
319 function exists almost solely for the benefit of Node Manager.