A declarative host provisioning system.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

178 lines
6.8 KiB

  1. # Copyright 2018 AT&T Intellectual Property. All other rights reserved.
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain 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,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. """Redfish object to provide commands.
  15. Uses Redfish client to communicate to node.
  16. """
  17. from redfish import AuthMethod, redfish_client
  18. from redfish.rest.v1 import ServerDownOrUnreachableError
  19. from redfish.rest.v1 import InvalidCredentialsError
  20. from redfish.rest.v1 import RetriesExhaustedError
  21. class RedfishSession(object):
  22. """Redfish Client to provide OOB commands"""
  23. def __init__(self, host, account, password, use_ssl=True, connection_retries=10):
  24. try:
  25. if use_ssl:
  26. redfish_url = 'https://' + host
  27. else:
  28. redfish_url = 'http://' + host
  29. self.redfish_client = redfish_client(base_url=redfish_url,
  30. username=account,
  31. password=password)
  32. self.redfish_client.MAX_RETRY = connection_retries
  33. self.redfish_client.login(auth=AuthMethod.SESSION)
  34. except RetriesExhaustedError:
  35. raise RedfishException("Login failed: Retries exhausted")
  36. except InvalidCredentialsError:
  37. raise RedfishException("Login failed: Invalid credentials")
  38. except ServerDownOrUnreachableError:
  39. raise RedfishException("Login failed: Server unreachable")
  40. def __del__(self):
  41. self.redfish_client.logout()
  42. def close_session(self):
  43. self.redfish_client.logout()
  44. def get_system_instance(self):
  45. response = self.redfish_client.get("/redfish/v1/Systems")
  46. if response.status != 200:
  47. raise RedfishException(response._read)
  48. # Assumption that only one system is available on Node
  49. if response.dict["Members@odata.count"] != 1:
  50. raise RedfishException("Number of systems are more than one in the node")
  51. instance = response.dict["Members"][0]["@odata.id"]
  52. return instance
  53. def get_bootdev(self):
  54. """Get current boot type information from Node.
  55. :raises: RedfishException on an error
  56. :return: dict -- response will return as dict in format of
  57. {'bootdev': bootdev}
  58. """
  59. instance = self.get_system_instance()
  60. response = self.redfish_client.get(path=instance)
  61. if response.status != 200:
  62. raise RedfishException(response._read)
  63. bootdev = response.dict["Boot"]["BootSourceOverrideTarget"]
  64. return {'bootdev': bootdev}
  65. def set_bootdev(self, bootdev, **kwargs):
  66. """Set boot type on the Node for next boot.
  67. :param bootdev: Boot source for the next boot
  68. * None - Boot from the normal boot device.
  69. * Pxe - Boot from network
  70. * Cd - Boot from CD/DVD disc
  71. * Usb - Boot from USB device specified by system BIOS
  72. * Hdd - Boot from Hard drive
  73. * BiosSetup - Boot to bios setup utility
  74. * Utilities - Boot manufacurer utlities program
  75. * UefiTarget - Boot to the UEFI Device specified in the
  76. UefiTargetBootSourceOverride property
  77. * UefiShell - Boot to the UEFI Shell
  78. * UefiHttp - Boot from UEFI HTTP network location
  79. :param **kwargs: To specify extra arguments for a given bootdev
  80. Example to specify UefiTargetBootSourceOverride value
  81. for bootdev UefiTarget
  82. :raises: RedfishException on an error
  83. :return: dict -- response will return as dict in format of
  84. {'bootdev': bootdev}
  85. """
  86. instance = self.get_system_instance()
  87. payload = {
  88. "Boot": {
  89. "BootSourceOverrideEnabled": "Once",
  90. "BootSourceOverrideTarget": bootdev,
  91. }
  92. }
  93. if bootdev == 'UefiTarget':
  94. payload['Boot']['UefiTargetBootSourceOverride'] = kwargs.get(
  95. 'UefiTargetBootSourceOverride', '')
  96. response = self.redfish_client.patch(path=instance, body=payload)
  97. if response.status != 200:
  98. raise RedfishException(response._read)
  99. return {'bootdev': bootdev}
  100. def get_power(self):
  101. """Get current power state information from Node.
  102. :raises: RedfishException on an error
  103. :return: dict -- response will return as dict in format of
  104. {'powerstate': powerstate}
  105. """
  106. instance = self.get_system_instance()
  107. response = self.redfish_client.get(path=instance)
  108. if response.status != 200:
  109. raise RedfishException(response._read)
  110. powerstate = response.dict["PowerState"]
  111. return {'powerstate': powerstate}
  112. def set_power(self, powerstate):
  113. """Request power change on the node.
  114. :param powerstate: set power change
  115. * On - Power On the unit
  116. * ForceOff - Turn off immediately (non graceful)
  117. * PushPowerButton - Simulate pressing physical
  118. power button
  119. * GracefulRestart - Perform a graceful shutdown
  120. and then start
  121. :raises: RedfishException on an error
  122. :return: dict -- response will return as dict in format of
  123. {'powerstate': powerstate}
  124. """
  125. instance = self.get_system_instance()
  126. if powerstate not in ["On", "ForceOff", "PushPowerButton", "GracefulRestart"]:
  127. raise RedfishException("Unsupported powerstate")
  128. current_state = self.get_power()
  129. if (powerstate == "On" and current_state["powerstate"] == "On") or \
  130. (powerstate == "ForceOff" and current_state["powerstate"] == "Off"):
  131. return {'powerstate': powerstate}
  132. payload = {
  133. "ResetType": powerstate
  134. }
  135. url = instance + "/Actions/ComputerSystem.Reset"
  136. response = self.redfish_client.post(path=url, body=payload)
  137. if response.status in [200, 201, 204]:
  138. return {'powerstate': powerstate}
  139. else:
  140. raise RedfishException(response._read)
  141. class RedfishException(Exception):
  142. """Redfish Exception with error in message"""
  143. pass