From 6c3487815b67b4756ce46bcd0453fdd93c1b6f84 Mon Sep 17 00:00:00 2001 From: Nguyen Quang Huy Date: Thu, 4 Oct 2018 10:51:57 +0700 Subject: [PATCH] Fix bug of eject virtual CDROM in scciclient This change will fix the case when Ironic tries to detach virtual CD if there is no ISO file attached. This applies to virtual floppy case as well. Change-Id: I4df5f6aa160fea0aff745fe929680aa61282c33e Signed-off-by: Nguyen Quang Huy --- scciclient/irmc/scci.py | 125 +++++++++++++++++++++++++++++ scciclient/tests/irmc/test_scci.py | 100 +++++++++++++++++++++++ 2 files changed, 225 insertions(+) diff --git a/scciclient/irmc/scci.py b/scciclient/irmc/scci.py index f0283a2..187e4fb 100755 --- a/scciclient/irmc/scci.py +++ b/scciclient/irmc/scci.py @@ -289,8 +289,20 @@ def scci_cmd(host, userid, password, cmd, port=443, auth_method='basic', data = file.read() config_type = '/biosupdate' else: + # For EJECT command, validate parameters to handle abnormal case + if check_eject_cd_cmd(cmd): + if not validate_params_cd_fd("cmd_cd", protocol, + host, auth_obj, + do_async, client_timeout): + return + if check_eject_fd_cmd(cmd): + if not validate_params_cd_fd("cmd_fd", protocol, + host, auth_obj, + do_async, client_timeout): + return data = cmd config_type = '/config' + r = requests.post(protocol + '://' + host + config_type, data=data, headers=header, @@ -343,6 +355,119 @@ def scci_cmd(host, userid, password, cmd, port=443, auth_method='basic', raise SCCIClientError(requests_exception) +def validate_params_cd_fd(cmd_type, protocol, host, auth_obj, + do_async, client_timeout): + """Validate parameters of CD/DVD or FD Image Virtual Media in iRMC + + If one of parameters (ImageServer, ImageShareName or ImageName) set in + ServerView Config Space is empty, and you try to eject virtual FD/CD, + iRMC returns error. This function determines whether iRMC doesn't return + error when you try to eject virtual FD/CD. + :param cmd_type: command type has value switch between "cmd_cd" or "cmd_fd" + :param protocol: + :param host: hostname or IP of iRMC + :param auth_obj: irmc userid/password + :param do_async: async call if True, sync call otherwise + :param client_timeout: timeout for SCCI operations + :return: False if one of param is null. Otherwise, returns True. + """ + + if cmd_type == "cmd_cd": + oe_image_server = "1A60" + oe_image_server_share_name = "1A65" + oe_image_name = "1A66" + else: + oe_image_server = "1A50" + oe_image_server_share_name = "1A55" + oe_image_name = "1A56" + + try: + param = {'P45': '1', 'SAVE_DATA': '1'} + header = {'Content-type': 'application/x-www-form-urlencoded'} + r = requests.get(protocol + '://' + host + '/iRMC_Settings.pre', + params=param, + headers=header, + verify=False, + timeout=client_timeout, + allow_redirects=False, + auth=auth_obj) + if not do_async: + time.sleep(5) + + if DEBUG: + print("---------------------------") + print("Current iRMC configuration:") + print(r.url) + + if r.status_code not in (200, 201): + raise SCCIClientError( + ('HTTP PROTOCOL ERROR, STATUS CODE = %s' % + str(r.status_code))) + + result = r.text + cmdseq = ET.fromstring(result) + cfg_dict = {} + for cmd_tag in cmdseq.iter(tag='CMD'): + oe = cmd_tag.get('OE') + data = cmd_tag.find('DATA').text + cfg_dict[oe] = data + + if DEBUG: + print("Server: ", cfg_dict[oe_image_server]) + print("Share Name: ", cfg_dict[oe_image_server_share_name]) + print("Image Name: ", cfg_dict[oe_image_name]) + print("---------------------------") + + if (cfg_dict[oe_image_server] is None) or \ + (cfg_dict[oe_image_server_share_name] is None) or \ + (cfg_dict[oe_image_name] is None): + return False + + except ET.ParseError as parse_error: + raise SCCIClientError(parse_error) + except requests.exceptions.RequestException as requests_exception: + raise SCCIClientError(requests_exception) + + return True + + +def check_eject_cd_cmd(xml_cmd): + """To check command is MOUNT or UNMOUNT + + :param xml_cmd: the command + :return: true if this is UNMOUNT command. Otherwise, return false. + """ + + try: + cmdseq = ET.fromstring(xml_cmd.strip()) + cmd = cmdseq.find("./CMD") + data = cmd.find("./DATA") + if cmd.get("OC") == "ConnectRemoteCdImage" and \ + cmd.get("Type") == "SET" and data.text == "0": + return True + except ET.ParseError as parse_error: + raise SCCIClientError(parse_error) + return False + + +def check_eject_fd_cmd(xml_cmd): + """To check command is MOUNT or UNMOUNT + + :param xml_cmd: the command + :return: true if this is UNMOUNT command. Otherwise, return false. + """ + try: + cmdseq = ET.fromstring(xml_cmd.strip()) + cmd = cmdseq.find("./CMD") + data = cmd.find("./DATA") + if cmd.get("OC") == "ConnectRemoteFdImage" and \ + cmd.get("Type") == "SET" and data.text == "0": + return True + except ET.ParseError as parse_error: + raise SCCIClientError(parse_error) + return False + + def get_client(host, userid, password, port=443, auth_method='basic', client_timeout=60, **kwargs): """get SCCI command partial function diff --git a/scciclient/tests/irmc/test_scci.py b/scciclient/tests/irmc/test_scci.py index 7c30233..139ecfc 100644 --- a/scciclient/tests/irmc/test_scci.py +++ b/scciclient/tests/irmc/test_scci.py @@ -35,6 +35,103 @@ if six.PY3: file = io.BytesIO +IRMC_CONFIG_PARTIAL = """ + + + + + 10.124.196.156 + 0 + + + + + 0 + + + + + 0 + + + + + 0 + + + + 1 + 0 + + + + /srv/nfs + 0 + + + + virtual-media.img + 0 + + + + 1 + 0 + + + + 1 + 0 + + + + 10.124.196.156 + 0 + + + + + 0 + + + + + 0 + + + + + 0 + + + + 1 + 0 + + + + /srv/nfs + 0 + + + + virtual-media.iso + 0 + + + + 1 + 0 + + + + 1 + 0 + + +""" + + class SCCITestCase(testtools.TestCase): """Tests for SCCI @@ -664,6 +761,9 @@ class SCCITestCase(testtools.TestCase): No Error """) + self.requests_mock.get("http://" + self.irmc_address + + "/iRMC_Settings.pre", text=IRMC_CONFIG_PARTIAL) + client = scci.get_client(self.irmc_address, self.irmc_username, self.irmc_password,