2 File used to handle issuing request to OAR and parse OAR's JSON responses.
3 Contains the following classes:
4 - JsonPage : handles multiple pages OAR answers.
5 - OARRestapi : handles issuing POST or GET requests to OAR.
6 - ParsingResourcesFull : dedicated to parsing OAR's answer to a get resources
8 - OARGETParser : handles parsing the Json answers to different GET requests.
11 from httplib import HTTPConnection, HTTPException, NotConnected
13 from sfa.util.config import Config
14 from sfa.util.sfalogging import logger
20 """Class used to manipulate json pages given by OAR.
22 In case the json answer from a GET request is too big to fit in one json
23 page, this class provides helper methods to retrieve all the pages and
24 store them in a list before putting them into one single json dictionary,
25 facilitating the parsing.
30 """Defines attributes to manipulate and parse the json pages.
33 #All are boolean variables
34 self.concatenate = False
35 #Indicates end of data, no more pages to be loaded.
37 self.next_page = False
39 self.next_offset = None
43 def FindNextPage(self):
45 Gets next data page from OAR when the query's results are too big to
46 be transmitted in a single page. Uses the "links' item in the json
47 returned to check if an additionnal page has to be loaded. Updates
48 object attributes next_page, next_offset, and end.
51 if "links" in self.raw_json:
52 for page in self.raw_json['links']:
53 if page['rel'] == 'next':
54 self.concatenate = True
56 self.next_offset = "?" + page['href'].split("?")[1]
61 self.next_page = False
62 self.next_offset = None
66 #Otherwise, no next page and no concatenate, must be a single page
67 #Concatenate the single page and get out of here.
69 self.next_page = False
70 self.concatenate = True
71 self.next_offset = None
75 def ConcatenateJsonPages(saved_json_list):
77 If the json answer is too big to be contained in a single page,
78 all the pages have to be loaded and saved before being appended to the
81 :param saved_json_list: list of all the stored pages, including the
83 :type saved_json_list: list
84 :returns: Returns a dictionary with all the pages saved in the
85 saved_json_list. The key of the dictionary is 'items'.
89 .. seealso:: SendRequest
90 .. warning:: Assumes the apilib is 0.2.10 (with the 'items' key in the
99 for page in saved_json_list:
100 tmp['items'].extend(page['items'])
103 def ResetNextPage(self):
105 Resets all the Json page attributes (next_page, next_offset,
106 concatenate, end). Has to be done before getting another json answer
107 so that the previous page status does not affect the new json load.
110 self.next_page = True
111 self.next_offset = None
112 self.concatenate = False
117 """Class used to connect to the OAR server and to send GET and POST
124 OAR_REQUEST_POST_URI_DICT = {'POST_job': {'uri': '/oarapi/jobs.json'},
126 {'uri': '/oarapi/jobs/id.json'},
129 POST_FORMAT = {'json': {'content': "application/json", 'object': json}}
131 #OARpostdatareqfields = {'resource' :"/nodes=", 'command':"sleep", \
132 #'workdir':"/home/", 'walltime':""}
134 def __init__(self, config_file='/etc/sfa/oar_config.py'):
136 self.oarserver['uri'] = None
137 self.oarserver['postformat'] = 'json'
140 execfile(config_file, self.__dict__)
142 self.config_file = config_file
143 # path to configuration data
144 self.config_path = os.path.dirname(config_file)
147 raise IOError, "Could not find or load the configuration file: %s" \
149 #logger.setLevelDebug()
150 self.oarserver['ip'] = self.OAR_IP
151 self.oarserver['port'] = self.OAR_PORT
152 self.jobstates = ['Terminated', 'Hold', 'Waiting', 'toLaunch',
153 'toError', 'toAckReservation', 'Launching',
154 'Finishing', 'Running', 'Suspended', 'Resuming',
157 self.parser = OARGETParser(self)
160 def GETRequestToOARRestAPI(self, request, strval=None,
161 next_page=None, username=None):
163 """Makes a GET request to OAR.
165 Fetch the uri associated with the resquest stored in
166 OARrequests_uri_dict, adds the username if needed and if available, adds
167 strval to the request uri if needed, connects to OAR and issues the GET
168 request. Gets the json reply.
170 :param request: One of the known get requests that are keys in the
171 OARrequests_uri_dict.
172 :param strval: used when a job id has to be specified.
173 :param next_page: used to tell OAR to send the next page for this
174 Get request. Is appended to the GET uri.
175 :param username: used when a username has to be specified, when looking
176 for jobs scheduled by a particular user for instance.
178 :type request: string
179 :type strval: integer
180 :type next_page: boolean
181 :type username: string
182 :returns: a json dictionary if OAR successfully processed the GET
185 .. seealso:: OARrequests_uri_dict
187 self.oarserver['uri'] = \
188 OARGETParser.OARrequests_uri_dict[request]['uri']
189 #Get job details with username
190 if 'owner' in OARGETParser.OARrequests_uri_dict[request] and username:
191 self.oarserver['uri'] += \
192 OARGETParser.OARrequests_uri_dict[request]['owner'] + username
194 data = json.dumps({})
195 logger.debug("OARrestapi \tGETRequestToOARRestAPI %s" % (request))
197 self.oarserver['uri'] = self.oarserver['uri'].\
198 replace("id", str(strval))
201 self.oarserver['uri'] += next_page
204 headers['X-REMOTE_IDENT'] = username
206 logger.debug("OARrestapi: \t GETRequestToOARRestAPI \
207 self.oarserver['uri'] %s strval %s"
208 % (self.oarserver['uri'], strval))
210 #seems that it does not work if we don't add this
211 headers['content-length'] = '0'
213 conn = HTTPConnection(self.oarserver['ip'],
214 self.oarserver['port'])
215 conn.request("GET", self.oarserver['uri'], data, headers)
216 resp = conn.getresponse()
218 except Exception as error:
219 logger.log_exc("GET_OAR_SRVR : Connection error: %s "
221 raise Exception ("GET_OAR_SRVR : Connection error %s " %(error))
226 # except HTTPException, error:
227 # logger.log_exc("GET_OAR_SRVR : Problem with OAR server : %s "
229 #raise ServerError("GET_OAR_SRVR : Could not reach OARserver")
230 if resp.status >= 400:
231 raise ValueError ("Response Error %s, %s, %s" %(resp.status,
232 resp.reason, resp.read()))
234 js_dict = json.loads(body)
235 #print "\r\n \t\t\t js_dict keys" , js_dict.keys(), " \r\n", js_dict
238 except ValueError, error:
239 logger.log_exc("Failed to parse Server Response: %s ERROR %s"
241 #raise ServerError("Failed to parse Server Response:" + js)
244 def POSTRequestToOARRestAPI(self, request, datadict, username=None):
245 """ Used to post a job on OAR , along with data associated
250 #first check that all params for are OK
252 self.oarserver['uri'] = \
253 self.OAR_REQUEST_POST_URI_DICT[request]['uri']
256 logger.log_exc("OARrestapi \tPOSTRequestToOARRestAPI request not \
259 if datadict and 'strval' in datadict:
260 self.oarserver['uri'] = self.oarserver['uri'].replace("id", \
261 str(datadict['strval']))
262 del datadict['strval']
264 logger.debug("JORDAN USERNAME FOR OAR QUERY: %r to server %r" % (username, self.oarserver))
265 data = json.dumps(datadict)
266 headers = {'X-REMOTE_IDENT':username, \
267 'content-type': self.POST_FORMAT['json']['content'], \
268 'content-length':str(len(data))}
271 conn = HTTPConnection(self.oarserver['ip'], \
272 self.oarserver['port'])
273 conn.request("POST", self.oarserver['uri'], data, headers)
274 resp = conn.getresponse()
278 logger.log_exc("POSTRequestToOARRestAPI NotConnected ERROR: \
279 data %s \r\n \t\n \t\t headers %s uri %s" \
280 %(data,headers,self.oarserver['uri']))
281 except Exception as error:
282 logger.log_exc("POST_OAR_SERVER : Connection error: %s "
284 raise Exception ("POST_OAR_SERVER : Connection error %s " %(error))
289 if resp.status >= 400:
290 raise ValueError ("Response Error %s, %s: %r" %(resp.status,
295 answer = json.loads(body)
296 logger.debug("POSTRequestToOARRestAPI : answer %s" % (answer))
299 except ValueError, error:
300 logger.log_exc("Failed to parse Server Response: error %s \
302 #raise ServerError("Failed to parse Server Response:" + answer)
305 class ParsingResourcesFull():
307 Class dedicated to parse the json response from a GET_resources_full from
313 Set the parsing dictionary. Works like a switch case, if the key is
314 found in the dictionary, then the associated function is called.
315 This is used in ParseNodes to create an usable dictionary from
316 the Json returned by OAR when issuing a GET resources full request.
318 .. seealso:: ParseNodes
321 self.resources_fulljson_dict = {
322 'network_address': self.AddNodeNetworkAddr,
323 'site': self.AddNodeSite,
324 # 'radio': self.AddNodeRadio,
325 'mobile': self.AddMobility,
329 'archi': self.AddHardwareType,
330 'state': self.AddBootState,
331 'id': self.AddOarNodeId,
332 'mobility_type': self.AddMobilityType,
337 def AddOarNodeId(self, tuplelist, value):
338 """Adds Oar internal node id to the nodes' attributes.
340 Appends tuple ('oar_id', node_id) to the tuplelist. Used by ParseNodes.
342 .. seealso:: ParseNodes
346 tuplelist.append(('oar_id', int(value)))
349 def AddNodeNetworkAddr(self, dictnode, value):
350 """First parsing function to be called to parse the json returned by OAR
351 answering a GET_resources (/oarapi/resources.json) request.
353 When a new node is found in the json, this function is responsible for
354 creating a new entry in the dictionary for storing information on this
355 specific node. The key is the node network address, which is also the
357 The value associated with the key is a tuple list.It contains all
358 the nodes attributes. The tuplelist will later be turned into a dict.
360 :param dictnode: should be set to the OARGETParser atribute
361 node_dictlist. It will store the information on the nodes.
362 :param value: the node_id is the network_address in the raw json.
364 :type dictnode: dictionary
366 .. seealso: ParseResources, ParseNodes
370 dictnode[node_id] = [('node_id', node_id),('hostname', node_id) ]
374 def AddNodeSite(self, tuplelist, value):
375 """Add the site's node to the dictionary.
378 :param tuplelist: tuple list on which to add the node's site.
379 Contains the other node attributes as well.
380 :param value: value to add to the tuple list, in this case the node's
382 :type tuplelist: list
385 .. seealso:: AddNodeNetworkAddr
388 tuplelist.append(('site', str(value)))
390 # def AddNodeRadio(tuplelist, value):
391 # """Add thenode's radio chipset type to the tuple list.
393 # :param tuplelist: tuple list on which to add the node's mobility
394 # status. The tuplelist is the value associated with the node's
395 # id in the OARGETParser
396 # 's dictionary node_dictlist.
397 # :param value: name of the radio chipset on the node.
398 # :type tuplelist: list
399 # :type value: string
401 # .. seealso:: AddNodeNetworkAddr
404 # tuplelist.append(('radio', str(value)))
406 def AddMobilityType(self, tuplelist, value):
407 """Adds which kind of mobility it is, train or roomba robot.
409 :param tuplelist: tuple list on which to add the node's mobility status.
410 The tuplelist is the value associated with the node's id in the
411 OARGETParser's dictionary node_dictlist.
412 :param value: tells if a node is a mobile node or not. The value is
415 :type tuplelist: list
419 tuplelist.append(('mobility_type', str(value)))
422 def AddMobility(self, tuplelist, value):
423 """Add if the node is a mobile node or not to the tuple list.
425 :param tuplelist: tuple list on which to add the node's mobility status.
426 The tuplelist is the value associated with the node's id in the
427 OARGETParser's dictionary node_dictlist.
428 :param value: tells if a node is a mobile node or not. The value is found
431 :type tuplelist: list
434 .. seealso:: AddNodeNetworkAddr
438 tuplelist.append(('mobile', 'False'))
440 tuplelist.append(('mobile', 'True'))
443 def AddPosX(self, tuplelist, value):
444 """Add the node's position on the x axis.
446 :param tuplelist: tuple list on which to add the node's position . The
447 tuplelist is the value associated with the node's id in the
448 OARGETParser's dictionary node_dictlist.
449 :param value: the position x.
451 :type tuplelist: list
454 .. seealso:: AddNodeNetworkAddr
457 tuplelist.append(('posx', value ))
461 def AddPosY(self, tuplelist, value):
462 """Add the node's position on the y axis.
464 :param tuplelist: tuple list on which to add the node's position . The
465 tuplelist is the value associated with the node's id in the
466 OARGETParser's dictionary node_dictlist.
467 :param value: the position y.
469 :type tuplelist: list
472 .. seealso:: AddNodeNetworkAddr
475 tuplelist.append(('posy', value))
479 def AddPosZ(self, tuplelist, value):
480 """Add the node's position on the z axis.
482 :param tuplelist: tuple list on which to add the node's position . The
483 tuplelist is the value associated with the node's id in the
484 OARGETParser's dictionary node_dictlist.
485 :param value: the position z.
487 :type tuplelist: list
490 .. seealso:: AddNodeNetworkAddr
494 tuplelist.append(('posz', value))
498 def AddBootState(tself, tuplelist, value):
499 """Add the node's state, Alive or Suspected.
501 :param tuplelist: tuple list on which to add the node's state . The
502 tuplelist is the value associated with the node's id in the
503 OARGETParser 's dictionary node_dictlist.
504 :param value: node's state.
506 :type tuplelist: list
509 .. seealso:: AddNodeNetworkAddr
512 tuplelist.append(('boot_state', str(value)))
515 def AddHardwareType(self, tuplelist, value):
516 """Add the node's hardware model and radio chipset type to the tuple
519 :param tuplelist: tuple list on which to add the node's architecture
520 and radio chipset type.
521 :param value: hardware type: radio chipset. The value contains both the
522 architecture and the radio chipset, separated by a colon.
523 :type tuplelist: list
526 .. seealso:: AddNodeNetworkAddr
530 value_list = value.split(':')
531 tuplelist.append(('archi', value_list[0]))
532 tuplelist.append(('radio', value_list[1]))
536 """Class providing parsing methods associated to specific GET requests.
540 def __init__(self, srv):
541 self.version_json_dict = {
542 'api_version': None, 'apilib_version': None,
543 'api_timezone': None, 'api_timestamp': None, 'oar_version': None}
544 self.config = Config()
545 self.interface_hrn = self.config.SFA_INTERFACE_HRN
546 self.timezone_json_dict = {
547 'timezone': None, 'api_timestamp': None, }
548 #self.jobs_json_dict = {
549 #'total' : None, 'links' : [],\
550 #'offset':None , 'items' : [], }
551 #self.jobs_table_json_dict = self.jobs_json_dict
552 #self.jobs_details_json_dict = self.jobs_json_dict
554 self.node_dictlist = {}
556 self.json_page = JsonPage()
557 self.parsing_resourcesfull = ParsingResourcesFull()
560 self.SendRequest("GET_version")
563 def ParseVersion(self):
564 """Parses the OAR answer to the GET_version ( /oarapi/version.json.)
566 Finds the OAR apilib version currently used. Has an impact on the json
567 structure returned by OAR, so the version has to be known before trying
568 to parse the jsons returned after a get request has been issued.
569 Updates the attribute version_json_dict.
573 if 'oar_version' in self.json_page.raw_json:
574 self.version_json_dict.update(
575 api_version=self.json_page.raw_json['api_version'],
576 apilib_version=self.json_page.raw_json['apilib_version'],
577 api_timezone=self.json_page.raw_json['api_timezone'],
578 api_timestamp=self.json_page.raw_json['api_timestamp'],
579 oar_version=self.json_page.raw_json['oar_version'])
581 self.version_json_dict.update(
582 api_version=self.json_page.raw_json['api'],
583 apilib_version=self.json_page.raw_json['apilib'],
584 api_timezone=self.json_page.raw_json['api_timezone'],
585 api_timestamp=self.json_page.raw_json['api_timestamp'],
586 oar_version=self.json_page.raw_json['oar'])
588 print self.version_json_dict['apilib_version']
591 def ParseTimezone(self):
592 """Get the timezone used by OAR.
594 Get the timezone from the answer to the GET_timezone request.
595 :return: api_timestamp and api timezone.
596 :rype: integer, integer
600 api_timestamp = self.json_page.raw_json['api_timestamp']
601 api_tz = self.json_page.raw_json['timezone']
602 return api_timestamp, api_tz
605 """Called when a GET_jobs request has been issued to OAR.
607 Corresponds to /oarapi/jobs.json uri. Currently returns the raw json
609 :returns: json_page.raw_json
612 .. warning:: Does not actually parse the information in the json. SA
618 return self.json_page.raw_json
620 def ParseJobsTable(self):
621 """In case we need to use the job table in the future.
623 Associated with the GET_jobs_table : '/oarapi/jobs/table.json uri.
624 .. warning:: NOT USED. DOES NOTHING.
626 print "ParseJobsTable"
628 def ParseJobsDetails(self):
629 """Currently only returns the same json in self.json_page.raw_json.
631 .. todo:: actually parse the json
632 .. warning:: currently, this function is not used a lot, so I have no
633 idea what could be useful to parse, returning the full json. NT
636 #logger.debug("ParseJobsDetails %s " %(self.json_page.raw_json))
637 return self.json_page.raw_json
640 def ParseJobsIds(self):
641 """Associated with the GET_jobs_id OAR request.
643 Parses the json dict (OAR answer) to the GET_jobs_id request
644 /oarapi/jobs/id.json.
647 :returns: dictionary whose keys are listed in the local variable
648 job_resources and values that are in the json dictionary returned
649 by OAR with the job information.
653 job_resources = ['wanted_resources', 'name', 'id', 'start_time',
654 'state', 'owner', 'walltime', 'message']
656 # Unused variable providing the contents of the json dict returned from
657 # get job resources full request
658 job_resources_full = [
659 'launching_directory', 'links',
660 'resubmit_job_id', 'owner', 'events', 'message',
661 'scheduled_start', 'id', 'array_id', 'exit_code',
662 'properties', 'state', 'array_index', 'walltime',
663 'type', 'initial_request', 'stop_time', 'project',
664 'start_time', 'dependencies', 'api_timestamp', 'submission_time',
665 'reservation', 'stdout_file', 'types', 'cpuset_name',
666 'name', 'wanted_resources', 'queue', 'stderr_file', 'command']
669 job_info = self.json_page.raw_json
670 #logger.debug("OARESTAPI ParseJobsIds %s" %(self.json_page.raw_json))
673 for k in job_resources:
674 values.append(job_info[k])
675 return dict(zip(job_resources, values))
678 logger.log_exc("ParseJobsIds KeyError ")
681 def ParseJobsIdResources(self):
682 """ Parses the json produced by the request
683 /oarapi/jobs/id/resources.json.
684 Returns a list of oar node ids that are scheduled for the
689 for resource in self.json_page.raw_json['items']:
690 job_resources.append(resource['id'])
694 def ParseResources(self):
695 """ Parses the json produced by a get_resources request on oar."""
697 #logger.debug("OARESTAPI \tParseResources " )
698 #resources are listed inside the 'items' list from the json
699 self.json_page.raw_json = self.json_page.raw_json['items']
702 def ParseReservedNodes(self):
703 """ Returns an array containing the list of the jobs scheduled
704 with the reserved nodes if available.
706 :returns: list of job dicts, each dict containing the following keys:
707 t_from, t_until, resources_ids (of the reserved nodes for this job).
708 If the information is not available, default values will be set for
709 these keys. The other keys are : state, lease_id and user.
714 #resources are listed inside the 'items' list from the json
715 reservation_list = []
717 #Parse resources info
718 for json_element in self.json_page.raw_json['items']:
719 #In case it is a real reservation (not asap case)
720 if json_element['scheduled_start']:
721 job['t_from'] = json_element['scheduled_start']
722 job['t_until'] = int(json_element['scheduled_start']) + \
723 int(json_element['walltime'])
724 #Get resources id list for the job
725 job['resource_ids'] = [node_dict['id'] for node_dict
726 in json_element['resources']]
728 job['t_from'] = "As soon as possible"
729 job['t_until'] = "As soon as possible"
730 job['resource_ids'] = ["Undefined"]
732 job['state'] = json_element['state']
733 job['lease_id'] = json_element['id']
735 job['user'] = json_element['owner']
736 #logger.debug("OARRestapi \tParseReservedNodes job %s" %(job))
737 reservation_list.append(job)
740 return reservation_list
742 def ParseRunningJobs(self):
743 """ Gets the list of nodes currently in use from the attributes of the
746 :returns: list of hostnames, the nodes that are currently involved in
752 logger.debug("OARESTAPI \tParseRunningJobs_________________ ")
753 #resources are listed inside the 'items' list from the json
755 for job in self.json_page.raw_json['items']:
756 for node in job['nodes']:
757 nodes.append(node['network_address'])
760 def ChangeRawJsonDependingOnApilibVersion(self):
762 Check if the OAR apilib version is different from 0.2.10, in which case
763 the Json answer is also dict instead as a plain list.
765 .. warning:: the whole code is assuming the json contains a 'items' key
766 .. seealso:: ConcatenateJsonPages, ParseJobs, ParseReservedNodes,
767 ParseJobsIdResources, ParseResources, ParseRunningJobs
768 .. todo:: Clean the whole code. Either suppose the apilib will always
769 provide the 'items' key, or handle different options.
772 if self.version_json_dict['apilib_version'] != "0.2.10":
773 self.json_page.raw_json = self.json_page.raw_json['items']
775 def ParseDeleteJobs(self):
776 """ No need to parse anything in this function.A POST
777 is done to delete the job.
782 def ParseResourcesFull(self):
783 """ This method is responsible for parsing all the attributes
784 of all the nodes returned by OAR when issuing a get resources full.
785 The information from the nodes and the sites are separated.
786 Updates the node_dictlist so that the dictionnary of the platform's
787 nodes is available afterwards.
789 :returns: node_dictlist, a list of dictionaries about the nodes and
794 logger.debug("OARRESTAPI ParseResourcesFull___________ ")
795 #print self.json_page.raw_json[1]
796 #resources are listed inside the 'items' list from the json
797 self.ChangeRawJsonDependingOnApilibVersion()
800 return self.node_dictlist
802 def ParseResourcesFullSites(self):
803 """ Called by GetSites which is unused.
804 Originally used to get information from the sites, with for each site
805 the list of nodes it has, along with their properties.
807 :return: site_dict, dictionary of sites
811 .. seealso:: GetSites (IotlabShell)
814 self.ChangeRawJsonDependingOnApilibVersion()
817 return self.site_dict
820 def ParseNodes(self):
821 """ Parse nodes properties from OAR
822 Put them into a dictionary with key = node id and value is a dictionary
823 of the node properties and properties'values.
827 _resources_fulljson_dict = \
828 self.parsing_resourcesfull.resources_fulljson_dict
829 keys = _resources_fulljson_dict.keys()
832 for dictline in self.json_page.raw_json:
834 # dictionary is empty and/or a new node has to be inserted
835 node_id = _resources_fulljson_dict['network_address'](
836 self.node_dictlist, dictline['network_address'])
839 if k == 'network_address':
842 _resources_fulljson_dict[k](
843 self.node_dictlist[node_id], dictline[k])
845 #The last property has been inserted in the property tuple list,
847 #Turn the property tuple list (=dict value) into a dictionary
848 self.node_dictlist[node_id] = dict(self.node_dictlist[node_id])
852 def iotlab_hostname_to_hrn(root_auth, hostname):
854 Transforms a node hostname into a SFA hrn.
856 :param root_auth: Name of the root authority of the SFA server. In
857 our case, it is set to iotlab.
858 :param hostname: node's hotname, given by OAR.
859 :type root_auth: string
860 :type hostname: string
861 :returns: inserts the root_auth and '.' before the hostname.
865 return root_auth + '.' + hostname
867 def ParseSites(self):
868 """ Returns a list of dictionnaries containing the sites' attributes."""
872 #logger.debug(" OARrestapi.py \tParseSites self.node_dictlist %s"\
873 #%(self.node_dictlist))
874 # Create a list of nodes per site_id
875 for node_id in self.node_dictlist:
876 node = self.node_dictlist[node_id]
878 if node['site'] not in nodes_per_site:
879 nodes_per_site[node['site']] = []
880 nodes_per_site[node['site']].append(node['node_id'])
882 if node['node_id'] not in nodes_per_site[node['site']]:
883 nodes_per_site[node['site']].append(node['node_id'])
885 #Create a site dictionary whose key is site_login_base
886 # (name of the site) and value is a dictionary of properties,
887 # including the list of the node_ids
888 for node_id in self.node_dictlist:
889 node = self.node_dictlist[node_id]
890 node.update({'hrn': self.iotlab_hostname_to_hrn(self.interface_hrn,
892 self.node_dictlist.update({node_id: node})
894 if node['site'] not in self.site_dict:
895 self.site_dict[node['site']] = {
896 'site': node['site'],
897 'node_ids': nodes_per_site[node['site']],
898 'latitude': "48.83726",
899 'longitude': "- 2.10336",
900 'name': config.SFA_REGISTRY_ROOT_AUTH,
901 'pcu_ids': [], 'max_slices': None,
902 'ext_consortium_id': None,
903 'max_slivers': None, 'is_public': True,
904 'peer_site_id': None,
905 'abbreviated_name': "iotlab", 'address_ids': [],
906 'url': "https://portal.senslab.info", 'person_ids': [],
907 'site_tag_ids': [], 'enabled': True, 'slice_ids': [],
908 'date_created': None, 'peer_id': None
911 OARrequests_uri_dict = {
913 {'uri': '/oarapi/version.json', 'parse_func': ParseVersion},
916 {'uri': '/oarapi/timezone.json', 'parse_func': ParseTimezone},
919 {'uri': '/oarapi/jobs.json', 'parse_func': ParseJobs},
922 {'uri': '/oarapi/jobs/id.json', 'parse_func': ParseJobsIds},
924 'GET_jobs_id_resources':
925 {'uri': '/oarapi/jobs/id/resources.json',
926 'parse_func': ParseJobsIdResources},
929 {'uri': '/oarapi/jobs/table.json', 'parse_func': ParseJobsTable},
932 {'uri': '/oarapi/jobs/details.json', 'parse_func': ParseJobsDetails},
934 'GET_reserved_nodes':
936 '/oarapi/jobs/details.json?state=Running,Waiting,Launching',
937 'owner': '&user=', 'parse_func': ParseReservedNodes},
940 {'uri': '/oarapi/jobs/details.json?state=Running',
941 'parse_func': ParseRunningJobs},
943 'GET_resources_full':
944 {'uri': '/oarapi/resources/full.json',
945 'parse_func': ParseResourcesFull},
948 {'uri': '/oarapi/resources/full.json',
949 'parse_func': ParseResourcesFullSites},
952 {'uri': '/oarapi/resources.json', 'parse_func': ParseResources},
955 {'uri': '/oarapi/jobs/id.json', 'parse_func': ParseDeleteJobs}}
958 def SendRequest(self, request, strval=None, username=None):
959 """ Connects to OAR , sends the valid GET requests and uses
960 the appropriate json parsing functions.
962 :returns: calls to the appropriate parsing function, associated with the
964 :rtype: depends on the parsing function called.
966 .. seealso:: OARrequests_uri_dict
970 self.json_page.ResetNextPage()
973 if request in self.OARrequests_uri_dict:
974 while self.json_page.next_page:
975 self.json_page.raw_json = self.server.GETRequestToOARRestAPI(
978 self.json_page.next_offset,
980 self.json_page.FindNextPage()
981 if self.json_page.concatenate:
982 save_json.append(self.json_page.raw_json)
984 if self.json_page.concatenate and self.json_page.end:
985 self.json_page.raw_json = \
986 self.json_page.ConcatenateJsonPages(save_json)
988 return self.OARrequests_uri_dict[request]['parse_func'](self)
990 logger.error("OARRESTAPI OARGetParse __init__ : ERROR_REQUEST "