Skip to content

Commit

Permalink
Support for LPAR Load from FTP
Browse files Browse the repository at this point in the history
Details:

* Added support for the 'Load LPAR from FTP' operation by adding a
  method Lpar.load_from_ftp().

* Extended the HTTP-level mock support by a new fixture
  http_mocked_lpar.

* Added unit tests for Lpar.load_from_ftp() based on HTTP-level
  mocking.

Signed-off-by: Andreas Maier <[email protected]>
  • Loading branch information
andy-maier committed Aug 3, 2023
1 parent ff927ba commit 5beb818
Show file tree
Hide file tree
Showing 4 changed files with 211 additions and 1 deletion.
3 changes: 3 additions & 0 deletions docs/changes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ Released: not yet
* Test: Added pytest fixtures for mocking at the HTTP level for unit tests
in cases where zhmcclient mock support is not implemented.

* Added support for LPAR Load from FTP via a new Lpar.load_from_ftp()
method. (issue #1048)

**Cleanup:**

* Fixed new issue reported by flake8 6.1.0.
Expand Down
33 changes: 33 additions & 0 deletions tests/common/http_mocked_fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,39 @@ def http_mocked_cpc_classic(request, http_mocked_session): # noqa: F811
return cpc


@pytest.fixture(
scope='module'
)
def http_mocked_lpar(request, http_mocked_cpc_classic): # noqa: F811
# pylint: disable=unused-argument,redefined-outer-name
"""
Pytest fixture representing a HTTP-mocked LPAR on a CPC in classic mode.
Its CPC object can be accessed as the parent object.
A test function parameter using this fixture resolves to a
:class:`~zhmcclient.Lpar` object that has only a
minimal set of properties in the object (those that are returned with the
'List Logical Partitions of CPC' operation).
"""

with requests_mock.mock() as m:
uri = http_mocked_cpc_classic.uri + '/logical-partitions'
m.get(uri, status_code=200, json={
'logical-partitions': [
{
'object-uri': '/api/logical-partitions/lpar-id-1',
'name': 'LPAR1',
'status': 'operating',
}
]
})
lpars = http_mocked_cpc_classic.lpars.list()
lpar = lpars[0]

return lpar


@pytest.fixture(
scope='module'
)
Expand Down
56 changes: 55 additions & 1 deletion tests/unit/zhmcclient/test_lpar.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,19 @@
import copy
import mock
import pytest
import requests_mock

from zhmcclient import Client, Lpar, HTTPError, StatusTimeout
from zhmcclient import Client, Lpar, HTTPError, StatusTimeout, Job
from zhmcclient_mock import FakedSession, LparActivateHandler, \
LparDeactivateHandler, LparLoadHandler
from tests.common.utils import assert_resources

# pylint: disable=unused-import,line-too-long
from tests.common.http_mocked_fixtures import http_mocked_session # noqa: F401
from tests.common.http_mocked_fixtures import http_mocked_cpc_classic # noqa: F401,E501
from tests.common.http_mocked_fixtures import http_mocked_lpar # noqa: F401
# pylint: enable=unused-import,line-too-long


# Object IDs and names of our faked LPARs:
LPAR1_OID = 'lpar1-oid'
Expand Down Expand Up @@ -1403,3 +1410,50 @@ def test_console_list_permitted_lpars(self, filter_args, exp_names):
# TODO: Test for Lpar.psw_restart()
# TODO: Test for Lpar.reset_clear()
# TODO: Test for Lpar.reset_normal()


def test_lpar_load_from_ftp(http_mocked_lpar): # noqa: F811
# pylint: disable=redefined-outer-name,unused-argument
"""
Test function for Lpar.load_from_ftp()
"""
session = http_mocked_lpar.manager.session
uri = http_mocked_lpar.uri + '/operations/load-from-ftp'

# Define the input parameters for the test call
host = 'test-ftp-host-1'
username = 'test-user'
password = 'test-pwd'
load_file = '/images/load1.img'
protocol = 'sftp'

job_uri = '/api/jobs/job-1'

exp_request_body = {
'host-name': host,
'user-name': username,
'password': password,
'file-path': load_file,
'protocol': protocol,
}
exp_status_code = 202
result_body = {
'job-uri': job_uri,
}
exp_result_job = Job(session, job_uri, 'POST', uri)

rm_adapter = requests_mock.Adapter(case_sensitive=True)
with requests_mock.mock(adapter=rm_adapter) as m:

m.post(uri, status_code=exp_status_code, json=result_body)

result_job = http_mocked_lpar.load_from_ftp(
host, username, password, load_file, protocol,
wait_for_completion=False)

assert rm_adapter.called
request_body = rm_adapter.last_request.json()
assert request_body == exp_request_body
assert result_job.uri == exp_result_job.uri
assert result_job.op_method == exp_result_job.op_method
assert result_job.op_uri == exp_result_job.op_uri
120 changes: 120 additions & 0 deletions zhmcclient/_lpar.py
Original file line number Diff line number Diff line change
Expand Up @@ -1258,6 +1258,126 @@ def load(self, load_address=None, load_parameter=None,
self.wait_for_status(statuses, status_timeout)
return result

@logged_api_call
def load_from_ftp(
self, host, username, password, load_file, protocol='ftp',
wait_for_completion=True, operation_timeout=None,
status_timeout=None, allow_status_exceptions=False):
"""
Load (boot) this LPAR from an FTP server, using the HMC operation
"Load Logical Partition from FTP".
This operation is not permitted for an LPAR whose 'activation-mode'
property is "zaware" or "ssc".
This HMC operation has deferred status behavior: If the asynchronous
job on the HMC is complete, it takes a few seconds until the LPAR
status has reached the desired value. If `wait_for_completion=True`,
this method repeatedly checks the status of the LPAR after the HMC
operation has completed, and waits until the status is in the desired
state "operating", or if `allow_status_exceptions` was
set additionally in the state "exceptions".
Authorization requirements:
* Object-access permission to this LPAR.
* Before HMC API version 3.6 in an update to HMC 2.15.0: Object-access
permission to the CPC of this LPAR.
* Task permission for the "Load from Removable Media or Server" task.
Parameters:
host (string): Host name or IP address of the FTP server.
username (string): User name for the account on the FTP server.
password (string): Password that is associated with the user name on
the FTP server.
load_file (string): Path name of the file to be read from the FTP
server and loaded into the LPAR.
protocol (string): Network protocol for transferring files. Must be
one of:
* "ftp" - File Transfer Protocol
* "ftps" - FTP Secure
* "sftp" - SSH File Transfer Protocol
Default: "ftp"
wait_for_completion (bool):
Boolean controlling whether this method should wait for completion
of the requested asynchronous HMC operation, as follows:
* If `True`, this method will wait for completion of the
asynchronous job performing the operation, and for the status
becoming "operating" (or in addition "exceptions", if
`allow_status_exceptions` was set.
* If `False`, this method will return immediately once the HMC has
accepted the request to perform the operation.
operation_timeout (:term:`number`):
Timeout in seconds, for waiting for completion of the asynchronous
job performing the operation. The special value 0 means that no
timeout is set. `None` means that the default async operation
timeout of the session is used. If the timeout expires when
`wait_for_completion=True`, a
:exc:`~zhmcclient.OperationTimeout` is raised.
status_timeout (:term:`number`):
Timeout in seconds, for waiting that the status of the LPAR has
reached the desired status, after the HMC operation has completed.
The special value 0 means that no timeout is set. `None` means that
the default async operation timeout of the session is used.
If the timeout expires when `wait_for_completion=True`, a
:exc:`~zhmcclient.StatusTimeout` is raised.
allow_status_exceptions (bool):
Boolean controlling whether LPAR status "exceptions" is considered
an additional acceptable end status when `wait_for_completion` is
set.
Returns:
`None` or :class:`~zhmcclient.Job`:
If `wait_for_completion` is `True`, returns `None`.
If `wait_for_completion` is `False`, returns a
:class:`~zhmcclient.Job` object representing the asynchronously
executing job on the HMC.
Raises:
:exc:`~zhmcclient.HTTPError`
:exc:`~zhmcclient.ParseError`
:exc:`~zhmcclient.AuthError`
:exc:`~zhmcclient.ConnectionError`
:exc:`~zhmcclient.OperationTimeout`: The timeout expired while
waiting for completion of the operation.
:exc:`~zhmcclient.StatusTimeout`: The timeout expired while
waiting for the desired LPAR status.
"""
body = {
'host-name': host,
'user-name': username,
'password': password,
'file-path': load_file,
'protocol': protocol,
}
result = self.manager.session.post(
self.uri + '/operations/load-from-ftp', body=body,
wait_for_completion=wait_for_completion,
operation_timeout=operation_timeout)
if wait_for_completion:
statuses = ["operating"]
if allow_status_exceptions:
statuses.append("exceptions")
self.wait_for_status(statuses, status_timeout)
return result

@logged_api_call
def stop(self, wait_for_completion=True, operation_timeout=None,
status_timeout=None, allow_status_exceptions=False):
Expand Down

0 comments on commit 5beb818

Please sign in to comment.