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
21 from PLC.Leases import Lease, Leases
22 from PLC.Timestamp import Duration
23 from PLC.Methods.GetSliceFamily import GetSliceFamily
25 from PLC.Accessors.Accessors_standard import *
27 # XXX used to check if slice expiration time is sane
30 def get_slivers(api, auth, slice_filter, node = None):
31 # Get slice information
32 slices = Slices(api, slice_filter, ['slice_id', 'name', 'instantiation', 'expires', 'person_ids', 'slice_tag_ids'])
34 # Build up list of users and slice attributes
38 person_ids.update(slice['person_ids'])
39 slice_tag_ids.update(slice['slice_tag_ids'])
41 # Get user information
42 all_persons = Persons(api, {'person_id':person_ids,'enabled':True}, ['person_id', 'enabled', 'key_ids']).dict()
44 # Build up list of keys
46 for person in all_persons.values():
47 key_ids.update(person['key_ids'])
49 # Get user account keys
50 all_keys = Keys(api, key_ids, ['key_id', 'key', 'key_type']).dict()
52 # Get slice attributes
53 all_slice_tags = SliceTags(api, slice_tag_ids).dict()
58 for person_id in slice['person_ids']:
59 if person_id in all_persons:
60 person = all_persons[person_id]
61 if not person['enabled']:
63 for key_id in person['key_ids']:
64 if key_id in all_keys:
65 key = all_keys[key_id]
66 keys += [{'key_type': key['key_type'],
71 # All (per-node and global) attributes for this slice
73 for slice_tag_id in slice['slice_tag_ids']:
74 if slice_tag_id in all_slice_tags:
75 slice_tags.append(all_slice_tags[slice_tag_id])
77 # Per-node sliver attributes take precedence over global
78 # slice attributes, so set them first.
79 # Then comes nodegroup slice attributes
80 # Followed by global slice attributes
81 sliver_attributes = []
84 for sliver_attribute in [ a for a in slice_tags if a['node_id'] == node['node_id'] ]:
85 sliver_attributes.append(sliver_attribute['tagname'])
86 attributes.append({'tagname': sliver_attribute['tagname'],
87 'value': sliver_attribute['value']})
89 # set nodegroup slice attributes
90 for slice_tag in [ a for a in slice_tags if a['nodegroup_id'] in node['nodegroup_ids'] ]:
91 # Do not set any nodegroup slice attributes for
92 # which there is at least one sliver attribute
94 if slice_tag not in slice_tags:
95 attributes.append({'tagname': slice_tag['tagname'],
96 'value': slice_tag['value']})
98 for slice_tag in [ a for a in slice_tags if a['node_id'] is None ]:
99 # Do not set any global slice attributes for
100 # which there is at least one sliver attribute
102 if slice_tag['tagname'] not in sliver_attributes:
103 attributes.append({'tagname': slice_tag['tagname'],
104 'value': slice_tag['value']})
106 # XXX Sanity check; though technically this should be a system invariant
107 # checked with an assertion
108 if slice['expires'] > MAXINT: slice['expires']= MAXINT
110 # expose the slice vref as computed by GetSliceFamily
111 family = GetSliceFamily (api).call(auth, slice['slice_id'])
114 'name': slice['name'],
115 'slice_id': slice['slice_id'],
116 'instantiation': slice['instantiation'],
117 'expires': slice['expires'],
119 'attributes': attributes,
120 'GetSliceFamily': family,
125 class GetSlivers(Method):
127 Returns a struct containing information about the specified node
128 (or calling node, if called by a node and node_id_or_hostname is
129 not specified), including the current set of slivers bound to the
132 All of the information returned by this call can be gathered from
133 other calls, e.g. GetNodes, GetInterfaces, GetSlices, etc. This
134 function exists almost solely for the benefit of Node Manager.
137 roles = ['admin', 'node']
141 Mixed(Node.fields['node_id'],
142 Node.fields['hostname']),
146 'timestamp': Parameter(int, "Timestamp of this call, in seconds since UNIX epoch"),
147 'node_id': Node.fields['node_id'],
148 'hostname': Node.fields['hostname'],
149 'interfaces': [Interface.fields],
150 'groups': [NodeGroup.fields['groupname']],
151 'conf_files': [ConfFile.fields],
152 'initscripts': [InitScript.fields],
154 'name': Parameter(str, "unix style account name", max = 254),
156 'key_type': Key.fields['key_type'],
157 'key': Key.fields['key']
161 'name': Slice.fields['name'],
162 'slice_id': Slice.fields['slice_id'],
163 'instantiation': Slice.fields['instantiation'],
164 'expires': Slice.fields['expires'],
166 'key_type': Key.fields['key_type'],
167 'key': Key.fields['key']
170 'tagname': SliceTag.fields['tagname'],
171 'value': SliceTag.fields['value']
174 # how to reach the xmpp server
175 'xmpp': {'server':Parameter(str,"hostname for the XMPP server"),
176 'user':Parameter(str,"username for the XMPP server"),
177 'password':Parameter(str,"username for the XMPP server"),
179 # we consider three policies (reservation-policy)
180 # none : the traditional way to use a node
181 # lease_or_idle : 0 or 1 slice runs at a given time
182 # lease_or_shared : 1 slice is running during a lease, otherwise all the slices come back
183 'reservation_policy': Parameter(str,"one among none, lease_or_idle, lease_or_shared"),
184 'leases': [ { 'slice_id' : Lease.fields['slice_id'],
185 't_from' : Lease.fields['t_from'],
186 't_until' : Lease.fields['t_until'],
190 def call(self, auth, node_id_or_hostname = None):
191 timestamp = int(time.time())
194 if node_id_or_hostname is None:
195 if isinstance(self.caller, Node):
198 raise PLCInvalidArgument, "'node_id_or_hostname' not specified"
200 nodes = Nodes(self.api, [node_id_or_hostname])
202 raise PLCInvalidArgument, "No such node"
205 if node['peer_id'] is not None:
206 raise PLCInvalidArgument, "Not a local node"
208 # Get interface information
209 interfaces = Interfaces(self.api, node['interface_ids'])
211 # Get node group information
212 nodegroups = NodeGroups(self.api, node['nodegroup_ids']).dict('groupname')
213 groups = nodegroups.keys()
215 # Get all (enabled) configuration files
216 all_conf_files = ConfFiles(self.api, {'enabled': True}).dict()
219 # Global configuration files are the default. If multiple
220 # entries for the same global configuration file exist, it is
221 # undefined which one takes precedence.
222 for conf_file in all_conf_files.values():
223 if not conf_file['node_ids'] and not conf_file['nodegroup_ids']:
224 conf_files[conf_file['dest']] = conf_file
226 # Node group configuration files take precedence over global
227 # ones. If a node belongs to multiple node groups for which
228 # the same configuration file is defined, it is undefined
229 # which one takes precedence.
230 for nodegroup in nodegroups.values():
231 for conf_file_id in nodegroup['conf_file_ids']:
232 if conf_file_id in all_conf_files:
233 conf_file = all_conf_files[conf_file_id]
234 conf_files[conf_file['dest']] = conf_file
236 # Node configuration files take precedence over node group
237 # configuration files.
238 for conf_file_id in node['conf_file_ids']:
239 if conf_file_id in all_conf_files:
240 conf_file = all_conf_files[conf_file_id]
241 conf_files[conf_file['dest']] = conf_file
243 # Get all (enabled) initscripts
244 initscripts = InitScripts(self.api, {'enabled': True})
247 system_slice_tags = SliceTags(self.api, {'tagname': 'system', 'value': '1'}).dict('slice_id')
248 system_slice_ids = system_slice_tags.keys()
250 # Get nm-controller slices
251 # xxx Thierry: should these really be exposed regardless of their mapping to nodes ?
252 controller_and_delegated_slices = Slices(self.api, {'instantiation': ['nm-controller', 'delegated']}, ['slice_id']).dict('slice_id')
253 controller_and_delegated_slice_ids = controller_and_delegated_slices.keys()
254 slice_ids = system_slice_ids + controller_and_delegated_slice_ids + node['slice_ids']
256 slivers = get_slivers(self.api, auth, slice_ids, node)
258 # get the special accounts and keys needed for the node
262 if False and 'site_id' not in node:
263 nodes = Nodes(self.api, node['node_id'])
266 # used in conjunction with reduce to flatten lists, like in
267 # reduce ( reduce_flatten_list, [ [1] , [2,3] ], []) => [ 1,2,3 ]
268 def reduce_flatten_list (x,y): return x+y
270 # power users are pis and techs
271 def get_site_power_user_keys(api,site_id_or_name):
272 site = Sites (api,site_id_or_name,['person_ids'])[0]
273 key_ids = reduce (reduce_flatten_list,
274 [ p['key_ids'] for p in \
275 Persons(api,{ 'person_id':site['person_ids'],
276 'enabled':True, '|role_ids' : [20, 40] },
279 return [ key['key'] for key in Keys (api, key_ids) if key['key_type']=='ssh']
281 # all admins regardless of their site
282 def get_all_admin_keys(api):
283 key_ids = reduce (reduce_flatten_list,
284 [ p['key_ids'] for p in \
285 Persons(api, {'peer_id':None, 'enabled':True, '|role_ids':[10] },
288 return [ key['key'] for key in Keys (api, key_ids) if key['key_type']=='ssh']
290 # 'site_admin' account setup
291 personsitekeys=get_site_power_user_keys(self.api,node['site_id'])
292 accounts.append({'name':'site_admin','keys':personsitekeys})
294 # 'root' account setup on nodes from all 'admin' users
295 personsitekeys=get_all_admin_keys(self.api)
296 accounts.append({'name':'root','keys':personsitekeys})
298 hrn = GetNodeHrn(self.api).call(auth,node['node_id'])
300 # XMPP config for omf federation
302 if not self.api.config.PLC_OMF_ENABLED:
303 raise Exception,"OMF disabled"
304 xmpp={'server':self.api.config.PLC_OMF_XMPP_SERVER,
305 'user':self.api.config.PLC_OMF_XMPP_USER,
306 'password':self.api.config.PLC_OMF_XMPP_PASSWORD,
309 xmpp={'server':None,'user':None,'password':None}
311 node.update_last_contact()
313 # expose leases & reservation policy
314 # in a first implementation we only support none and lease_or_idle
315 lease_exposed_fields = [ 'slice_id', 't_from', 't_until', ]
317 if node['node_type'] != 'reservable':
318 reservation_policy='none'
320 reservation_policy='lease_or_idle'
321 # expose the leases for the next 24 hours
322 leases = [ dict ( [ (k,l[k]) for k in lease_exposed_fields ] )
323 for l in Leases (self.api, {'node_id':node['node_id'],
324 'clip': (timestamp, timestamp+24*Duration.HOUR),
327 granularity=self.api.config.PLC_RESERVATION_GRANULARITY
330 'timestamp': timestamp,
331 'node_id': node['node_id'],
332 'hostname': node['hostname'],
333 'interfaces': interfaces,
335 'conf_files': conf_files.values(),
336 'initscripts': initscripts,
338 'accounts': accounts,
341 'reservation_policy': reservation_policy,
343 'lease_granularity': granularity,