Advertisement
Guest User

Untitled

a guest
Apr 28th, 2016
128
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 17.03 KB | None | 0 0
  1. # Copyright 2016 Hewlett Packard Enterprise Development, LP.
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License"); you may
  4. # not use this file except in compliance with the License. You may obtain
  5. # a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  11. # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  12. # License for the specific language governing permissions and limitations
  13. # under the License.
  14.  
  15.  
  16. """
  17. Provides examples of using the HP RESTful API on iLO for common use cases. This is for tutorial/example purposes only.
  18. ---------------------------------------------------------------------------------------------------------------------
  19. IMPORTANT!!!
  20. ---------------------------------------------------------------------------------------------------------------------
  21. When developing a client for the HP RESTful API, be sure to not code based upon assumptions that are not guaranteed.
  22. Search for, and note any 'NOTE' comments in this code to read about ways to avoid incorrect assumptions.
  23. The reason avoiding these assumptions is so important is that implementations may vary across systems and firmware
  24. versions, and we want your code to work consistently.
  25. ---------------------------------------------------------------------------------------------------------------------
  26. STARTING ASSUMPTIONS
  27. ---------------------------------------------------------------------------------------------------------------------
  28. On URIs:
  29. The HP RESTful API is a "hypermedia API" by design. This is to avoid building in restrictive assumptions to the
  30. data model that will make it difficult to adapt to future hardware implementations. A hypermedia API avoids these
  31. assumptions by making the data model discoverable via links between resources.
  32. A URI should be treated by the client as opaque, and thus should not be attempted to be understood or deconstructed
  33. by the client. Only specific top level URIs (any URI in this sample code) may be assumed, and even these may be
  34. absent based upon the implementation (e.g. there might be no /rest/v1/Systems collection on something that doesn't
  35. have compute nodes.)
  36. The other URIs must be discovered dynamically by following href links. This is because the API will eventually be
  37. implemented on a system that breaks any existing data model "shape" assumptions we may make now. In particular,
  38. clients should not make assumptions about the URIs for the resource members of a collection. For instance, the URI of
  39. a collection member will NOT always be /rest/v1/.../collection/1, or 2. On Moonshot a System collection member might be
  40. /rest/v1/Systems/C1N1.
  41. This sounds very complicated, but in reality (as these examples demonstrate), if you are looking for specific items,
  42. the traversal logic isn't too complicated.
  43. On Resource Model Traversal:
  44. Although the resources in the data model are linked together, because of cross link references between resources,
  45. a client may not assume the resource model is a tree. It is a graph instead, so any crawl of the data model should
  46. keep track of visited resources to avoid an infinite traversal loop.
  47. A reference to another resource is any property called "href" no matter where it occurs in a resource.
  48. An external reference to a resource outside the data model is referred to by a property called "extref". Any
  49. resource referred to by extref should not be assumed to follow the conventions of the API.
  50. On Resource Versions:
  51. Each resource has a "Type" property with a value of the format Tyepname.x.y.z where
  52. * x = major version - incrementing this is a breaking change to the schema
  53. * y = minor version - incrementing this is a non-breaking additive change to the schema
  54. * z = errata - non-breaking change
  55. Because all resources are versioned and schema also have a version, it is possible to design rules for "nearest"
  56. match (e.g. if you are interacting with multiple services using a common batch of schema files). The mechanism
  57. is not prescribed, but a client should be prepared to encounter both older and newer versions of resource types.
  58. On HTTP POST to create:
  59. WHen POSTing to create a resource (e.g. create an account or session) the guarantee is that a successful response
  60. includes a "Location" HTTP header indicating the resource URI of the newly created resource. The POST may also
  61. include a representation of the newly created object in a JSON response body but may not. Do not assume the response
  62. body, but test it. It may also be an ExtendedError object.
  63. HTTP REDIRECT:
  64. All clients must correctly handle HTTP redirect. We (or Redfish) may eventually need to use redirection as a way
  65. to alias portions of the data model.
  66. FUTURE: Asynchronous tasks
  67. In the future some operations may start asynchonous tasks. In this case, the client should recognized and handle
  68. HTTP 202 if needed and the 'Location' header will point to a resource with task information and status.
  69. JSON-SCHEMA:
  70. The json-schema available at /rest/v1/Schemas governs the content of the resources, but keep in mind:
  71. * not every property in the schema is implemented in every implementation.
  72. * some properties are schemed to allow both null and anotehr type like string or integer.
  73. Robust client code should check both the existence and type of interesting properties and fail gracefully if
  74. expectations are not met.
  75. GENERAL ADVICE:
  76. Clients should always be prepared for:
  77. * unimplemented properties (e.g. a property doesn't apply in a particular case)
  78. * null values in some cases if the value of a property is not currently known due to system conditions
  79. * HTTP status codes other than 200 OK. Can your code handle an HTTP 500 Internal Server Error with no other info?
  80. * URIs are case insensitive
  81. * HTTP header names are case insensitive
  82. * JSON Properties and Enum values are case sensitive
  83. * A client should be tolerant of any set of HTTP headers the service returns
  84. """
  85.  
  86. import sys
  87. import json
  88. import logging
  89. import urlparse
  90. import jsonpatch
  91.  
  92. from ilorest import AuthMethod, ilorest_logger, rest_client
  93.  
  94.  
  95. #Config logger used by HPE Restful library
  96. LOGGERFILE = "RestfulApiExamples.log"
  97. LOGGERFORMAT = "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
  98. LOGGER = ilorest_logger(LOGGERFILE, LOGGERFORMAT, logging.INFO)
  99. LOGGER.info("HPE Restful API examples")
  100.  
  101.  
  102. def ex1_get_resource_directory(restobj):
  103. sys.stdout.write("\nEXAMPLE 1: Find and store the resource directory " + "\n")
  104. response = restobj.rest_get("/rest/v1/resourcedirectory")
  105. resources = {}
  106.  
  107. if response.status == 200:
  108. sys.stdout.write("\tFound resource directory at /rest/v1/resource" \
  109. "directory" + "\n")
  110. resources["resources"] = response.dict["Instances"]
  111. return resources
  112. else:
  113. sys.stderr.write("\tResource directory missing at /rest/v1/resource" \
  114. "directory" + "\n")
  115.  
  116. def ex2_get_base_registry(restobj):
  117. sys.stdout.write("\nEXAMPLE 2: Find and return registry " + "\n")
  118. response = restobj.rest_get("/rest/v1/Registries")
  119. messages = {}
  120.  
  121. identifier = None
  122.  
  123. for entry in response.dict["Items"]:
  124. if "Id" in entry:
  125. identifier = entry["Id"]
  126. else:
  127. identifier = entry["Schema"].split(".")[0]
  128.  
  129. if identifier not in ["Base", "iLO"]:
  130. continue
  131.  
  132. for location in entry["Location"]:
  133. reg_resp = restobj.rest_get(location["Uri"]["extref"])
  134.  
  135. if reg_resp.status == 200:
  136. sys.stdout.write("\tFound " + identifier + " at " + \
  137. location["Uri"]["extref"] + "\n")
  138. messages[identifier] = reg_resp.dict["Messages"]
  139. else:
  140. sys.stdout.write("\t" + identifier + " not found at "\
  141. + location["Uri"]["extref"] + "\n")
  142.  
  143. return messages
  144.  
  145.  
  146. def ex3_change_bios_setting(restobj, bios_property, property_value, \
  147. bios_password=None):
  148. sys.stdout.write("\nEXAMPLE 3: Change a BIOS setting\n")
  149. instances = restobj.search_for_type("Bios.")
  150.  
  151. for instance in instances:
  152. body = {bios_property: property_value}
  153. response = restobj.rest_patch(instance["href"], body, \
  154. optionalpassword=bios_password)
  155. restobj.error_handler(response)
  156.  
  157. def ex4_reset_server(restobj, bios_password=None):
  158. sys.stdout.write("\nEXAMPLE 4: Reset a server\n")
  159. instances = restobj.search_for_type("ComputerSystem.")
  160.  
  161. for instance in instances:
  162. body = dict()
  163. body["Action"] = "Reset"
  164. body["ResetType"] = "ForceRestart"
  165.  
  166. response = restobj.rest_post(instance["href"], body)
  167. restobj.error_handler(response)
  168.  
  169.  
  170.  
  171. def ex8_change_temporary_boot_order(restobj, boottarget, bios_password=None):
  172. sys.stdout.write("\nEXAMPLE 8: Change temporary boot order (one time boot" \
  173. " or temporary override)\n")
  174. instances = restobj.search_for_type("ComputerSystem.")
  175.  
  176. for instance in instances:
  177. response = restobj.rest_get(instance["href"])
  178. bootoptions = response.dict["Boot"]
  179.  
  180. if boottarget not in bootoptions["BootSourceOverrideSupported"]:
  181. sys.stderr.write("ERROR: %s is not a supported boot option.\n" \
  182. % boottarget)
  183.  
  184. body = dict()
  185. body["Boot"] = dict()
  186. body["Boot"]["BootSourceOverrideTarget"] = boottarget
  187.  
  188. response = restobj.rest_patch(instance["href"], body, \
  189. optionalpassword=bios_password)
  190. restobj.error_handler(response)
  191.  
  192. def ex10_add_ilo_user_account(restobj, new_ilo_loginname, new_ilo_username, \
  193. new_ilo_password, irc=None, cfg=None, \
  194. virtual_media=None, usercfg=None, vpr=None):
  195. sys.stdout.write("\nEXAMPLE 10: Create an iLO User Account\n")
  196. instances = restobj.search_for_type("AccountService.")
  197.  
  198. for instance in instances:
  199. rsp = restobj.rest_get(instance["href"])
  200.  
  201. body = {"UserName": new_ilo_loginname, "Password": \
  202. new_ilo_password, "Oem": {}}
  203. body["Oem"]["Hp"] = {}
  204. body["Oem"]["Hp"]["LoginName"] = new_ilo_username
  205. body["Oem"]["Hp"]["Privileges"] = {}
  206. body["Oem"]["Hp"]["Privileges"]["RemoteConsolePriv"] = irc
  207. body["Oem"]["Hp"]["Privileges"]["iLOConfigPriv"] = cfg
  208. body["Oem"]["Hp"]["Privileges"]["VirtualMediaPriv"] = virtual_media
  209. body["Oem"]["Hp"]["Privileges"]["UserConfigPriv"] = usercfg
  210. body["Oem"]["Hp"]["Privileges"]["VirtualPowerAndResetPriv"] = vpr
  211.  
  212. newrsp = restobj.rest_post(rsp.dict["links"]["Accounts"]["href"], body)
  213. restobj.error_handler(newrsp)
  214.  
  215. def ex12_remove_ilo_account(restobj, ilo_loginname_to_remove):
  216. sys.stdout.write("\nEXAMPLE 12: Remove an iLO account\n")
  217. instances = restobj.search_for_type("AccountService.")
  218.  
  219. for instance in instances:
  220. response = restobj.rest_get(instance["href"])
  221. accounts = restobj.rest_get(response.dict["links"]["Accounts"]["href"])
  222.  
  223. for account in accounts.dict["Items"]:
  224. if account["UserName"] == ilo_loginname_to_remove:
  225. newrsp = restobj.rest_delete(account["links"]["self"]["href"])
  226. restobj.error_handler(newrsp)
  227. return
  228.  
  229. sys.stderr.write("Account not found\n")
  230.  
  231. class RestObject(object):
  232. def __init__(self, host, login_account, login_password):
  233. self.rest_client = rest_client(base_url=host, \
  234. username=login_account, password=login_password, \
  235. default_prefix="/rest/v1")
  236. self.rest_client.login(auth=AuthMethod.SESSION)
  237. self.SYSTEMS_RESOURCES = ex1_get_resource_directory(self)
  238. self.MESSAGE_REGISTRIES = ex2_get_base_registry(self)
  239.  
  240. def __del__(self):
  241. self.rest_client.logout()
  242.  
  243. def search_for_type(self, type):
  244. instances = []
  245.  
  246. for item in self.SYSTEMS_RESOURCES["resources"]:
  247. foundsettings = False
  248.  
  249. if type.lower() in item["Type"].lower():
  250. for entry in self.SYSTEMS_RESOURCES["resources"]:
  251. if (item["href"] + "/settings").lower() == \
  252. (entry["href"]).lower():
  253. foundsettings = True
  254.  
  255. if not foundsettings:
  256. instances.append(item)
  257.  
  258. if not instances:
  259. sys.stderr.write("\t'%s' resource or feature is not " \
  260. "supported on this system\n" % type)
  261. return instances
  262.  
  263. def error_handler(self, response):
  264. if not self.MESSAGE_REGISTRIES:
  265. sys.stderr.write("ERROR: No message registries found.")
  266.  
  267. try:
  268. message = json.loads(response.text)
  269. newmessage = message["Messages"][0]["MessageID"].split(".")
  270. except:
  271. sys.stdout.write("\tNo extended error information returned by " \
  272. "iLO.\n")
  273. return
  274.  
  275. for err_mesg in self.MESSAGE_REGISTRIES:
  276. if err_mesg != newmessage[0]:
  277. continue
  278. else:
  279. for err_entry in self.MESSAGE_REGISTRIES[err_mesg]:
  280. if err_entry == newmessage[3]:
  281. sys.stdout.write("\tiLO return code %s: %s\n" % (\
  282. message["Messages"][0]["MessageID"], \
  283. self.MESSAGE_REGISTRIES[err_mesg][err_entry]\
  284. ["Description"]))
  285.  
  286. def rest_get(self, suburi):
  287. """REST GET"""
  288. return self.rest_client.get(path=suburi)
  289.  
  290. def rest_patch(self, suburi, request_body, optionalpassword=None):
  291. """REST PATCH"""
  292. sys.stdout.write("PATCH " + str(request_body) + " to " + suburi + "\n")
  293. response = self.rest_client.patch(path=suburi, body=request_body, \
  294. optionalpassword=optionalpassword)
  295. sys.stdout.write("PATCH response = " + str(response.status) + "\n")
  296.  
  297. return response
  298.  
  299. def rest_put(self, suburi, request_body, optionalpassword=None):
  300. """REST PUT"""
  301. sys.stdout.write("PUT " + str(request_body) + " to " + suburi + "\n")
  302. response = self.rest_client.put(path=suburi, body=request_body, \
  303. optionalpassword=optionalpassword)
  304. sys.stdout.write("PUT response = " + str(response.status) + "\n")
  305.  
  306. return response
  307.  
  308.  
  309. def rest_post(self, suburi, request_body):
  310. """REST POST"""
  311. sys.stdout.write("POST " + str(request_body) + " to " + suburi + "\n")
  312. response = self.rest_client.post(path=suburi, body=request_body)
  313. sys.stdout.write("POST response = " + str(response.status) + "\n")
  314.  
  315. return response
  316.  
  317.  
  318. def rest_delete(self, suburi):
  319. """REST DELETE"""
  320. sys.stdout.write("DELETE " + suburi + "\n")
  321. response = self.rest_client.delete(path=suburi)
  322. sys.stdout.write("DELETE response = " + str(response.status) + "\n")
  323.  
  324. return response
  325.  
  326. if __name__ == "__main__":
  327. # When running on the server locally use the following commented values
  328. # iLO_host = "blobstore://."
  329. # iLO_account = "None"
  330. # iLO_password = "None"
  331.  
  332. # When running remotely connect using the iLO address, iLO account name,
  333. # and password to send https requests
  334. #iLO_host = "https://16.83.16.43"
  335. #login_account = "admin"
  336. #login_password = "password"
  337.  
  338. #accepts arguments when run,
  339. #RestfulApiExamples.py https://xx.xx.xx.xx username password
  340. try:
  341. iLO_host = "https://" + str(sys.argv[1])
  342. login_account = str(sys.argv[2])
  343. login_password = str(sys.argv[3])
  344. except Exception:
  345. sys.stderr.write("Credentials Error\n")
  346. # Create a REST object
  347. try:
  348. REST_OBJ = RestObject(iLO_host, login_account, login_password)
  349. except Exception:
  350. sys.stderr.write("Error with credentials \n")
  351.  
  352. # These examples are comment out because they are now part of the RestObject
  353. # class. They are initiated when you create a RestObject class object.
  354. #ex1_get_resource_directory(REST_OBJ)
  355. #ex2_get_base_registry(REST_OBJ)
  356.  
  357. # Uncomment the examples you would like to execute
  358. ex8_change_temporary_boot_order(REST_OBJ, "Hdd")
  359. ex10_add_ilo_user_account(REST_OBJ, "name", "username", "password")
  360. #ex12_remove_ilo_account(REST_OBJ, "name")
  361. #ex4_reset_server(REST_OBJ)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement