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 <huynq@vn.fujitsu.com>
This commit is contained in:
Nguyen Quang Huy 2018-10-04 10:51:57 +07:00 committed by Vanou Ishii
parent 3f45624aa6
commit 6c3487815b
2 changed files with 225 additions and 0 deletions

View File

@ -289,8 +289,20 @@ def scci_cmd(host, userid, password, cmd, port=443, auth_method='basic',
data = file.read() data = file.read()
config_type = '/biosupdate' config_type = '/biosupdate'
else: 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 data = cmd
config_type = '/config' config_type = '/config'
r = requests.post(protocol + '://' + host + config_type, r = requests.post(protocol + '://' + host + config_type,
data=data, data=data,
headers=header, headers=header,
@ -343,6 +355,119 @@ def scci_cmd(host, userid, password, cmd, port=443, auth_method='basic',
raise SCCIClientError(requests_exception) 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', def get_client(host, userid, password, port=443, auth_method='basic',
client_timeout=60, **kwargs): client_timeout=60, **kwargs):
"""get SCCI command partial function """get SCCI command partial function

View File

@ -35,6 +35,103 @@ if six.PY3:
file = io.BytesIO file = io.BytesIO
IRMC_CONFIG_PARTIAL = """<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<CMDSEQ>
<!-- "Many Items Ommited..." -->
<!-- "ConfBmcRemoteFdImageServer" -->
<CMD Context="SCCI" OC="ConfigSpace" OE="1A50" OI="0" Type="SET">
<DATA Type="xsd::string">10.124.196.156</DATA>
<STATUS>0</STATUS>
</CMD>
<!-- "ConfBmcRemoteFdImageUserName" -->
<CMD Context="SCCI" OC="ConfigSpace" OE="1A51" OI="0" Type="SET">
<DATA Type="xsd::string"></DATA>
<STATUS>0</STATUS>
</CMD>
<!-- "ConfBmcRemoteFdImageUserPassword" -->
<CMD Context="SCCI" OC="ConfigSpace" OE="1A52" OI="0" Type="SET">
<DATA Type="xsd::string"></DATA>
<STATUS>0</STATUS>
</CMD>
<!-- "ConfBmcRemoteFdImageUserDomain" -->
<CMD Context="SCCI" OC="ConfigSpace" OE="1A53" OI="0" Type="SET">
<DATA Type="xsd::string"></DATA>
<STATUS>0</STATUS>
</CMD>
<!-- "ConfBmcRemoteFdImageShareType" -->
<CMD Context="SCCI" OC="ConfigSpace" OE="1A54" OI="0" Type="SET">
<DATA Type="xsd::integer">1</DATA>
<STATUS>0</STATUS>
</CMD>
<!-- "ConfBmcRemoteFdImageShareName" -->
<CMD Context="SCCI" OC="ConfigSpace" OE="1A55" OI="0" Type="SET">
<DATA Type="xsd::string">/srv/nfs</DATA>
<STATUS>0</STATUS>
</CMD>
<!-- "ConfBmcRemoteFdImageImageName" -->
<CMD Context="SCCI" OC="ConfigSpace" OE="1A56" OI="0" Type="SET">
<DATA Type="xsd::string">virtual-media.img</DATA>
<STATUS>0</STATUS>
</CMD>
<!-- "ConfBmcMediaOptionsFdAttachMode" -->
<CMD Context="SCCI" OC="ConfigSpace" OE="1A57" OI="0" Type="SET">
<DATA Type="xsd::integer">1</DATA>
<STATUS>0</STATUS>
</CMD>
<!-- "ConfBmcMediaOptionsFdNumber" -->
<CMD Context="SCCI" OC="ConfigSpace" OE="1A58" OI="0" Type="SET">
<DATA Type="xsd::integer">1</DATA>
<STATUS>0</STATUS>
</CMD>
<!-- "ConfBmcRemoteCdImageServer" -->
<CMD Context="SCCI" OC="ConfigSpace" OE="1A60" OI="0" Type="SET">
<DATA Type="xsd::string">10.124.196.156</DATA>
<STATUS>0</STATUS>
</CMD>
<!-- "ConfBmcRemoteCdImageUserName" -->
<CMD Context="SCCI" OC="ConfigSpace" OE="1A61" OI="0" Type="SET">
<DATA Type="xsd::string"></DATA>
<STATUS>0</STATUS>
</CMD>
<!-- "ConfBmcRemoteCdImageUserPassword" -->
<CMD Context="SCCI" OC="ConfigSpace" OE="1A62" OI="0" Type="SET">
<DATA Type="xsd::string"></DATA>
<STATUS>0</STATUS>
</CMD>
<!-- "ConfBmcRemoteCdImageUserDomain" -->
<CMD Context="SCCI" OC="ConfigSpace" OE="1A63" OI="0" Type="SET">
<DATA Type="xsd::string"></DATA>
<STATUS>0</STATUS>
</CMD>
<!-- "ConfBmcRemoteCdImageShareType" -->
<CMD Context="SCCI" OC="ConfigSpace" OE="1A64" OI="0" Type="SET">
<DATA Type="xsd::integer">1</DATA>
<STATUS>0</STATUS>
</CMD>
<!-- "ConfBmcRemoteCdImageShareName" -->
<CMD Context="SCCI" OC="ConfigSpace" OE="1A65" OI="0" Type="SET">
<DATA Type="xsd::string">/srv/nfs</DATA>
<STATUS>0</STATUS>
</CMD>
<!-- "ConfBmcRemoteCdImageImageName" -->
<CMD Context="SCCI" OC="ConfigSpace" OE="1A66" OI="0" Type="SET">
<DATA Type="xsd::string">virtual-media.iso</DATA>
<STATUS>0</STATUS>
</CMD>
<!-- "ConfBmcMediaOptionsCdAttachMode" -->
<CMD Context="SCCI" OC="ConfigSpace" OE="1A67" OI="0" Type="SET">
<DATA Type="xsd::integer">1</DATA>
<STATUS>0</STATUS>
</CMD>
<!-- "ConfBmcMediaOptionsCdNumber" -->
<CMD Context="SCCI" OC="ConfigSpace" OE="1A68" OI="0" Type="SET">
<DATA Type="xsd::integer">1</DATA>
<STATUS>0</STATUS>
</CMD>
<!-- "Many Items Ommited..." -->
</CMDSEQ>"""
class SCCITestCase(testtools.TestCase): class SCCITestCase(testtools.TestCase):
"""Tests for SCCI """Tests for SCCI
@ -664,6 +761,9 @@ class SCCITestCase(testtools.TestCase):
<Message>No Error</Message> <Message>No Error</Message>
</Status>""") </Status>""")
self.requests_mock.get("http://" + self.irmc_address +
"/iRMC_Settings.pre", text=IRMC_CONFIG_PARTIAL)
client = scci.get_client(self.irmc_address, client = scci.get_client(self.irmc_address,
self.irmc_username, self.irmc_username,
self.irmc_password, self.irmc_password,