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 # slice_filter essentially contains the slice_ids for the relevant slices (on the node + system & delegated slices)
31 def get_slivers(api, auth, slice_filter, node = None):
32 # Get slice information
33 slices = Slices(api, slice_filter, ['slice_id', 'name', 'instantiation', 'expires', 'person_ids', 'slice_tag_ids'])
35 # Build up list of users and slice attributes
39 person_ids.update(slice['person_ids'])
40 slice_tag_ids.update(slice['slice_tag_ids'])
42 # Get user information
43 all_persons = Persons(api, {'person_id':person_ids,'enabled':True}, ['person_id', 'enabled', 'key_ids']).dict()
45 # Build up list of keys
47 for person in all_persons.values():
48 key_ids.update(person['key_ids'])
50 # Get user account keys
51 all_keys = Keys(api, key_ids, ['key_id', 'key', 'key_type']).dict()
53 # Get slice attributes
54 all_slice_tags = SliceTags(api, slice_tag_ids).dict()
59 for person_id in slice['person_ids']:
60 if person_id in all_persons:
61 person = all_persons[person_id]
62 if not person['enabled']:
64 for key_id in person['key_ids']:
65 if key_id in all_keys:
66 key = all_keys[key_id]
67 keys += [{'key_type': key['key_type'],
72 # All (per-node and global) attributes for this slice
74 for slice_tag_id in slice['slice_tag_ids']:
75 if slice_tag_id in all_slice_tags:
76 slice_tags.append(all_slice_tags[slice_tag_id])
78 # Per-node sliver attributes take precedence over global
79 # slice attributes, so set them first.
80 # Then comes nodegroup slice attributes
81 # Followed by global slice attributes
82 sliver_attributes = []
85 for sliver_attribute in [ a for a in slice_tags if a['node_id'] == node['node_id'] ]:
86 sliver_attributes.append(sliver_attribute['tagname'])
87 attributes.append({'tagname': sliver_attribute['tagname'],
88 'value': sliver_attribute['value']})
90 # set nodegroup slice attributes
91 for slice_tag in [ a for a in slice_tags if a['nodegroup_id'] in node['nodegroup_ids'] ]:
92 # Do not set any nodegroup slice attributes for
93 # which there is at least one sliver attribute
95 if slice_tag not in slice_tags:
96 attributes.append({'tagname': slice_tag['tagname'],
97 'value': slice_tag['value']})
99 for slice_tag in [ a for a in slice_tags if a['node_id'] is None ]:
100 # Do not set any global slice attributes for
101 # which there is at least one sliver attribute
103 if slice_tag['tagname'] not in sliver_attributes:
104 attributes.append({'tagname': slice_tag['tagname'],
105 'value': slice_tag['value']})
107 # XXX Sanity check; though technically this should be a system invariant
108 # checked with an assertion
109 if slice['expires'] > MAXINT: slice['expires']= MAXINT
111 # expose the slice vref as computed by GetSliceFamily
112 family = GetSliceFamily (api).call(auth, slice['slice_id'])
115 'name': slice['name'],
116 'slice_id': slice['slice_id'],
117 'instantiation': slice['instantiation'],
118 'expires': slice['expires'],
120 'attributes': attributes,
121 'GetSliceFamily': family,
126 class GetSlivers(Method):
128 Returns a struct containing information about the specified node
129 (or calling node, if called by a node and node_id_or_hostname is
130 not specified), including the current set of slivers bound to the
133 All of the information returned by this call can be gathered from
134 other calls, e.g. GetNodes, GetInterfaces, GetSlices, etc. This
135 function exists almost solely for the benefit of Node Manager.
138 roles = ['admin', 'node']
142 Mixed(Node.fields['node_id'],
143 Node.fields['hostname']),
147 'timestamp': Parameter(int, "Timestamp of this call, in seconds since UNIX epoch"),
148 'node_id': Node.fields['node_id'],
149 'hostname': Node.fields['hostname'],
150 'interfaces': [Interface.fields],
151 'groups': [NodeGroup.fields['groupname']],
152 'conf_files': [ConfFile.fields],
153 'initscripts': [InitScript.fields],
155 'name': Parameter(str, "unix style account name", max = 254),
157 'key_type': Key.fields['key_type'],
158 'key': Key.fields['key']
162 'name': Slice.fields['name'],
163 'slice_id': Slice.fields['slice_id'],
164 'instantiation': Slice.fields['instantiation'],
165 'expires': Slice.fields['expires'],
167 'key_type': Key.fields['key_type'],
168 'key': Key.fields['key']
171 'tagname': SliceTag.fields['tagname'],
172 'value': SliceTag.fields['value']
175 # how to reach the xmpp server
176 'xmpp': {'server':Parameter(str,"hostname for the XMPP server"),
177 'user':Parameter(str,"username for the XMPP server"),
178 'password':Parameter(str,"username for the XMPP server"),
180 # we consider three policies (reservation-policy)
181 # none : the traditional way to use a node
182 # lease_or_idle : 0 or 1 slice runs at a given time
183 # lease_or_shared : 1 slice is running during a lease, otherwise all the slices come back
184 'reservation_policy': Parameter(str,"one among none, lease_or_idle, lease_or_shared"),
185 'leases': [ { 'slice_id' : Lease.fields['slice_id'],
186 't_from' : Lease.fields['t_from'],
187 't_until' : Lease.fields['t_until'],
191 def call(self, auth, node_id_or_hostname = None):
192 timestamp = int(time.time())
195 if node_id_or_hostname is None:
196 if isinstance(self.caller, Node):
199 raise PLCInvalidArgument, "'node_id_or_hostname' not specified"
201 nodes = Nodes(self.api, [node_id_or_hostname])
203 raise PLCInvalidArgument, "No such node"
206 if node['peer_id'] is not None:
207 raise PLCInvalidArgument, "Not a local node"
209 # Get interface information
210 interfaces = Interfaces(self.api, node['interface_ids'])
212 # Get node group information
213 nodegroups = NodeGroups(self.api, node['nodegroup_ids']).dict('groupname')
214 groups = nodegroups.keys()
216 # Get all (enabled) configuration files
217 all_conf_files = ConfFiles(self.api, {'enabled': True}).dict()
220 # Global configuration files are the default. If multiple
221 # entries for the same global configuration file exist, it is
222 # undefined which one takes precedence.
223 for conf_file in all_conf_files.values():
224 if not conf_file['node_ids'] and not conf_file['nodegroup_ids']:
225 conf_files[conf_file['dest']] = conf_file
227 # Node group configuration files take precedence over global
228 # ones. If a node belongs to multiple node groups for which
229 # the same configuration file is defined, it is undefined
230 # which one takes precedence.
231 for nodegroup in nodegroups.values():
232 for conf_file_id in nodegroup['conf_file_ids']:
233 if conf_file_id in all_conf_files:
234 conf_file = all_conf_files[conf_file_id]
235 conf_files[conf_file['dest']] = conf_file
237 # Node configuration files take precedence over node group
238 # configuration files.
239 for conf_file_id in node['conf_file_ids']:
240 if conf_file_id in all_conf_files:
241 conf_file = all_conf_files[conf_file_id]
242 conf_files[conf_file['dest']] = conf_file
244 # Get all (enabled) initscripts
245 initscripts = InitScripts(self.api, {'enabled': True})
248 system_slice_tags = SliceTags(self.api, {'tagname': 'system', 'value': '1'}).dict('slice_id')
249 system_slice_ids = system_slice_tags.keys()
251 # Get nm-controller slices
252 # xxx Thierry: should these really be exposed regardless of their mapping to nodes ?
253 controller_and_delegated_slices = Slices(self.api, {'instantiation': ['nm-controller', 'delegated']}, ['slice_id']).dict('slice_id')
254 controller_and_delegated_slice_ids = controller_and_delegated_slices.keys()
255 slice_ids = system_slice_ids + controller_and_delegated_slice_ids + node['slice_ids']
257 slivers = get_slivers(self.api, auth, slice_ids, node)
259 # get the special accounts and keys needed for the node
263 if False and 'site_id' not in node:
264 nodes = Nodes(self.api, node['node_id'])
267 # used in conjunction with reduce to flatten lists, like in
268 # reduce ( reduce_flatten_list, [ [1] , [2,3] ], []) => [ 1,2,3 ]
269 def reduce_flatten_list (x,y): return x+y
271 # power users are pis and techs
272 def get_site_power_user_keys(api,site_id_or_name):
273 site = Sites (api,site_id_or_name,['person_ids'])[0]
274 key_ids = reduce (reduce_flatten_list,
275 [ p['key_ids'] for p in \
276 Persons(api,{ 'person_id':site['person_ids'],
277 'enabled':True, '|role_ids' : [20, 40] },
280 return [ key['key'] for key in Keys (api, key_ids) if key['key_type']=='ssh']
282 # all admins regardless of their site
283 def get_all_admin_keys(api):
284 key_ids = reduce (reduce_flatten_list,
285 [ p['key_ids'] for p in \
286 Persons(api, {'peer_id':None, 'enabled':True, '|role_ids':[10] },
289 return [ key['key'] for key in Keys (api, key_ids) if key['key_type']=='ssh']
291 # 'site_admin' account setup
292 personsitekeys=get_site_power_user_keys(self.api,node['site_id'])
293 accounts.append({'name':'site_admin','keys':personsitekeys})
295 # 'root' account setup on nodes from all 'admin' users
296 personsitekeys=get_all_admin_keys(self.api)
297 accounts.append({'name':'root','keys':personsitekeys})
299 hrn = GetNodeHrn(self.api).call(auth,node['node_id'])
301 # XMPP config for omf federation
303 if not self.api.config.PLC_OMF_ENABLED:
304 raise Exception,"OMF disabled"
305 xmpp={'server':self.api.config.PLC_OMF_XMPP_SERVER,
306 'user':self.api.config.PLC_OMF_XMPP_USER,
307 'password':self.api.config.PLC_OMF_XMPP_PASSWORD,
310 xmpp={'server':None,'user':None,'password':None}
312 node.update_last_contact()
314 # expose leases & reservation policy
315 # in a first implementation we only support none and lease_or_idle
316 lease_exposed_fields = [ 'slice_id', 't_from', 't_until', 'name', ]
318 if node['node_type'] != 'reservable':
319 reservation_policy='none'
321 reservation_policy='lease_or_idle'
322 # expose the leases for the next 24 hours
323 leases = [ dict ( [ (k,l[k]) for k in lease_exposed_fields ] )
324 for l in Leases (self.api, {'node_id':node['node_id'],
325 'clip': (timestamp, timestamp+24*Duration.HOUR),
328 granularity=self.api.config.PLC_RESERVATION_GRANULARITY
331 'timestamp': timestamp,
332 'node_id': node['node_id'],
333 'hostname': node['hostname'],
334 'interfaces': interfaces,
336 'conf_files': conf_files.values(),
337 'initscripts': initscripts,
339 'accounts': accounts,
342 'reservation_policy': reservation_policy,
344 'lease_granularity': granularity,