Caio Oliveira f4816b6837 Fixing URL of index variable and refactoring
This patch fixes the problem related to the URL of the server
 profile template, that was not the same as used in REST API of
 OneView. Also, some refactoring in the client.py module,
 methods *by_uuid, that could be simplified.

Change-Id: I943d1444c9a0315ff1061f20432d7255df75e468
Closes-Bug: #1549994
2016-02-26 16:18:00 +00:00

548 lines
20 KiB
Python

# (c) Copyright 2015 Hewlett Packard Enterprise Development LP
# Copyright 2015 Universidade Federal de Campina Grande
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import json
import time
import requests
import retrying
from oneview_client import exceptions
from oneview_client.models import ServerHardware
from oneview_client.models import ServerProfile
from oneview_client.models import ServerProfileTemplate
from oneview_client import states
SUPPORTED_ONEVIEW_VERSION = 200
WAIT_DO_REQUEST_IN_MILLISECONDS = 1000
WAIT_TASK_IN_MILLISECONDS = 1000
GET_REQUEST_TYPE = 'GET'
PUT_REQUEST_TYPE = 'PUT'
POST_REQUEST_TYPE = 'POST'
DELETE_REQUEST_TYPE = 'DELETE'
MOMENTARY_PRESS = 'MomentaryPress'
PRESS_AND_HOLD = 'PressAndHold'
SERVER_HARDWARE_PREFIX_URI = '/rest/server-hardware/'
SERVER_PROFILE_TEMPLATE_PREFIX_URI = '/rest/server-profile-templates/'
class Client(object):
def __init__(
self, manager_url, username, password,
allow_insecure_connections=False, tls_cacert_file='',
max_polling_attempts=20
):
self.manager_url = manager_url
self.username = username
self.password = password
self.allow_insecure_connections = allow_insecure_connections
self.tls_cacert_file = tls_cacert_file
self.max_polling_attempts = max_polling_attempts
self.session_id = self.get_session()
def verify_credentials(self):
return self._authenticate()
def get_session(self):
response = self._authenticate()
return response.json().get('sessionID')
def _authenticate(self):
if self.manager_url in ("", None):
raise exceptions.OneViewConnectionError(
"Can't connect to OneView: 'manager_url' configuration"
"parameter is blank")
url = '%s/rest/login-sessions' % self.manager_url
body = {
'userName': self.username,
'password': self.password
}
headers = {
'content-type': 'application/json'
}
verify_ssl = self._get_verify_connection_option()
r = requests.post(url,
data=json.dumps(body),
headers=headers,
verify=verify_ssl)
if r.status_code == 400:
raise exceptions.OneViewNotAuthorizedException()
else:
return r
def _get_verify_connection_option(self):
verify_status = False
user_cacert = self.tls_cacert_file
if self.allow_insecure_connections is False:
if not user_cacert:
verify_status = True
else:
verify_status = user_cacert
return verify_status
def verify_oneview_version(self):
if not self._is_oneview_version_compatible():
raise exceptions.IncompatibleOneViewAPIVersion(
"The version of the OneView's API is unsupported. Supported "
"version is '%s'" % SUPPORTED_ONEVIEW_VERSION)
def _is_oneview_version_compatible(self):
versions = self.get_oneview_version()
v = SUPPORTED_ONEVIEW_VERSION
min_version_compatible = versions.get("minimumVersion") <= v
max_version_compatible = versions.get("currentVersion") >= v
return min_version_compatible and max_version_compatible
def get_oneview_version(self):
url = '%s/rest/version' % self.manager_url
headers = {"Accept-Language": "en_US"}
verify_ssl = self._get_verify_connection_option()
try:
versions = requests.get(
url, headers=headers, verify=verify_ssl
).json()
return versions
except requests.RequestException as e:
raise exceptions.OneViewConnectionError(e.message)
# --- Power Driver ---
def get_node_power_state(self, node_info):
return self.get_server_hardware(node_info).power_state
def power_on(self, node_info):
if self.get_node_power_state(node_info) == states.ONEVIEW_POWER_ON:
ret = states.ONEVIEW_POWER_ON
else:
ret = self.set_node_power_state(
node_info, states.ONEVIEW_POWER_ON
)
return ret
def power_off(self, node_info):
if self.get_node_power_state(node_info) == states.ONEVIEW_POWER_OFF:
ret = states.ONEVIEW_POWER_OFF
else:
ret = self.set_node_power_state(
node_info, states.ONEVIEW_POWER_OFF, PRESS_AND_HOLD
)
return ret
def set_node_power_state(
self, node_info, state, press_type=MOMENTARY_PRESS
):
body = {'powerState': state, 'powerControl': press_type}
power_state_uri = (node_info.get('server_hardware_uri') +
'/powerState')
task = self._prepare_and_do_request(
uri=power_state_uri, body=body, request_type=PUT_REQUEST_TYPE
)
try:
self._wait_for_task_to_complete(task)
except exceptions.OneViewTaskError as e:
raise exceptions.OneViewErrorStateSettingPowerState(e.message)
current_state = self.get_node_power_state(node_info)
if current_state is states.ONEVIEW_ERROR:
message = (
"Error setting node power state to %(state)s" %
{"state": state}
)
raise exceptions.OneViewErrorStateSettingPowerState(message)
return current_state
# --- ManagementDriver ---
def get_server_hardware(self, node_info):
uuid = node_info['server_hardware_uri'].split("/")[-1]
return self.get_server_hardware_by_uuid(uuid)
def get_server_hardware_by_uuid(self, uuid):
server_hardware_uri = SERVER_HARDWARE_PREFIX_URI + str(uuid)
server_hardware_json = self._prepare_and_do_request(
uri=server_hardware_uri
)
if server_hardware_json.get("uri") is None:
message = "OneView Server Hardware resource not found."
raise exceptions.OneViewResourceNotFoundError(message)
return ServerHardware.from_json(server_hardware_json)
def get_server_profile_from_hardware(self, node_info):
server_hardware = self.get_server_hardware(node_info)
server_profile_uri = server_hardware.server_profile_uri
if server_profile_uri is None:
message = (
"There is no server profile assigned to"
" %(server_hardware_uri)s" %
{'server_hardware_uri': node_info.get('server_hardware_uri')}
)
raise exceptions.OneViewServerProfileAssociatedError(message)
server_profile_json = self._prepare_and_do_request(
uri=server_profile_uri
)
if server_profile_json.get('uri') is None:
message = "OneView Server Profile resource not found."
raise exceptions.OneViewResourceNotFoundError(message)
return ServerProfile.from_json(server_profile_json)
def get_server_profile_template(self, node_info):
uuid = node_info['server_profile_template_uri'].split("/")[-1]
return self.get_server_profile_template_by_uuid(uuid)
def get_server_profile_template_by_uuid(self, uuid):
server_profile_template_uri = SERVER_PROFILE_TEMPLATE_PREFIX_URI \
+ str(uuid)
spt_json = self._prepare_and_do_request(
uri=server_profile_template_uri
)
if spt_json.get("uri") is None:
message = "OneView Server Profile Template resource not found."
raise exceptions.OneViewResourceNotFoundError(message)
return ServerProfileTemplate.from_json(spt_json)
def get_boot_order(self, node_info):
server_profile = self.get_server_profile_from_hardware(
node_info
)
return server_profile.boot.get("order")
def _update_boot_order(self, server_profile, order):
manageBoot = server_profile.boot.get("manageBoot")
server_profile.boot = {
"manageBoot": manageBoot,
"order": order
}
return server_profile
def set_boot_device(self, node_info, new_primary_boot_device):
boot_order = self.get_boot_order(node_info)
if new_primary_boot_device is None:
raise exceptions.OneViewBootDeviceInvalidError()
if new_primary_boot_device in boot_order:
boot_order.remove(new_primary_boot_device)
boot_order.insert(0, new_primary_boot_device)
server_profile = self.get_server_profile_from_hardware(
node_info
)
server_profile_updated = self._update_boot_order(
server_profile,
boot_order
)
boot_order_dict = server_profile_updated.to_oneview_dict()
task = self._prepare_and_do_request(
uri=server_profile.uri, body=boot_order_dict,
request_type=PUT_REQUEST_TYPE
)
try:
self._wait_for_task_to_complete(task)
except exceptions.OneViewTaskError as e:
raise exceptions.OneViewErrorSettingBootDevice(e.message)
# ---- Node validate ----
def validate_node_server_hardware(
self, node_info, node_memorymb, node_cpus
):
node_sh_uri = node_info.get('server_hardware_uri')
server_hardware = self.get_server_hardware(node_info)
server_hardware_cpus = (server_hardware.processor_core_count
* server_hardware.processor_count)
if server_hardware.memory_mb != node_memorymb:
message = (
"Node memory_mb is inconsistent with OneView's"
" server hardware %(server_hardware_uri)s memoryMb."
% {'server_hardware_uri': node_sh_uri}
)
raise exceptions.OneViewInconsistentResource(message)
elif server_hardware_cpus != node_cpus:
message = (
"Node cpus is inconsistent with OneView's"
" server hardware %(server_hardware_uri)s cpus."
% {'server_hardware_uri': node_sh_uri}
)
raise exceptions.OneViewInconsistentResource(message)
def validate_node_server_hardware_type(self, node_info):
node_sht_uri = node_info.get('server_hardware_type_uri')
server_hardware = self.get_server_hardware(node_info)
server_hardware_sht_uri = server_hardware.server_hardware_type_uri
if server_hardware_sht_uri != node_sht_uri:
message = (
"Node server_hardware_type_uri is inconsistent"
" with OneView's server hardware %(server_hardware_uri)s"
" serverHardwareTypeUri." %
{'server_hardware_uri': node_info.get('server_hardware_uri')}
)
raise exceptions.OneViewInconsistentResource(message)
def check_server_profile_is_applied(self, node_info):
self.get_server_profile_from_hardware(node_info)
def validate_node_enclosure_group(self, node_info):
server_hardware = self.get_server_hardware(node_info)
sh_enclosure_group_uri = server_hardware.enclosure_group_uri
node_enclosure_group_uri = node_info.get('enclosure_group_uri')
if node_enclosure_group_uri is not '':
if sh_enclosure_group_uri != node_enclosure_group_uri:
message = (
"Node enclosure_group_uri is inconsistent"
" with OneView's server hardware %(server_hardware_uri)s"
" serverGroupUri." %
{'server_hardware_uri': node_info.
get('server_hardware_uri')}
)
raise exceptions.OneViewInconsistentResource(message)
def is_node_port_mac_compatible_with_server_profile(
self, node_info, ports
):
server_profile = self.get_server_profile_from_hardware(
node_info
)
primary_boot_connection = None
for connection in server_profile.connections:
boot = connection.get('boot')
if boot is not None and boot.get('priority').lower() == 'primary':
primary_boot_connection = connection
if primary_boot_connection is None:
message = (
"No primary boot connection configured for server profile"
" %s." % server_profile.uri
)
raise exceptions.OneViewInconsistentResource(message)
server_profile_mac = primary_boot_connection.get('mac')
is_mac_address_compatible = True
for port in ports:
port_address = port.__dict__.get('_obj_address')
if port_address is None:
port_address = port.__dict__.get('_address')
if port_address.lower() != \
server_profile_mac.lower():
is_mac_address_compatible = False
if (not is_mac_address_compatible) or len(ports) == 0:
message = (
"The ports of the node are not compatible with its"
" server profile %(server_profile_uri)s." %
{'server_profile_uri': server_profile.uri}
)
raise exceptions.OneViewInconsistentResource(message)
def is_node_port_mac_compatible_with_server_hardware(
self, node_info, ports
):
server_hardware = self.get_server_hardware(node_info)
device = server_hardware.port_map.get('deviceSlots')[0]
first_physical_port = device.get('physicalPorts')[0]
is_mac_address_compatible = True
for port in ports:
port_address = port.__dict__.get('_obj_address')
if port_address is None:
port_address = port.__dict__.get('_address')
if port_address.lower() != \
first_physical_port.get('mac').lower():
is_mac_address_compatible = False
if (not is_mac_address_compatible) or len(ports) == 0:
message = (
"The ports of the node are not compatible with its"
" server hardware %(server_hardware_uri)s." %
{'server_hardware_uri': server_hardware.uri}
)
raise exceptions.OneViewInconsistentResource(message)
def validate_node_server_profile_template(self, node_info):
node_spt_uri = node_info.get('server_profile_template_uri')
server_profile_template = self.get_server_profile_template(
node_info
)
spt_server_hardware_type_uri = server_profile_template \
.server_hardware_type_uri
spt_enclosure_group_uri = server_profile_template.enclosure_group_uri
server_hardware = self.get_server_hardware(node_info)
sh_server_hardware_type_uri = server_hardware.server_hardware_type_uri
sh_enclosure_group_uri_uri = server_hardware.enclosure_group_uri
if spt_server_hardware_type_uri != sh_server_hardware_type_uri:
message = (
"Server profile template %(spt_uri)s serverHardwareTypeUri is"
" inconsistent with server hardware %(server_hardware_uri)s"
" serverHardwareTypeUri." %
{'spt_uri': node_spt_uri,
'server_hardware_uri': node_info.get('server_hardware_uri')}
)
raise exceptions.OneViewInconsistentResource(message)
if spt_enclosure_group_uri != sh_enclosure_group_uri_uri:
message = (
"Server profile template %(spt_uri)s enclosureGroupUri is"
" inconsistent with server hardware %(server_hardware_uri)s"
" serverGroupUri." %
{'spt_uri': node_spt_uri,
'server_hardware_uri': node_info.get('server_hardware_uri')}
)
raise exceptions.OneViewInconsistentResource(message)
for connection in server_profile_template.connections:
boot = connection.get('boot')
if boot is not None and boot.get('priority').lower() != 'primary':
message = (
"No primary boot connection configured for server profile"
" template %s." % server_profile_template.uri
)
raise exceptions.OneViewInconsistentResource(message)
# --- Requests ---
def _prepare_and_do_request(
self, uri, body={}, request_type=GET_REQUEST_TYPE
):
json_response = {}
try:
if not self.session_id:
self.session_id = self.get_session()
headers = {
'content-type': 'application/json',
'X-Api-Version': SUPPORTED_ONEVIEW_VERSION,
'Auth': self.session_id
}
url = '%s%s' % (self.manager_url, uri)
body = json.dumps(body)
response = self._do_request(url, headers, body, request_type)
json_response = response.json()
except requests.RequestException as e:
connection_error = str(e.message).split(':')[-1]
log_message = ("Can't connect to OneView: %s" % connection_error)
raise exceptions.OneViewConnectionError(log_message)
return json_response
def _do_request(self, url, headers, body, request_type):
verify_status = self._get_verify_connection_option()
@retrying.retry(
stop_max_attempt_number=self.max_polling_attempts,
retry_on_result=lambda response: _check_request_status(response),
wait_fixed=WAIT_DO_REQUEST_IN_MILLISECONDS
)
def request(url, headers, body, request_type):
if request_type == PUT_REQUEST_TYPE:
response = requests.put(
url, data=body, headers=headers, verify=verify_status
)
elif request_type == POST_REQUEST_TYPE:
response = requests.post(
url, data=body, headers=headers, verify=verify_status
)
elif request_type == DELETE_REQUEST_TYPE:
response = requests.delete(
url, headers=headers, verify=verify_status
)
else:
response = requests.get(
url, headers=headers, verify=verify_status
)
return response
return request(url, headers, body, request_type)
def _wait_for_task_to_complete(self, task):
@retrying.retry(
retry_on_result=lambda task: task.get('percentComplete') < 100,
wait_fixed=WAIT_TASK_IN_MILLISECONDS,
retry_on_exception=lambda task: False
)
def wait(task):
uri = task.get('uri')
task = self._prepare_and_do_request(uri)
task_state = task.get("taskState")
error_code = task.get("errorCode")
if (not task_state) and error_code:
details = task.get("details")
if error_code == "RESOURCE_NOT_FOUND":
raise exceptions.OneViewResourceNotFoundError(details)
else:
raise exceptions.OneViewTaskError("%s - %s"
% (error_code, details))
elif task_state.lower() == 'error':
raise exceptions.OneViewTaskError("The task '%s' returned an "
"error state" % uri)
return task
return wait(task)
def _check_request_status(response):
repeat = False
status = response.status_code
if status == 401:
raise exceptions.OneViewNotAuthorizedException()
elif status == 404:
raise exceptions.OneViewResourceNotFoundError()
elif status in (408, 409,):
time.sleep(10)
repeat = True
elif status == 500:
raise exceptions.OneViewInternalServerError(
"OneView returned HTTP 500"
)
# Any other unexpected status are logged
elif status not in (200, 202,):
message = (
"OneView appliance returned an unknown response status: %s"
% status
)
raise exceptions.UnknowOneViewResponseError(message)
return repeat