1 from sfa.util.xrn import get_authority, urn_to_hrn
2 from sfa.util.sfalogging import logger
9 rspec_to_slice_tag = {'max_rate':'net_max_rate'}
12 def __init__(self, driver):
16 #def get_slivers(self, xrn, node=None):
17 #hrn, hrn_type = urn_to_hrn(xrn)
19 #slice_name = hrn_to_pl_slicename(hrn)
20 ## XX Should we just call PLCAPI.GetSliceTicket(slice_name) instead
21 ## of doing all of this?
22 ##return self.api.driver.GetSliceTicket(self.auth, slice_name)
26 #sfa_slice = self.driver.GetSlices(slice_filter = slice_name, \
27 # slice_filter_type = 'slice_hrn')
30 ## Get user information
32 #alchemy_person = dbsession.query(RegRecord).filter_by(record_id = \
33 #sfa_slice['record_id_user']).first()
36 #sliver_attributes = []
38 #if sfa_slice['oar_job_id'] is not -1:
39 #nodes_all = self.driver.GetNodes({'hostname': \
40 #sfa_slice['node_ids']},
41 #['node_id', 'hostname','site','boot_state'])
42 #nodeall_byhostname = dict([(n['hostname'], n) for n in nodes_all])
43 #nodes = sfa_slice['node_ids']
46 ##for sliver_attribute in filter(lambda a: a['node_id'] == \
47 #node['node_id'], slice_tags):
48 #sliver_attribute['tagname'] = 'slab-tag'
49 #sliver_attribute['value'] = 'slab-value'
50 #sliver_attributes.append(sliver_attribute['tagname'])
51 #attributes.append({'tagname': sliver_attribute['tagname'],
52 #'value': sliver_attribute['value']})
54 ## set nodegroup slice attributes
55 #for slice_tag in filter(lambda a: a['nodegroup_id'] \
56 #in node['nodegroup_ids'], slice_tags):
57 ## Do not set any nodegroup slice attributes for
58 ## which there is at least one sliver attribute
60 #if slice_tag not in slice_tags:
61 #attributes.append({'tagname': slice_tag['tagname'],
62 #'value': slice_tag['value']})
64 #for slice_tag in filter(lambda a: a['node_id'] is None, \
66 ## Do not set any global slice attributes for
67 ## which there is at least one sliver attribute
69 #if slice_tag['tagname'] not in sliver_attributes:
70 #attributes.append({'tagname': slice_tag['tagname'],
71 #'value': slice_tag['value']})
73 ## XXX Sanity check; though technically this should
74 ## be a system invariant
75 ## checked with an assertion
76 #if sfa_slice['expires'] > MAXINT: sfa_slice['expires']= MAXINT
80 #'name': sfa_slice['name'],
81 #'slice_id': sfa_slice['slice_id'],
82 #'instantiation': sfa_slice['instantiation'],
83 #'expires': sfa_slice['expires'],
85 #'attributes': attributes
95 def get_peer(self, xrn):
96 hrn, hrn_type = urn_to_hrn(xrn)
97 #Does this slice belong to a local site or a peer senslab site?
100 # get this slice's authority (site)
101 slice_authority = get_authority(hrn)
102 site_authority = slice_authority
103 # get this site's authority (sfa root authority or sub authority)
104 #site_authority = get_authority(slice_authority).lower()
105 logger.debug("SLABSLICES \ get_peer slice_authority %s \
106 site_authority %s hrn %s" %(slice_authority, \
107 site_authority, hrn))
108 #This slice belongs to the current site
109 if site_authority == self.driver.root_auth :
111 # check if we are already peered with this site_authority, if so
112 #peers = self.driver.GetPeers({})
113 peers = self.driver.GetPeers(peer_filter = slice_authority)
114 for peer_record in peers:
116 if site_authority == peer_record.hrn:
118 logger.debug(" SLABSLICES \tget_peer peer %s " %(peer))
121 def get_sfa_peer(self, xrn):
122 hrn, hrn_type = urn_to_hrn(xrn)
124 # return the authority for this hrn or None if we are the authority
126 slice_authority = get_authority(hrn)
127 site_authority = get_authority(slice_authority)
129 if site_authority != self.driver.hrn:
130 sfa_peer = site_authority
135 def verify_slice_leases(self, sfa_slice, requested_jobs_dict, kept_leases, \
139 #First get the list of current leases from OAR
140 leases = self.driver.GetLeases({'name':sfa_slice['slice_hrn']})
141 logger.debug("SLABSLICES verify_slice_leases requested_jobs_dict %s leases %s "%(requested_jobs_dict, leases ))
142 #leases = self.driver.GetLeases({'name':sfa_slice['name']},\
145 current_nodes_reserved_by_start_time = {}
146 requested_nodes_by_start_time = {}
147 leases_by_start_time = {}
148 #Create reduced dictionary with key start_time and value list of nodes
149 #-for the leases already registered by OAR first
150 # then for the new leases requested by the user
152 #Leases already scheduled/running in OAR
153 for lease in leases :
154 current_nodes_reserved_by_start_time[lease['t_from']] = lease['reserved_nodes']
155 leases_by_start_time[lease['t_from']] = lease
158 for start_time in requested_jobs_dict:
159 requested_nodes_by_start_time[int(start_time)] = requested_jobs_dict[start_time]['hostname']
161 #Check if there is any difference between the leases already
162 #registered in OAR and the requested jobs.
163 #Difference could be:
164 #-Lease deleted in the requested jobs
165 #-Added/removed nodes
168 logger.debug("SLABSLICES verify_slice_leases requested_nodes_by_start_time %s current_nodes_reserved_by_start_time %s " %(requested_nodes_by_start_time,current_nodes_reserved_by_start_time))
170 #Find all deleted leases
171 start_time_list = list(set(leases_by_start_time.keys()).difference(requested_nodes_by_start_time.keys()))
172 deleted_leases = [leases_by_start_time[start_time]['lease_id'] for start_time in start_time_list]
176 for start_time in requested_nodes_by_start_time:
177 if start_time in current_nodes_reserved_by_start_time:
179 if requested_nodes_by_start_time[start_time] == \
180 current_nodes_reserved_by_start_time[start_time]:
184 update_node_set = set(requested_nodes_by_start_time[start_time])
185 added_nodes = update_node_set.difference(current_nodes_reserved_by_start_time[start_time])
186 shared_nodes = update_node_set.intersection(current_nodes_reserved_by_start_time[start_time])
187 old_nodes_set = set(current_nodes_reserved_by_start_time[start_time])
188 removed_nodes = old_nodes_set.difference(requested_nodes_by_start_time[start_time])
189 logger.debug("SLABSLICES verify_slice_leases shared_nodes %s added_nodes %s removed_nodes %s"%(shared_nodes, added_nodes,removed_nodes ))
190 #If the lease is modified, delete it before creating it again.
191 #Add the deleted lease job id in the list
192 if added_nodes or removed_nodes:
193 deleted_leases.append(leases_by_start_time[start_time]['lease_id'])
195 if added_nodes or shared_nodes:
196 reschedule[str(start_time)] = requested_jobs_dict[str(start_time)]
197 #logger.debug("SLABSLICES verify_slice_leases RESCHEDULE!!!!!")
198 #job = requested_jobs_dict[str(start_time)]
199 #self.driver.AddLeases(job['hostname'], \
200 #sfa_slice, int(job['start_time']), \
201 #int(job['duration']))
204 logger.debug("SLABSLICES verify_slice_leases NEW LEASE")
205 job = requested_jobs_dict[str(start_time)]
206 self.driver.AddLeases(job['hostname'], \
207 sfa_slice, int(job['start_time']), \
208 int(job['duration']))
210 current_leases = [lease['lease_id'] for lease in leases]
211 logger.debug("SLABSLICES verify_slice_leases current_leases %s kept_leases %s requested_jobs_dict %s"\
212 %(current_leases,kept_leases,requested_jobs_dict))
213 #Deleted leases are the ones with lease id not declared in the Rspec
214 #deleted_leases = list(set(current_leases).difference(kept_leases))
216 self.driver.DeleteLeases(deleted_leases, sfa_slice['slice_hrn'])
219 for start_time in reschedule :
220 job = reschedule[start_time]
221 self.driver.AddLeases(job['hostname'], \
222 sfa_slice, int(job['start_time']), \
223 int(job['duration']))
227 ##peer = RegAuyhority object is unsubscriptable
228 ##TODO :UnBindObjectFromPeer Quick and dirty
229 ##auth='senslab2 SA 27/07/12
231 ##Commented out UnBindObjectFromPeer SA 09/10/12
232 ##self.driver.UnBindObjectFromPeer('senslab2', 'slice', \
233 ##sfa_slice['record_id_slice'], peer.hrn)
234 #logger.debug("SLABSLICES verify_slice_leases slice %s deleted_leases %s"\
235 #%(sfa_slice, deleted_leases))
236 #self.driver.DeleteLeases(deleted_leases, \
238 ##self.driver.DeleteLeases(deleted_leases, \
241 ##TODO verify_slice_leases: catch other exception?
243 #logger.log_exc('Failed to remove slice leases')
246 #for start_time in requested_jobs_dict:
247 #job = requested_jobs_dict[start_time]
248 #self.driver.AddLeases(job['hostname'], \
249 #sfa_slice, int(job['start_time']), \
250 #int(job['duration']))
254 def verify_slice_nodes(self, sfa_slice, requested_slivers, peer):
258 if 'node_ids' in sfa_slice:
259 nodes = self.driver.GetNodes(sfa_slice['list_node_ids'], ['hostname'])
260 current_slivers = [node['hostname'] for node in nodes]
262 # remove nodes not in rspec
263 deleted_nodes = list(set(current_slivers).\
264 difference(requested_slivers))
265 # add nodes from rspec
266 #added_nodes = list(set(requested_slivers).\
267 #difference(current_slivers))
269 #Update the table with the nodes that populate the slice
270 logger.debug("SLABSLICES \tverify_slice_nodes slice %s\
271 \r\n \r\n deleted_nodes %s"\
272 %(sfa_slice,deleted_nodes))
275 #Delete the entire experience
276 self.driver.DeleteSliceFromNodes(sfa_slice)
277 #self.driver.DeleteSliceFromNodes(sfa_slice['slice_hrn'], \
283 def free_egre_key(self):
285 for tag in self.driver.GetSliceTags({'tagname': 'egre_key'}):
286 used.add(int(tag['value']))
288 for i in range(1, 256):
293 raise KeyError("No more EGRE keys available")
302 def handle_peer(self, site, sfa_slice, persons, peer):
307 self.driver.BindObjectToPeer('site', site['site_id'], \
308 peer['shortname'], sfa_slice['site_id'])
309 except Exception, error:
310 self.driver.DeleteSite(site['site_id'])
316 self.driver.BindObjectToPeer('slice', slice['slice_id'], \
317 peer['shortname'], sfa_slice['slice_id'])
318 except Exception, error:
319 self.driver.DeleteSlice(sfa_slice['slice_id'])
323 for person in persons:
325 self.driver.BindObjectToPeer('person', \
326 person['person_id'], peer['shortname'], \
327 person['peer_person_id'])
329 for (key, remote_key_id) in zip(person['keys'], \
332 self.driver.BindObjectToPeer( 'key', \
333 key['key_id'], peer['shortname'], \
336 self.driver.DeleteKey(key['key_id'])
337 logger.log_exc("failed to bind key: %s \
338 to peer: %s " % (key['key_id'], \
340 except Exception, error:
341 self.driver.DeletePerson(person['person_id'])
346 #def verify_site(self, slice_xrn, slice_record={}, peer=None, \
347 #sfa_peer=None, options={}):
348 #(slice_hrn, type) = urn_to_hrn(slice_xrn)
349 #site_hrn = get_authority(slice_hrn)
350 ## login base can't be longer than 20 characters
351 ##slicename = hrn_to_pl_slicename(slice_hrn)
352 #authority_name = slice_hrn.split('.')[0]
353 #login_base = authority_name[:20]
354 #logger.debug(" SLABSLICES.PY \tverify_site authority_name %s \
355 #login_base %s slice_hrn %s" \
356 #%(authority_name,login_base,slice_hrn)
358 #sites = self.driver.GetSites(login_base)
360 ## create new site record
361 #site = {'name': 'geni.%s' % authority_name,
362 #'abbreviated_name': authority_name,
363 #'login_base': login_base,
365 #'max_slivers': 1000,
367 #'peer_site_id': None}
369 #site['peer_site_id'] = slice_record.get('site_id', None)
370 #site['site_id'] = self.driver.AddSite(site)
371 ## exempt federated sites from monitor policies
372 #self.driver.AddSiteTag(site['site_id'], 'exempt_site_until', \
375 ### is this still necessary?
376 ### add record to the local registry
377 ##if sfa_peer and slice_record:
378 ##peer_dict = {'type': 'authority', 'hrn': site_hrn, \
379 ##'peer_authority': sfa_peer, 'pointer': \
381 ##self.registry.register_peer_object(self.credential, peer_dict)
385 ## unbind from peer so we can modify if necessary.
386 ## Will bind back later
387 #self.driver.UnBindObjectFromPeer('site', site['site_id'], \
392 def verify_slice(self, slice_hrn, slice_record, peer, sfa_peer):
394 #login_base = slice_hrn.split(".")[0]
395 slicename = slice_hrn
396 slices_list = self.driver.GetSlices(slice_filter = slicename, \
397 slice_filter_type = 'slice_hrn')
399 for sl in slices_list:
401 logger.debug("SLABSLICE \tverify_slice slicename %s sl %s \
402 slice_record %s"%(slicename, sl, \
405 sfa_slice.update(slice_record)
406 #del slice['last_updated']
407 #del slice['date_created']
409 #slice['peer_slice_id'] = slice_record.get('slice_id', None)
410 ## unbind from peer so we can modify if necessary.
411 ## Will bind back later
412 #self.driver.UnBindObjectFromPeer('slice', \
413 #slice['slice_id'], \
415 #Update existing record (e.g. expires field)
416 #it with the latest info.
417 ##if slice_record and slice['expires'] != slice_record['expires']:
418 ##self.driver.UpdateSlice( slice['slice_id'], {'expires' : \
419 #slice_record['expires']})
421 logger.debug(" SLABSLICES \tverify_slice Oups \
422 slice_record %s peer %s sfa_peer %s "\
423 %(slice_record, peer,sfa_peer))
424 sfa_slice = {'slice_hrn': slicename,
425 #'url': slice_record.get('url', slice_hrn),
426 #'description': slice_record.get('description', slice_hrn)
428 'record_id_user' : slice_record['person_ids'][0],
429 'record_id_slice': slice_record['record_id'],
430 'peer_authority':str(peer.hrn)
434 self.driver.AddSlice(sfa_slice)
435 #slice['slice_id'] = self.driver.AddSlice(slice)
436 logger.debug("SLABSLICES \tverify_slice ADDSLICE OK")
437 #slice['node_ids']=[]
438 #slice['person_ids'] = []
440 #slice['peer_slice_id'] = slice_record.get('slice_id', None)
441 # mark this slice as an sfa peer record
443 #peer_dict = {'type': 'slice', 'hrn': slice_hrn,
444 #'peer_authority': sfa_peer, 'pointer': \
446 #self.registry.register_peer_object(self.credential, peer_dict)
453 def verify_persons(self, slice_hrn, slice_record, users, peer, sfa_peer, \
456 users is a record list. Records can either be local records
457 or users records from known and trusted federated sites.
458 If the user is from another site that senslab doesn't trust yet,
459 then Resolve will raise an error before getting to create_sliver.
461 #TODO SA 21/08/12 verify_persons Needs review
466 #users_dict : dict whose keys can either be the user's hrn or its id.
467 #Values contains only id and hrn
470 #First create dicts by hrn and id for each user in the user record list:
473 if 'urn' in user and (not 'hrn' in user ) :
474 user['hrn'], user['type'] = urn_to_hrn(user['urn'])
476 if 'person_id' in user and 'hrn' in user:
477 users_by_id[user['person_id']] = user
478 users_dict[user['person_id']] = {'person_id':\
479 user['person_id'], 'hrn':user['hrn']}
481 users_by_hrn[user['hrn']] = user
482 users_dict[user['hrn']] = {'person_id':user['person_id'], \
486 logger.debug( "SLABSLICE.PY \t verify_person \
487 users_dict %s \r\n user_by_hrn %s \r\n \
489 %(users_dict,users_by_hrn, users_by_id))
491 existing_user_ids = []
492 existing_user_hrns = []
494 # Check if user is in Senslab LDAP using its hrn.
495 # Assuming Senslab is centralised : one LDAP for all sites,
496 # user_id unknown from LDAP
497 # LDAP does not provide users id, therefore we rely on hrns containing
498 # the login of the user.
499 # If the hrn is not a senslab hrn, the user may not be in LDAP.
501 #Construct the list of filters (list of dicts) for GetPersons
503 for hrn in users_by_hrn:
504 filter_user.append (users_by_hrn[hrn])
505 logger.debug(" SLABSLICE.PY \tverify_person filter_user %s " \
507 #Check user's in LDAP with GetPersons
508 #Needed because what if the user has been deleted in LDAP but
510 existing_users = self.driver.GetPersons(filter_user)
512 #User's in senslab LDAP
514 for user in existing_users :
515 existing_user_hrns.append(users_dict[user['hrn']]['hrn'])
517 append(users_dict[user['hrn']]['person_id'])
519 # User from another known trusted federated site. Check
520 # if a senslab account matching the email has already been created.
523 if isinstance(users, list):
525 req += users[0]['email']
527 req += users['email']
529 ldap_reslt = self.driver.ldap.LdapSearch(req)
531 logger.debug(" SLABSLICE.PY \tverify_person users \
532 USER already in Senslab \t ldap_reslt %s \
534 existing_users.append(ldap_reslt[1])
537 #User not existing in LDAP
538 #TODO SA 21/08/12 raise smthg to add user or add it auto ?
539 logger.debug(" SLABSLICE.PY \tverify_person users \
540 not in ldap ...NEW ACCOUNT NEEDED %s \r\n \t \
541 ldap_reslt %s " %(users, ldap_reslt))
543 requested_user_ids = users_by_id.keys()
544 requested_user_hrns = users_by_hrn.keys()
545 logger.debug("SLABSLICE.PY \tverify_person requested_user_ids %s \
546 user_by_hrn %s " %(requested_user_ids, users_by_hrn))
549 #Check that the user of the slice in the slice record
550 #matches the existing users
552 if slice_record['record_id_user'] in requested_user_ids and \
553 slice_record['PI'][0] in requested_user_hrns:
554 logger.debug(" SLABSLICE \tverify_person \
555 requested_user_ids %s = \
556 slice_record['record_id_user'] %s" \
557 %(requested_user_ids,slice_record['record_id_user']))
563 # users to be added, removed or updated
564 #One user in one senslab slice : there should be no need
565 #to remove/ add any user from/to a slice.
566 #However a user from SFA which is not registered in Senslab yet
567 #should be added to the LDAP.
569 added_user_hrns = set(requested_user_hrns).\
570 difference(set(existing_user_hrns))
572 #self.verify_keys(existing_slice_users, updated_users_list, \
577 for added_user_hrn in added_user_hrns:
578 added_user = users_dict[added_user_hrn]
579 #hrn, type = urn_to_hrn(added_user['urn'])
581 #'first_name': added_user.get('first_name', hrn),
582 #'last_name': added_user.get('last_name', hrn),
583 'first_name': added_user['first_name'],
584 'last_name': added_user['last_name'],
585 'person_id': added_user['person_id'],
586 'peer_person_id': None,
588 'key_ids': added_user.get('key_ids', []),
591 person['person_id'] = self.driver.AddPerson(person)
593 person['peer_person_id'] = added_user['person_id']
594 added_persons.append(person)
597 self.driver.UpdatePerson(person['person_id'], {'enabled': True})
600 #self.driver.AddPersonToSite(added_user_id, login_base)
602 #for key_string in added_user.get('keys', []):
603 #key = {'key':key_string, 'key_type':'ssh'}
604 #key['key_id'] = self.driver.AddPersonKey(person['person_id'], \
606 #person['keys'].append(key)
608 # add the registry record
610 #peer_dict = {'type': 'user', 'hrn': hrn, 'peer_authority': \
612 #'pointer': person['person_id']}
613 #self.registry.register_peer_object(self.credential, peer_dict)
614 #for added_slice_user_hrn in \
615 #added_slice_user_hrns.union(added_user_hrns):
616 #self.driver.AddPersonToSlice(added_slice_user_hrn, \
617 #slice_record['name'])
618 #for added_slice_user_id in \
619 #added_slice_user_ids.union(added_user_ids):
620 # add person to the slice
621 #self.driver.AddPersonToSlice(added_slice_user_id, \
622 #slice_record['name'])
623 # if this is a peer record then it
624 # should already be bound to a peer.
625 # no need to return worry about it getting bound later
630 def verify_keys(self, persons, users, peer, options={}):
633 for person in persons:
634 key_ids.extend(person['key_ids'])
635 keylist = self.driver.GetKeys(key_ids, ['key_id', 'key'])
638 keydict[key['key']] = key['key_id']
639 existing_keys = keydict.keys()
641 for person in persons:
642 persondict[person['email']] = person
648 user_keys = user.get('keys', [])
649 updated_persons.append(user)
650 for key_string in user_keys:
651 requested_keys.append(key_string)
652 if key_string not in existing_keys:
653 key = {'key': key_string, 'key_type': 'ssh'}
656 person = persondict[user['email']]
657 self.driver.UnBindObjectFromPeer('person', \
658 person['person_id'], peer['shortname'])
660 self.driver.AddPersonKey(user['email'], key)
662 key_index = user_keys.index(key['key'])
663 remote_key_id = user['key_ids'][key_index]
664 self.driver.BindObjectToPeer('key', \
665 key['key_id'], peer['shortname'], \
670 self.driver.BindObjectToPeer('person', \
671 person['person_id'], peer['shortname'], \
674 # remove old keys (only if we are not appending)
675 append = options.get('append', True)
677 removed_keys = set(existing_keys).difference(requested_keys)
678 for existing_key_id in keydict:
679 if keydict[existing_key_id] in removed_keys:
682 self.driver.UnBindObjectFromPeer('key', \
683 existing_key_id, peer['shortname'])
684 self.driver.DeleteKey(existing_key_id)
687 #def verify_slice_attributes(self, slice, requested_slice_attributes, \
688 #append=False, admin=False):
689 ## get list of attributes users ar able to manage
690 #filter = {'category': '*slice*'}
692 #filter['|roles'] = ['user']
693 #slice_attributes = self.driver.GetTagTypes(filter)
694 #valid_slice_attribute_names = [attribute['tagname'] \
695 #for attribute in slice_attributes]
697 ## get sliver attributes
698 #added_slice_attributes = []
699 #removed_slice_attributes = []
700 #ignored_slice_attribute_names = []
701 #existing_slice_attributes = self.driver.GetSliceTags({'slice_id': \
704 ## get attributes that should be removed
705 #for slice_tag in existing_slice_attributes:
706 #if slice_tag['tagname'] in ignored_slice_attribute_names:
707 ## If a slice already has a admin only role
708 ## it was probably given to them by an
709 ## admin, so we should ignore it.
710 #ignored_slice_attribute_names.append(slice_tag['tagname'])
712 ## If an existing slice attribute was not
713 ## found in the request it should
715 #attribute_found=False
716 #for requested_attribute in requested_slice_attributes:
717 #if requested_attribute['name'] == slice_tag['tagname'] \
718 #and requested_attribute['value'] == slice_tag['value']:
719 #attribute_found=True
722 #if not attribute_found and not append:
723 #removed_slice_attributes.append(slice_tag)
725 ## get attributes that should be added:
726 #for requested_attribute in requested_slice_attributes:
727 ## if the requested attribute wasn't found we should add it
728 #if requested_attribute['name'] in valid_slice_attribute_names:
729 #attribute_found = False
730 #for existing_attribute in existing_slice_attributes:
731 #if requested_attribute['name'] == \
732 #existing_attribute['tagname'] and \
733 #requested_attribute['value'] == \
734 #existing_attribute['value']:
735 #attribute_found=True
737 #if not attribute_found:
738 #added_slice_attributes.append(requested_attribute)
741 ## remove stale attributes
742 #for attribute in removed_slice_attributes:
744 #self.driver.DeleteSliceTag(attribute['slice_tag_id'])
745 #except Exception, error:
746 #self.logger.warn('Failed to remove sliver attribute. name: \
747 #%s, value: %s, node_id: %s\nCause:%s'\
748 #% (name, value, node_id, str(error)))
750 ## add requested_attributes
751 #for attribute in added_slice_attributes:
753 #self.driver.AddSliceTag(slice['name'], attribute['name'], \
754 #attribute['value'], attribute.get('node_id', None))
755 #except Exception, error:
756 #self.logger.warn('Failed to add sliver attribute. name: %s, \
757 #value: %s, node_id: %s\nCause:%s'\
758 #% (name, value, node_id, str(error)))