Skip to content
Merged

Dev #48

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
5dc45d2
F/db/zoshost (#26)
blearandy Mar 28, 2023
87b1e7f
Add set property interface to system services (#27)
blearandy Mar 28, 2023
5893a69
Dev (#28) (#29)
blearandy Mar 28, 2023
e05f44b
Update version number for 1.0.2
blearandy Mar 28, 2023
33cca73
F/db/zoshost (#31)
blearandy Jul 10, 2023
e442838
Merge branch 'main' into dev
blearandy Jul 10, 2023
c5e8ecd
Added methods for added a cert to zos by taking in the base url, toke…
blearandy Sep 29, 2023
02dc9ef
Update release to 1.0.4
blearandy Sep 29, 2023
9c099d4
Merge branch 'main' into dev
blearandy Sep 29, 2023
7920b8e
Update for new release number 1.0.5
blearandy Oct 12, 2023
3149070
Merge branch 'main' into dev
blearandy Oct 12, 2023
9d4941d
Quick fix for pypi build issue (#35) (#36)
blearandy Dec 18, 2023
1138bf6
Added methods to duplicate, get and create new scheduled tasks. (#37)
blearandy Dec 18, 2023
21e3631
Merge branch 'main' into dev
blearandy Dec 18, 2023
ccf8313
Merge branch 'main' into dev
blearandy Dec 18, 2023
63ff1ba
F/sh/py Update and add new methods that have been created since the l…
dblea00 Mar 25, 2026
bd92f6f
F/rb/readthedocsfixes (#44)
blearandy Mar 26, 2026
9a5e9e7
Add GitHub Actions workflow for Python package publishing
blearandy Mar 26, 2026
3a3c57e
Add GitHub Actions workflow for linting and testing
blearandy Mar 26, 2026
0090c1d
testing lint workflow (#45)
blearandy Mar 26, 2026
77ee811
F/db/addtestmethods (#46)
dblea00 Mar 26, 2026
72d8968
Dev (#39) (#47)
blearandy Mar 27, 2026
1ae7221
Signed-off-by: Randy Blea <blead@us.ibm.com>
blearandy Mar 27, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 70 additions & 0 deletions .github/workflows/python-publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# This workflow will upload a Python Package to PyPI when a release is created
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python#publishing-to-package-registries

# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.

name: Upload Python Package

on:
release:
types: [published]

permissions:
contents: read

jobs:
release-build:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- uses: actions/setup-python@v5
with:
python-version: "3.x"

- name: Build release distributions
run: |
# NOTE: put your own distribution build steps here.
python -m pip install build
python -m build

- name: Upload distributions
uses: actions/upload-artifact@v4
with:
name: release-dists
path: dist/

pypi-publish:
runs-on: ubuntu-latest
needs:
- release-build
permissions:
# IMPORTANT: this permission is mandatory for trusted publishing
id-token: write

# Dedicated environments with protections for publishing are strongly recommended.
# For more information, see: https://docs.github.com/en/actions/deployment/targeting-different-environments/using-environments-for-deployment#deployment-protection-rules
environment:
name: pypi
# OPTIONAL: uncomment and update to include your PyPI project URL in the deployment status:
# url: https://pypi.org/p/YOURPROJECT
#
# ALTERNATIVE: if your GitHub Release name is the PyPI project version string
# ALTERNATIVE: exactly, uncomment the following line instead:
# url: https://pypi.org/project/YOURPROJECT/${{ github.event.release.name }}

steps:
- name: Retrieve release distributions
uses: actions/download-artifact@v4
with:
name: release-dists
path: dist/

- name: Publish release distributions to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
packages-dir: dist/
2 changes: 1 addition & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
# -- Project information -----------------------------------------------------

project = 'pyCSM'
copyright = '2022, dblea00'
copyright = '2026, dblea00'
author = 'dblea00'


Expand Down
2 changes: 1 addition & 1 deletion docs/environment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ channels:
- conda-forge
- defaults
dependencies:
- sphinx==4.2.0
- sphinx==5.0.0
- nbsphinx==0.8.1
- pip:
- sphinx_rtd_theme==1.0.0
2 changes: 1 addition & 1 deletion docs/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# File: docs/requirements.txt

# Defining the exact version will make sure things don't break
sphinx==4.2.0
sphinx==5.0.0
sphinx_rtd_theme==1.0.0
72 changes: 63 additions & 9 deletions pyCSM/clients/session_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -237,10 +237,10 @@ def wait_for_state(self, ses_name, state, minutes, debug=False):
ses_name, state, minutes, debug)
resp = result_dict["state_reached"]
if resp.status_code == 401:
minutes = (datetime.utcnow() - start_time).total_seconds()
elapsed_minutes = int((datetime.utcnow() - start_time).total_seconds() / 60)
self.tk = auth.get_token(self.base_url, self.username, self.password)
return session_service.wait_for_state(self.base_url, self.tk, ses_name,
state, minutes, debug)
state, elapsed_minutes, debug)
return result_dict

def sgc_recover(self, ses_name, com_name, role, backup_id):
Expand Down Expand Up @@ -482,7 +482,7 @@ def add_copysets(self, name, copyset, roleorder=None):
roleorder)
return resp

def remove_copysets(self, name, copysets, force=None, soft=None):
def remove_copysets(self, name, copysets, force=False, soft=False):
"""
Removes Copy Sets from the given session.

Expand Down Expand Up @@ -517,7 +517,7 @@ def export_copysets(self, name, file_name):
resp = copyset_service.export_copysets(self.base_url, self.tk, name, file_name)
if resp.status_code == 401:
self.tk = auth.get_token(self.base_url, self.username, self.password)
return copyset_service.export_copysets.export_cpyst(self.base_url, self.tk, name, file_name)
return copyset_service.export_copysets(self.base_url, self.tk, name, file_name)
return resp

def get_pair_info(self, name, rolepair):
Expand All @@ -538,7 +538,7 @@ def get_pair_info(self, name, rolepair):
rolepair)
if resp.status_code == 401:
self.tk = auth.get_token(self.base_url, self.username, self.password)
return copyset_service.get_pair_info.export_cpyst(self.base_url, self.tk, name,
return copyset_service.get_pair_info(self.base_url, self.tk, name,
rolepair)
return resp

Expand All @@ -554,11 +554,11 @@ def enable_scheduled_task_at_time(self, task_id, start_time):
JSON String representing the result of the command.
'I' = successful,'W' = warning, 'E' = error.
"""
resp = copyset_service.enable_scheduled_task_at_time(self.base_url, self.tk, task_id,
resp = schedule_service.enable_scheduled_task_at_time(self.base_url, self.tk, task_id,
start_time)
if resp.status_code == 401:
self.tk = auth.get_token(self.base_url, self.username, self.password)
return copyset_service.enable_scheduled_task_at_time(self.base_url, self.tk, task_id,
return schedule_service.enable_scheduled_task_at_time(self.base_url, self.tk, task_id,
start_time)
return resp

Expand All @@ -574,11 +574,11 @@ def run_scheduled_task_at_time(self, task_id, start_time):
JSON String representing the result of the command.
'I' = successful,'W' = warning, 'E' = error.
"""
resp = copyset_service.run_scheduled_task_at_time(self.base_url, self.tk, task_id,
resp = schedule_service.run_scheduled_task_at_time(self.base_url, self.tk, task_id,
start_time)
if resp.status_code == 401:
self.tk = auth.get_token(self.base_url, self.username, self.password)
return copyset_service.run_scheduled_task_at_time(self.base_url, self.tk, task_id,
return schedule_service.run_scheduled_task_at_time(self.base_url, self.tk, task_id,
start_time)
return resp

Expand Down Expand Up @@ -769,3 +769,57 @@ def get_rolepair_info(self, name, rolepair):
return session_service.get_rolepair_info(self.base_url, self.tk,
name, rolepair)
return resp

def delete_task(self, taskid):
"""
Delete a scheduled task.
Args:
taskid (str): ID of the schedule task to enable.

Returns:
JSON String representing the result of the command.
'I' = successful, 'W' = warning, 'E' = error.
"""
resp = schedule_service.delete_task(self.base_url, self.tk, taskid)
if resp.status_code == 401:
self.tk = auth.get_token(self.base_url, self.username, self.password)
return schedule_service.delete_task(self.base_url, self.tk, taskid)

return resp

def cancel_task(self, taskid):
"""
Cancel a running scheduled task.
Args:
taskid (str): ID of the schedule task to cancel.

Returns:
JSON String representing the result of the command.
'I' = successful, 'W' = warning, 'E' = error.
"""
resp = schedule_service.cancel_task(self.base_url, self.tk, taskid)
if resp.status_code == 401:
self.tk = auth.get_token(self.base_url, self.username, self.password)
return schedule_service.cancel_task(self.base_url, self.tk, taskid)

return resp

def run_task_now(self, taskid, synchronous=False, step=0):
"""
Run a scheduled task immediately at a specific step.


Returns:
JSON String representing the result of the command.
'I' = successful, 'W' = warning, 'E' = error.
"""

resp = schedule_service.cancel_task(self.base_url, self.tk, taskid)
if resp.status_code == 401:
self.tk = auth.get_token(self.base_url, self.username, self.password)
resp = schedule_service.cancel_task(self.base_url, self.tk, taskid)

run_resp = schedule_service.run_task_now(url=self.base_url, tk=self.tk, taskid=taskid, step=step, synchronous=synchronous)

return run_resp

45 changes: 44 additions & 1 deletion pyCSM/clients/system_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -510,5 +510,48 @@ def set_property(self, file, property_name, value):
resp = system_service.set_property(self.base_url, self.tk, file, property_name, value)
if resp.status_code == 401:
self.tk = auth.get_token(self.base_url, self.username, self.password)
return system_service.remove_active_or_standby_server(self.base_url, self.tk, file, property_name, value)
return system_service.remove_active_or_standby_server(self.base_url, self.tk, file)
return resp

def put_email_notifications_enabled(self, enabled):
"""
Enables or disables email alert notifications on the server.

Returns:
JSON String representing the result of the command.
'I' = successful, 'W' = warning, 'E' = error.
"""
resp = system_service.put_email_notifications_enabled(self.base_url, self.tk, enabled)
if resp.status_code == 401:
self.tk = auth.get_token(self.base_url, self.username, self.password)
return system_service.put_email_notifications_enabled(self.base_url, self.tk, enabled)
return resp

def get_email_recipients(self):
"""
Get a summary of the volume usage on the server

Returns:
JSON String representing the result of the command.
'I' = successful, 'W' = warning, 'E' = error.
"""
resp = system_service.get_email_recipients(self.base_url, self.tk)
if resp.status_code == 401:
self.tk = auth.get_token(self.base_url, self.username, self.password)
return system_service.get_email_recipients(self.base_url, self.tk)
return resp

def add_email_recipients(self, addresses, alert_type, session_names):
"""
Enables or disables email alert notifications on the server.

Returns:
JSON String representing the result of the command.
'I' = successful, 'W' = warning, 'E' = error.
"""
resp = system_service.add_email_recipients(self.base_url, self.tk, addresses, alert_type, session_names )
if resp.status_code == 401:
self.tk = auth.get_token(self.base_url, self.username, self.password)
return system_service.add_email_recipients(self.base_url, self.tk, addresses, alert_type, session_names)
return resp

52 changes: 0 additions & 52 deletions pyCSM/services/hardware_service/hardware_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -524,58 +524,6 @@ def add_zos_cert(url, tk, file_path):

return requests.post(post_url, headers=headers, files=files, verify=properties["verify"], cert=properties["cert"])


def add_zos_device(url, tk, device_id):
"""
This method will add a storage system through the zoshost connection.

Args:
url (str): Base url of CSM server. ex. https://servername:port/CSM/web.
tk (str): Rest token for the CSM server.
device_id (str): Storage system name in the format "DS8000:BOX:2107.KXZ91".

Returns:
JSON String representing the result of the command.
'I' = successful, 'W' = warning, 'E' = error.
"""
put_url = f"{url}/storagedevices/zosdevice"
headers = {
"Accept-Language": properties["language"],
"X-Auth-Token": str(tk),
"Content-Type": "application/x-www-form-urlencoded"
}

params = {
"deviceid": device_id
}

return requests.put(put_url, headers=headers, data=params, verify=properties["verify"], cert=properties["cert"])


def get_zos_host(url, tk):
"""
This method will get the information for all zos host connections.

Args:
url (str): Base url of CSM server. ex. https://servername:port/CSM/web.
tk (str): Rest token for the CSM server.

Returns:
JSON String representing the result of the command.
'I' = successful, 'W' = warning, 'E' = error.
"""
get_url = f"{url}/storagedevices/zoshost"
headers = {
"Accept-Language": properties["language"],
"X-Auth-Token": str(tk),
"Content-Type": "application/x-www-form-urlencoded"
}
return requests.get(get_url, headers=headers, verify=properties["verify"], cert=properties["cert"])





def add_zos_device(url, tk, device_id):
"""
This method will add a storage system through the zoshost connection.
Expand Down
47 changes: 0 additions & 47 deletions pyCSM/services/session_service/copyset_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,50 +172,3 @@ def get_pair_info(url, tk, name, rolepair):
}
return requests.get(get_url, headers=headers, verify=properties["verify"], cert=properties["cert"])


def enable_scheduled_task_at_time(url, tk, task_id, start_time):
"""
Enable the task at the given time

Args:
url (str): Base url of csm server. ex. https://servername:port/CSM/web.
tk (str): Rest token for the CSM server.
task_id (int): ID of the schedule task to enable
start_time (str): Time to enable the task.
Format of yyyy-MM-dd'T'HH-mm.

Returns:
JSON String representing the result of the command.
'I' = successful,'W' = warning, 'E' = error.
"""
post_url = f"{url}/sessions/scheduledtasks/enable/{task_id}/{start_time}"
headers = {
"Accept-Language": properties["language"],
"X-Auth-Token": str(tk),
"Content-Type": "application/x-www-form-urlencoded"
}
return requests.post(post_url, headers=headers, verify=properties["verify"], cert=properties["cert"])


def run_scheduled_task_at_time(url, tk, task_id, start_time):
"""
Run a scheduled task immediately.

Args:
url (str): Base url of csm server. ex. https://servername:port/CSM/web.
tk (str): Rest token for the CSM server.
task_id (int): ID of the schedule task to enable
start_time (str): Time to enable the task.
Format of yyyy-MM-dd'T'HH-mm.

Returns:
JSON String representing the result of the command.
'I' = successful,'W' = warning, 'E' = error.
"""
post_url = f"{url}/sessions/scheduledtasks/{task_id}/runat/{start_time}"
headers = {
"Accept-Language": properties["language"],
"X-Auth-Token": str(tk),
"Content-Type": "application/x-www-form-urlencoded"
}
return requests.post(post_url, headers=headers, verify=properties["verify"], cert=properties["cert"])
Loading