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 from PLC.Accessors.Accessors_standard import *
25 # XXX used to check if slice expiration time is sane
28 def get_slivers(api, auth, slice_filter, node = None):
29 # Get slice information
30 slices = Slices(api, slice_filter, ['slice_id', 'name', 'instantiation', 'expires', 'person_ids', 'slice_tag_ids'])
32 # Build up list of users and slice attributes
36 person_ids.update(slice['person_ids'])
37 slice_tag_ids.update(slice['slice_tag_ids'])
39 # Get user information
40 all_persons = Persons(api, {'person_id':person_ids,'enabled':True}, ['person_id', 'enabled', 'key_ids']).dict()
42 # Build up list of keys
44 for person in all_persons.values():
45 key_ids.update(person['key_ids'])
47 # Get user account keys
48 all_keys = Keys(api, key_ids, ['key_id', 'key', 'key_type']).dict()
50 # Get slice attributes
51 all_slice_tags = SliceTags(api, slice_tag_ids).dict()
56 for person_id in slice['person_ids']:
57 if person_id in all_persons:
58 person = all_persons[person_id]
59 if not person['enabled']:
61 for key_id in person['key_ids']:
62 if key_id in all_keys:
63 key = all_keys[key_id]
64 keys += [{'key_type': key['key_type'],
69 # All (per-node and global) attributes for this slice
71 for slice_tag_id in slice['slice_tag_ids']:
72 if slice_tag_id in all_slice_tags:
73 slice_tags.append(all_slice_tags[slice_tag_id])
75 # Per-node sliver attributes take precedence over global
76 # slice attributes, so set them first.
77 # Then comes nodegroup slice attributes
78 # Followed by global slice attributes
79 sliver_attributes = []
82 for sliver_attribute in [ a for a in slice_tags if a['node_id'] == node['node_id'] ]:
83 sliver_attributes.append(sliver_attribute['tagname'])
84 attributes.append({'tagname': sliver_attribute['tagname'],
85 'value': sliver_attribute['value']})
87 # set nodegroup slice attributes
88 for slice_tag in [ a for a in slice_tags if a['nodegroup_id'] in node['nodegroup_ids'] ]:
89 # Do not set any nodegroup slice attributes for
90 # which there is at least one sliver attribute
92 if slice_tag not in slice_tags:
93 attributes.append({'tagname': slice_tag['tagname'],
94 'value': slice_tag['value']})
96 for slice_tag in [ a for a in slice_tags if a['node_id'] is None ]:
97 # Do not set any global slice attributes for
98 # which there is at least one sliver attribute
100 if slice_tag['tagname'] not in sliver_attributes:
101 attributes.append({'tagname': slice_tag['tagname'],
102 'value': slice_tag['value']})
104 # XXX Sanity check; though technically this should be a system invariant
105 # checked with an assertion
106 if slice['expires'] > MAXINT: slice['expires']= MAXINT
108 # expose the slice vref as computed by GetSliceFamily
109 family = GetSliceFamily (api).call(auth, slice['slice_id'])
112 'name': slice['name'],
113 'slice_id': slice['slice_id'],
114 'instantiation': slice['instantiation'],
115 'expires': slice['expires'],
117 'attributes': attributes,
118 'GetSliceFamily': family,
123 class GetSlivers(Method):
125 Returns a struct containing information about the specified node
126 (or calling node, if called by a node and node_id_or_hostname is
127 not specified), including the current set of slivers bound to the
130 All of the information returned by this call can be gathered from
131 other calls, e.g. GetNodes, GetInterfaces, GetSlices, etc. This
132 function exists almost solely for the benefit of Node Manager.
135 roles = ['admin', 'node']
139 Mixed(Node.fields['node_id'],
140 Node.fields['hostname']),
144 'timestamp': Parameter(int, "Timestamp of this call, in seconds since UNIX epoch"),
145 'node_id': Node.fields['node_id'],
146 'hostname': Node.fields['hostname'],
147 'interfaces': [Interface.fields],
148 'groups': [NodeGroup.fields['groupname']],
149 'conf_files': [ConfFile.fields],
150 'initscripts': [InitScript.fields],
152 'name': Parameter(str, "unix style account name", max = 254),
154 'key_type': Key.fields['key_type'],
155 'key': Key.fields['key']
159 'name': Slice.fields['name'],
160 'slice_id': Slice.fields['slice_id'],
161 'instantiation': Slice.fields['instantiation'],
162 'expires': Slice.fields['expires'],
164 'key_type': Key.fields['key_type'],
165 'key': Key.fields['key']
168 'tagname': SliceTag.fields['tagname'],
169 'value': SliceTag.fields['value']
172 'xmpp': {'server':Parameter(str,"hostname for the XMPP server"),
173 'user':Parameter(str,"username for the XMPP server"),
174 'password':Parameter(str,"username for the XMPP server"),
178 def call(self, auth, node_id_or_hostname = None):
179 timestamp = int(time.time())
182 if node_id_or_hostname is None:
183 if isinstance(self.caller, Node):
186 raise PLCInvalidArgument, "'node_id_or_hostname' not specified"
188 nodes = Nodes(self.api, [node_id_or_hostname])
190 raise PLCInvalidArgument, "No such node"
193 if node['peer_id'] is not None:
194 raise PLCInvalidArgument, "Not a local node"
196 # Get interface information
197 interfaces = Interfaces(self.api, node['interface_ids'])
199 # Get node group information
200 nodegroups = NodeGroups(self.api, node['nodegroup_ids']).dict('groupname')
201 groups = nodegroups.keys()
203 # Get all (enabled) configuration files
204 all_conf_files = ConfFiles(self.api, {'enabled': True}).dict()
207 # Global configuration files are the default. If multiple
208 # entries for the same global configuration file exist, it is
209 # undefined which one takes precedence.
210 for conf_file in all_conf_files.values():
211 if not conf_file['node_ids'] and not conf_file['nodegroup_ids']:
212 conf_files[conf_file['dest']] = conf_file
214 # Node group configuration files take precedence over global
215 # ones. If a node belongs to multiple node groups for which
216 # the same configuration file is defined, it is undefined
217 # which one takes precedence.
218 for nodegroup in nodegroups.values():
219 for conf_file_id in nodegroup['conf_file_ids']:
220 if conf_file_id in all_conf_files:
221 conf_file = all_conf_files[conf_file_id]
222 conf_files[conf_file['dest']] = conf_file
224 # Node configuration files take precedence over node group
225 # configuration files.
226 for conf_file_id in node['conf_file_ids']:
227 if conf_file_id in all_conf_files:
228 conf_file = all_conf_files[conf_file_id]
229 conf_files[conf_file['dest']] = conf_file
231 # Get all (enabled) initscripts
232 initscripts = InitScripts(self.api, {'enabled': True})
235 system_slice_tags = SliceTags(self.api, {'tagname': 'system', 'value': '1'}).dict('slice_id')
236 system_slice_ids = system_slice_tags.keys()
238 # Get nm-controller slices
239 # xxx Thierry: should these really be exposed regardless of their mapping to nodes ?
240 controller_and_delegated_slices = Slices(self.api, {'instantiation': ['nm-controller', 'delegated']}, ['slice_id']).dict('slice_id')
241 controller_and_delegated_slice_ids = controller_and_delegated_slices.keys()
242 slice_ids = system_slice_ids + controller_and_delegated_slice_ids + node['slice_ids']
244 slivers = get_slivers(self.api, auth, slice_ids, node)
246 # get the special accounts and keys needed for the node
250 if False and 'site_id' not in node:
251 nodes = Nodes(self.api, node['node_id'])
254 # used in conjunction with reduce to flatten lists, like in
255 # reduce ( reduce_flatten_list, [ [1] , [2,3] ], []) => [ 1,2,3 ]
256 def reduce_flatten_list (x,y): return x+y
258 # power users are pis and techs
259 def get_site_power_user_keys(api,site_id_or_name):
260 site = Sites (api,site_id_or_name,['person_ids'])[0]
261 key_ids = reduce (reduce_flatten_list,
262 [ p['key_ids'] for p in \
263 Persons(api,{ 'person_id':site['person_ids'],
264 'enabled':True, '|role_ids' : [20, 40] },
267 return [ key['key'] for key in Keys (api, key_ids) if key['key_type']=='ssh']
269 # all admins regardless of their site
270 def get_all_admin_keys(api):
271 key_ids = reduce (reduce_flatten_list,
272 [ p['key_ids'] for p in \
273 Persons(api, {'peer_id':None, 'enabled':True, '|role_ids':[10] },
276 return [ key['key'] for key in Keys (api, key_ids) if key['key_type']=='ssh']
278 # 'site_admin' account setup
279 personsitekeys=get_site_power_user_keys(self.api,node['site_id'])
280 accounts.append({'name':'site_admin','keys':personsitekeys})
282 # 'root' account setup on nodes from all 'admin' users
283 personsitekeys=get_all_admin_keys(self.api)
284 accounts.append({'name':'root','keys':personsitekeys})
286 hrn = GetNodeHrn(self.api).call(auth,node['node_id'])
288 # XMPP config for omf federation
290 if not self.api.config.PLC_OMF_ENABLED:
291 raise Exception,"OMF disabled"
292 xmpp={'server':self.api.config.PLC_OMF_XMPP_SERVER,
293 'user':self.api.config.PLC_OMF_XMPP_USER,
294 'password':self.api.config.PLC_OMF_XMPP_PASSWORD,
297 xmpp={'server':None,'user':None,'password':None}
299 node.update_last_contact()
302 'timestamp': timestamp,
303 'node_id': node['node_id'],
304 'hostname': node['hostname'],
305 'interfaces': interfaces,
307 'conf_files': conf_files.values(),
308 'initscripts': initscripts,
310 'accounts': accounts,