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.Methods.GetSliceFamily import GetSliceFamily
23 # XXX used to check if slice expiration time is sane
26 def get_slivers(api, auth, slice_filter, node = None):
27 # Get slice information
28 slices = Slices(api, slice_filter, ['slice_id', 'name', 'instantiation', 'expires', 'person_ids', 'slice_tag_ids'])
30 # Build up list of users and slice attributes
34 person_ids.update(slice['person_ids'])
35 slice_tag_ids.update(slice['slice_tag_ids'])
37 # Get user information
38 all_persons = Persons(api, {'person_id':person_ids,'enabled':True}, ['person_id', 'enabled', 'key_ids']).dict()
40 # Build up list of keys
42 for person in all_persons.values():
43 key_ids.update(person['key_ids'])
45 # Get user account keys
46 all_keys = Keys(api, key_ids, ['key_id', 'key', 'key_type']).dict()
48 # Get slice attributes
49 all_slice_tags = SliceTags(api, slice_tag_ids).dict()
54 for person_id in slice['person_ids']:
55 if person_id in all_persons:
56 person = all_persons[person_id]
57 if not person['enabled']:
59 for key_id in person['key_ids']:
60 if key_id in all_keys:
61 key = all_keys[key_id]
62 keys += [{'key_type': key['key_type'],
67 # All (per-node and global) attributes for this slice
69 for slice_tag_id in slice['slice_tag_ids']:
70 if slice_tag_id in all_slice_tags:
71 slice_tags.append(all_slice_tags[slice_tag_id])
73 # Per-node sliver attributes take precedence over global
74 # slice attributes, so set them first.
75 # Then comes nodegroup slice attributes
76 # Followed by global slice attributes
77 sliver_attributes = []
80 for sliver_attribute in [ a for a in slice_tags if a['node_id'] == node['node_id'] ]:
81 sliver_attributes.append(sliver_attribute['tagname'])
82 attributes.append({'tagname': sliver_attribute['tagname'],
83 'value': sliver_attribute['value']})
85 # set nodegroup slice attributes
86 for slice_tag in [ a for a in slice_tags if a['nodegroup_id'] in node['nodegroup_ids'] ]:
87 # Do not set any nodegroup slice attributes for
88 # which there is at least one sliver attribute
90 if slice_tag not in slice_tags:
91 attributes.append({'tagname': slice_tag['tagname'],
92 'value': slice_tag['value']})
94 for slice_tag in [ a for a in slice_tags if a['node_id'] is None ]:
95 # Do not set any global slice attributes for
96 # which there is at least one sliver attribute
98 if slice_tag['tagname'] not in sliver_attributes:
99 attributes.append({'tagname': slice_tag['tagname'],
100 'value': slice_tag['value']})
102 # XXX Sanity check; though technically this should be a system invariant
103 # checked with an assertion
104 if slice['expires'] > MAXINT: slice['expires']= MAXINT
106 # expose the slice vref as computed by GetSliceFamily
107 family = GetSliceFamily (api).call(auth, slice['slice_id'])
110 'name': slice['name'],
111 'slice_id': slice['slice_id'],
112 'instantiation': slice['instantiation'],
113 'expires': slice['expires'],
115 'attributes': attributes,
116 'GetSliceFamily': family,
121 class GetSlivers(Method):
123 Returns a struct containing information about the specified node
124 (or calling node, if called by a node and node_id_or_hostname is
125 not specified), including the current set of slivers bound to the
128 All of the information returned by this call can be gathered from
129 other calls, e.g. GetNodes, GetInterfaces, GetSlices, etc. This
130 function exists almost solely for the benefit of Node Manager.
133 roles = ['admin', 'node']
137 Mixed(Node.fields['node_id'],
138 Node.fields['hostname']),
142 'timestamp': Parameter(int, "Timestamp of this call, in seconds since UNIX epoch"),
143 'node_id': Node.fields['node_id'],
144 'hostname': Node.fields['hostname'],
145 'interfaces': [Interface.fields],
146 'groups': [NodeGroup.fields['groupname']],
147 'conf_files': [ConfFile.fields],
148 'initscripts': [InitScript.fields],
150 'name': Parameter(str, "unix style account name", max = 254),
152 'key_type': Key.fields['key_type'],
153 'key': Key.fields['key']
157 'name': Slice.fields['name'],
158 'slice_id': Slice.fields['slice_id'],
159 'instantiation': Slice.fields['instantiation'],
160 'expires': Slice.fields['expires'],
162 'key_type': Key.fields['key_type'],
163 'key': Key.fields['key']
166 'tagname': SliceTag.fields['tagname'],
167 'value': SliceTag.fields['value']
170 'xmpp': {'server':Parameter(str,"hostname for the XMPP server"),
171 'user':Parameter(str,"username for the XMPP server"),
172 'password':Parameter(str,"username for the XMPP server"),
176 def call(self, auth, node_id_or_hostname = None):
177 timestamp = int(time.time())
180 if node_id_or_hostname is None:
181 if isinstance(self.caller, Node):
184 raise PLCInvalidArgument, "'node_id_or_hostname' not specified"
186 nodes = Nodes(self.api, [node_id_or_hostname])
188 raise PLCInvalidArgument, "No such node"
191 if node['peer_id'] is not None:
192 raise PLCInvalidArgument, "Not a local node"
194 # Get interface information
195 interfaces = Interfaces(self.api, node['interface_ids'])
197 # Get node group information
198 nodegroups = NodeGroups(self.api, node['nodegroup_ids']).dict('groupname')
199 groups = nodegroups.keys()
201 # Get all (enabled) configuration files
202 all_conf_files = ConfFiles(self.api, {'enabled': True}).dict()
205 # Global configuration files are the default. If multiple
206 # entries for the same global configuration file exist, it is
207 # undefined which one takes precedence.
208 for conf_file in all_conf_files.values():
209 if not conf_file['node_ids'] and not conf_file['nodegroup_ids']:
210 conf_files[conf_file['dest']] = conf_file
212 # Node group configuration files take precedence over global
213 # ones. If a node belongs to multiple node groups for which
214 # the same configuration file is defined, it is undefined
215 # which one takes precedence.
216 for nodegroup in nodegroups.values():
217 for conf_file_id in nodegroup['conf_file_ids']:
218 if conf_file_id in all_conf_files:
219 conf_file = all_conf_files[conf_file_id]
220 conf_files[conf_file['dest']] = conf_file
222 # Node configuration files take precedence over node group
223 # configuration files.
224 for conf_file_id in node['conf_file_ids']:
225 if conf_file_id in all_conf_files:
226 conf_file = all_conf_files[conf_file_id]
227 conf_files[conf_file['dest']] = conf_file
229 # Get all (enabled) initscripts
230 initscripts = InitScripts(self.api, {'enabled': True})
233 system_slice_tags = SliceTags(self.api, {'tagname': 'system', 'value': '1'}).dict('slice_id')
234 system_slice_ids = system_slice_tags.keys()
236 # Get nm-controller slices
237 # xxx Thierry: should these really be exposed regardless of their mapping to nodes ?
238 controller_and_delegated_slices = Slices(self.api, {'instantiation': ['nm-controller', 'delegated']}, ['slice_id']).dict('slice_id')
239 controller_and_delegated_slice_ids = controller_and_delegated_slices.keys()
240 slice_ids = system_slice_ids + controller_and_delegated_slice_ids + node['slice_ids']
242 slivers = get_slivers(self.api, auth, slice_ids, node)
244 # get the special accounts and keys needed for the node
248 if False and 'site_id' not in node:
249 nodes = Nodes(self.api, node['node_id'])
252 # used in conjunction with reduce to flatten lists, like in
253 # reduce ( reduce_flatten_list, [ [1] , [2,3] ], []) => [ 1,2,3 ]
254 def reduce_flatten_list (x,y): return x+y
256 # power users are pis and techs
257 def get_site_power_user_keys(api,site_id_or_name):
258 site = Sites (api,site_id_or_name,['person_ids'])[0]
259 key_ids = reduce (reduce_flatten_list,
260 [ p['key_ids'] for p in \
261 Persons(api,{ 'person_id':site['person_ids'],
262 'enabled':True, '|role_ids' : [20, 40] },
265 return [ key['key'] for key in Keys (api, key_ids) if key['key_type']=='ssh']
267 # all admins regardless of their site
268 def get_all_admin_keys(api):
269 key_ids = reduce (reduce_flatten_list,
270 [ p['key_ids'] for p in \
271 Persons(api, {'peer_id':None, 'enabled':True, '|role_ids':[10] },
274 return [ key['key'] for key in Keys (api, key_ids) if key['key_type']=='ssh']
276 # 'site_admin' account setup
277 personsitekeys=get_site_power_user_keys(self.api,node['site_id'])
278 accounts.append({'name':'site_admin','keys':personsitekeys})
280 # 'root' account setup on nodes from all 'admin' users
281 personsitekeys=get_all_admin_keys(self.api)
282 accounts.append({'name':'root','keys':personsitekeys})
284 node.update_last_contact()
286 # XMPP config for omf federation
288 if not self.api.config.PLC_OMF_ENABLED:
289 raise Exception,"OMF disabled"
290 xmpp={'server':self.api.config.PLC_OMF_XMPP_SERVER,
291 'user':self.api.config.PLC_OMF_XMPP_USER,
292 'password':self.api.config.PLC_OMF_XMPP_PASSWORD,
295 xmpp={'server':None,'user':None,'password':None}
298 'timestamp': timestamp,
299 'node_id': node['node_id'],
300 'hostname': node['hostname'],
301 'interfaces': interfaces,
303 'conf_files': conf_files.values(),
304 'initscripts': initscripts,
306 'accounts': accounts,