Script 'mail_helper' called by obssrc
Hello community,
here is the log from the commit of package python-freezerclient for
openSUSE:Factory checked in at 2026-06-03 20:22:01
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-freezerclient (Old)
and /work/SRC/openSUSE:Factory/.python-freezerclient.new.1937 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-freezerclient"
Wed Jun 3 20:22:01 2026 rev:15 rq:1356774 version:6.3.0
Changes:
--------
---
/work/SRC/openSUSE:Factory/python-freezerclient/python-freezerclient.changes
2025-11-10 19:17:10.675938395 +0100
+++
/work/SRC/openSUSE:Factory/.python-freezerclient.new.1937/python-freezerclient.changes
2026-06-03 20:25:51.249752353 +0200
@@ -1,0 +2,24 @@
+Tue Jun 2 15:55:16 UTC 2026 - Dirk Müller <[email protected]>
+
+- update to 6.3.0:
+ * Re-enable tempest jobs
+ * Stop logging create/update requests to stdout
+ * Implement jobs retrieval for all projects
+ * Temporary disable tempest jobs
+ * Update master for stable/2026.1
+ * tox: Remove ineffective ignore_basepython_conflict and bump
+ minimum version
+
+-------------------------------------------------------------------
+Mon Mar 9 15:23:12 UTC 2026 - Dirk Müller <[email protected]>
+
+- update to 6.2.0:
+ * Drop support for Python 3.8 and 3.9
+ * Fix tag in README
+
+-------------------------------------------------------------------
+Mon Nov 10 13:04:16 UTC 2025 - Dirk Müller <[email protected]>
+
+- fix filelist
+
+-------------------------------------------------------------------
Old:
----
python_freezerclient-6.1.0.tar.gz
New:
----
python_freezerclient-6.3.0.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-freezerclient.spec ++++++
--- /var/tmp/diff_new_pack.BdHCW7/_old 2026-06-03 20:25:52.205792013 +0200
+++ /var/tmp/diff_new_pack.BdHCW7/_new 2026-06-03 20:25:52.209792179 +0200
@@ -1,7 +1,7 @@
#
# spec file for package python-freezerclient
#
-# Copyright (c) 2025 SUSE LLC and contributors
+# Copyright (c) 2026 SUSE LLC and contributors
#
# All modifications and additions to the file contributed by third parties
# remain the property of their copyright owners, unless otherwise agreed
@@ -18,7 +18,7 @@
%global pythons %{primary_python}
Name: python-freezerclient
-Version: 6.1.0
+Version: 6.3.0
Release: 0
Summary: Python API and CLI for OpenStack Freezer
License: Apache-2.0
@@ -79,8 +79,8 @@
%files %{python_files}
%doc README.rst
%license LICENSE
-%{python3_sitelib}/freezerclient
-%{python3_sitelib}/python_freezerclient-%{version}.dist-info
+%{python_sitelib}/freezerclient
+%{python_sitelib}/python_freezerclient-%{version}.dist-info
%{_bindir}/freezer
%files -n python-freezerclient-doc
++++++ python_freezerclient-6.1.0.tar.gz -> python_freezerclient-6.3.0.tar.gz
++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python_freezerclient-6.1.0/AUTHORS
new/python_freezerclient-6.3.0/AUTHORS
--- old/python_freezerclient-6.1.0/AUTHORS 2025-09-01 15:31:33.000000000
+0200
+++ new/python_freezerclient-6.3.0/AUTHORS 2026-05-19 11:26:35.000000000
+0200
@@ -10,6 +10,7 @@
Chen <[email protected]>
Corey Bryant <[email protected]>
Deepak Jon <[email protected]>
+Dmitriy Rabotyagov <[email protected]>
Dmitriy Rabotyagov <[email protected]>
Doug Hellmann <[email protected]>
Fabrizio Vanni <[email protected]>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python_freezerclient-6.1.0/ChangeLog
new/python_freezerclient-6.3.0/ChangeLog
--- old/python_freezerclient-6.1.0/ChangeLog 2025-09-01 15:31:33.000000000
+0200
+++ new/python_freezerclient-6.3.0/ChangeLog 2026-05-19 11:26:35.000000000
+0200
@@ -1,6 +1,22 @@
CHANGES
=======
+6.3.0
+-----
+
+* Re-enable tempest jobs
+* Stop logging create/update requests to stdout
+* Implement jobs retrieval for all projects
+* Temporary disable tempest jobs
+* Update master for stable/2026.1
+* tox: Remove ineffective ignore\_basepython\_conflict and bump minimum version
+
+6.2.0
+-----
+
+* Drop support for Python 3.8 and 3.9
+* Fix tag in README
+
6.1.0
-----
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python_freezerclient-6.1.0/PKG-INFO
new/python_freezerclient-6.3.0/PKG-INFO
--- old/python_freezerclient-6.1.0/PKG-INFO 2025-09-01 15:31:33.163253300
+0200
+++ new/python_freezerclient-6.3.0/PKG-INFO 2026-05-19 11:26:35.345000500
+0200
@@ -1,6 +1,6 @@
-Metadata-Version: 2.1
+Metadata-Version: 2.4
Name: python-freezerclient
-Version: 6.1.0
+Version: 6.3.0
Summary: OpenStack Disaster Recovery API Client Library
Home-page: https://docs.openstack.org/python-freezerclient/latest/
Author: OpenStack
@@ -11,11 +11,10 @@
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3.8
-Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
+Classifier: Programming Language :: Python :: 3.13
Classifier: Development Status :: 5 - Production/Stable
Classifier: Natural Language :: English
Classifier: Environment :: OpenStack
@@ -33,7 +32,7 @@
Classifier: Topic :: System :: Archiving :: Backup
Classifier: Topic :: System :: Archiving :: Compression
Classifier: Topic :: System :: Archiving
-Requires-Python: >=3.8
+Requires-Python: >=3.10
License-File: LICENSE
Requires-Dist:
setuptools!=24.0.0,!=34.0.0,!=34.0.1,!=34.0.2,!=34.0.3,!=34.1.0,!=34.1.1,!=34.2.0,!=34.3.0,!=34.3.1,!=34.3.2,!=36.2.0,>=21.0.0
Requires-Dist: pbr!=2.1.0,>=2.0.0
@@ -41,11 +40,26 @@
Requires-Dist: cliff!=2.9.0,>=2.8.0
Requires-Dist: oslo.serialization>=2.25.0
Requires-Dist: oslo.utils>=3.33.0
+Dynamic: author
+Dynamic: author-email
+Dynamic: classifier
+Dynamic: description
+Dynamic: home-page
+Dynamic: keywords
+Dynamic: license
+Dynamic: license-file
+Dynamic: requires-dist
+Dynamic: requires-python
+Dynamic: summary
=============================================================
Python bindings to the OpenStack Backup/Restore API (Freezer)
=============================================================
+.. image:: https://governance.openstack.org/tc/badges/python-freezerclient.svg
+
+.. Change things from this point on
+
.. image:: https://img.shields.io/pypi/v/python-freezerclient.svg
:target: https://pypi.org/project/python-freezerclient/
:alt: Latest Version
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python_freezerclient-6.1.0/README.rst
new/python_freezerclient-6.3.0/README.rst
--- old/python_freezerclient-6.1.0/README.rst 2025-09-01 15:31:07.000000000
+0200
+++ new/python_freezerclient-6.3.0/README.rst 2026-05-19 11:26:00.000000000
+0200
@@ -2,6 +2,10 @@
Python bindings to the OpenStack Backup/Restore API (Freezer)
=============================================================
+.. image:: https://governance.openstack.org/tc/badges/python-freezerclient.svg
+
+.. Change things from this point on
+
.. image:: https://img.shields.io/pypi/v/python-freezerclient.svg
:target: https://pypi.org/project/python-freezerclient/
:alt: Latest Version
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/python_freezerclient-6.1.0/freezerclient/tests/unit/v2/test_client_jobs.py
new/python_freezerclient-6.3.0/freezerclient/tests/unit/v2/test_client_jobs.py
---
old/python_freezerclient-6.1.0/freezerclient/tests/unit/v2/test_client_jobs.py
2025-09-01 15:31:07.000000000 +0200
+++
new/python_freezerclient-6.3.0/freezerclient/tests/unit/v2/test_client_jobs.py
2026-05-19 11:26:00.000000000 +0200
@@ -18,6 +18,7 @@
from oslo_serialization import jsonutils as json
from freezerclient import exceptions
+from freezerclient.v2 import jobs as jobs_cmd
from freezerclient.v2.managers import jobs
@@ -145,6 +146,40 @@
self.assertRaises(exceptions.ApiClientException, self.job_manager.list)
@mock.patch('freezerclient.v2.managers.jobs.requests')
+ def test_list_all_all_projects(self, mock_requests):
+ self.mock_response.status_code = 200
+ job_list = [{'job_id_0': 'bomboloid'}, {'job_id_1': 'asdfasdf'}]
+ self.mock_response.json.return_value = {'jobs': job_list}
+ mock_requests.get.return_value = self.mock_response
+ retval = self.job_manager.list_all(all_projects=True)
+ self.assertEqual(job_list, retval)
+ mock_requests.get.assert_called_with(
+ self.job_manager.endpoint,
+ headers=self.headers,
+ params={'limit': 10, 'offset': 0, 'all_projects': True},
+ data=None,
+ verify=True
+ )
+
+ @mock.patch('freezerclient.v2.managers.jobs.requests')
+ def test_list_all_projects(self, mock_requests):
+ self.mock_response.status_code = 200
+ job_list = [{'job_id_0': 'bomboloid'}, {'job_id_1': 'asdfasdf'}]
+ self.mock_response.json.return_value = {'jobs': job_list}
+ mock_requests.get.return_value = self.mock_response
+ retval = self.job_manager.list(all_projects=True)
+ self.assertEqual(job_list, retval)
+ mock_requests.get.assert_called_with(
+ self.job_manager.endpoint,
+ headers=self.headers,
+ params={'limit': 10, 'offset': 0, 'all_projects': True},
+ data=json.dumps(
+ {'match': [{'client_id': 'test_client_id_78900987'}]}
+ ),
+ verify=True
+ )
+
+ @mock.patch('freezerclient.v2.managers.jobs.requests')
def test_update_ok(self, mock_requests):
self.mock_response.status_code = 200
self.mock_response.json.return_value = {
@@ -271,3 +306,56 @@
mock_requests.post.return_value = self.mock_response
self.assertRaises(exceptions.ApiClientException,
self.job_manager.abort_job, job_id)
+
+
+class TestJobList(unittest.TestCase):
+ def setUp(self):
+ self.app = mock.Mock()
+ self.app.client = mock.Mock()
+ self.job_list = jobs_cmd.JobList(self.app, mock.Mock())
+
+ def test_get_parser(self):
+ parser = self.job_list.get_parser('test')
+ self.assertEqual('Specify a limit for search query',
+ parser._option_string_actions['--limit'].help)
+ self.assertEqual('Get jobs for all projects',
+ parser._option_string_actions['--all-projects'].help)
+
+ def test_take_action_all_projects(self):
+ parsed_args = mock.Mock()
+ parsed_args.limit = 10
+ parsed_args.offset = 0
+ parsed_args.search = ''
+ parsed_args.client_id = ''
+ parsed_args.all_projects = True
+
+ self.app.client.jobs.list_all.return_value = []
+
+ columns, data = self.job_list.take_action(parsed_args)
+
+ self.app.client.jobs.list_all.assert_called_once_with(
+ limit=10,
+ offset=0,
+ search={},
+ all_projects=True
+ )
+
+ def test_take_action_client_id_all_projects(self):
+ parsed_args = mock.Mock()
+ parsed_args.limit = 10
+ parsed_args.offset = 0
+ parsed_args.search = ''
+ parsed_args.client_id = 'test_client'
+ parsed_args.all_projects = True
+
+ self.app.client.jobs.list.return_value = []
+
+ columns, data = self.job_list.take_action(parsed_args)
+
+ self.app.client.jobs.list.assert_called_once_with(
+ limit=10,
+ offset=0,
+ search={},
+ client_id='test_client',
+ all_projects=True
+ )
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/python_freezerclient-6.1.0/freezerclient/v2/actions.py
new/python_freezerclient-6.3.0/freezerclient/v2/actions.py
--- old/python_freezerclient-6.1.0/freezerclient/v2/actions.py 2025-09-01
15:31:07.000000000 +0200
+++ new/python_freezerclient-6.3.0/freezerclient/v2/actions.py 2026-05-19
11:26:00.000000000 +0200
@@ -25,6 +25,43 @@
logging = logging.getLogger(__name__)
+def format_action(action):
+ column = (
+ 'Action ID',
+ 'Name',
+ 'Action',
+ 'Mode',
+ 'Path to Backup or Restore',
+ 'Storage',
+ 'Snapshot',
+ 'Container',
+ 'Log_file',
+ 'Remove_older_than',
+ 'Max_retries_interval',
+ 'Max_retries',
+ 'User_id',
+ 'Project_id'
+ )
+
+ data = (
+ action.get('action_id'),
+ action.get('freezer_action', {}).get('backup_name', ''),
+ action.get('freezer_action', {}).get('action', 'backup'),
+ action.get('freezer_action', {}).get('mode', 'fs'),
+ action.get('freezer_action', {}).get('path_to_backup', ''),
+ action.get('freezer_action', {}).get('storage', 'swift'),
+ action.get('freezer_action', {}).get('snapshot', 'False'),
+ action.get('freezer_action', {}).get('container', ''),
+ action.get('freezer_action', {}).get('log_file', ''),
+ action.get('freezer_action', {}).get('remove_older_than', '365'),
+ action.get('max_retries_interval', '6'),
+ action.get('max_retries', '5'),
+ action.get('user_id', ''),
+ action.get('project_id', ''),
+ )
+ return column, data
+
+
class ActionShow(show.ShowOne):
"""Show a single action """
def get_parser(self, prog_name):
@@ -39,41 +76,7 @@
if not action:
raise exceptions.ApiClientException('Action not found')
- column = (
- 'Action ID',
- 'Name',
- 'Action',
- 'Mode',
- 'Path to Backup or Restore',
- 'Storage',
- 'Snapshot',
- 'Container',
- 'Log_file',
- 'Remove_older_than',
- 'Max_retries_interval',
- 'Max_retries',
- 'User_id',
- 'Project_id'
- )
-
- data = (
- action.get('action_id'),
- action.get('freezer_action', {}).get('backup_name', ''),
- action.get('freezer_action', {}).get('action', 'backup'),
- action.get('freezer_action', {}).get('mode', 'fs'),
- action.get('freezer_action', {}).get('path_to_backup', ''),
- action.get('freezer_action', {}).get('storage', 'swift'),
- action.get('freezer_action', {}).get('snapshot', 'False'),
- action.get('freezer_action', {}).get('container', ''),
- action.get('freezer_action', {}).get('log_file', ''),
- action.get('freezer_action', {}).get('remove_older_than', '365'),
- action.get('max_retries_interval', '6'),
- action.get('max_retries', '5'),
- action.get('user_id', ''),
- action.get('project_id', ''),
- )
-
- return column, data
+ return format_action(action)
class ActionList(lister.Lister):
@@ -151,10 +154,9 @@
def take_action(self, parsed_args):
self.app.client.actions.delete(parsed_args.action_id)
- logging.info('Action {0} deleted'.format(parsed_args.action_id))
-class ActionCreate(command.Command):
+class ActionCreate(show.ShowOne):
"""Create an action from a file"""
def get_parser(self, prog_name):
parser = super(ActionCreate, self).get_parser(prog_name)
@@ -165,12 +167,15 @@
return parser
def take_action(self, parsed_args):
- action = utils.doc_from_json_file(parsed_args.file)
- action_id = self.app.client.actions.create(action)
- logging.info('Action {0} created'.format(action_id))
+ action_data = utils.doc_from_json_file(parsed_args.file)
+ action_id = self.app.client.actions.create(action_data)
+ action = self.app.client.actions.get(action_id)
+ if not action:
+ raise exceptions.ApiClientException('Action created but not found')
+ return format_action(action)
-class ActionUpdate(command.Command):
+class ActionUpdate(show.ShowOne):
"""Update an action from a file"""
def get_parser(self, prog_name):
parser = super(ActionUpdate, self).get_parser(prog_name)
@@ -182,6 +187,9 @@
return parser
def take_action(self, parsed_args):
- action = utils.doc_from_json_file(parsed_args.file)
- self.app.client.actions.update(parsed_args.action_id, action)
- logging.info('Action {0} updated'.format(parsed_args.action_id))
+ action_data = utils.doc_from_json_file(parsed_args.file)
+ self.app.client.actions.update(parsed_args.action_id, action_data)
+ action = self.app.client.actions.get(parsed_args.action_id)
+ if not action:
+ raise exceptions.ApiClientException('Action not found')
+ return format_action(action)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/python_freezerclient-6.1.0/freezerclient/v2/backups.py
new/python_freezerclient-6.3.0/freezerclient/v2/backups.py
--- old/python_freezerclient-6.1.0/freezerclient/v2/backups.py 2025-09-01
15:31:07.000000000 +0200
+++ new/python_freezerclient-6.3.0/freezerclient/v2/backups.py 2026-05-19
11:26:00.000000000 +0200
@@ -27,6 +27,24 @@
logging = logging.getLogger(__name__)
+def format_backup(backup):
+ column = (
+ 'Backup ID',
+ 'Project ID',
+ 'User ID',
+ 'User name',
+ 'Metadata'
+ )
+ data = (
+ backup.get('backup_uuid'),
+ backup.get('project_id'),
+ backup.get('user_id'),
+ backup.get('user_name'),
+ pprint.pformat(backup.get('backup_metadata'))
+ )
+ return column, data
+
+
class BackupShow(show.ShowOne):
"""Show the metadata of a single backup"""
def get_parser(self, prog_name):
@@ -40,21 +58,7 @@
if not backup:
raise exceptions.ApiClientException('Backup not found')
- column = (
- 'Backup ID',
- 'Project ID',
- 'User ID',
- 'User name',
- 'Metadata'
- )
- data = (
- backup.get('backup_uuid'),
- backup.get('project_id'),
- backup.get('user_id'),
- backup.get('user_name'),
- pprint.pformat(backup.get('backup_metadata'))
- )
- return column, data
+ return format_backup(backup)
class BackupList(lister.Lister):
@@ -126,10 +130,9 @@
def take_action(self, parsed_args):
self.app.client.backups.delete(parsed_args.backup_uuid)
- logging.info('Backup {0} deleted'.format(parsed_args.backup_uuid))
-class BackupCreate(command.Command):
+class BackupCreate(show.ShowOne):
"""Create an backup from a file"""
def get_parser(self, prog_name):
parser = super(BackupCreate, self).get_parser(prog_name)
@@ -140,6 +143,9 @@
return parser
def take_action(self, parsed_args):
- backup = utils.doc_from_json_file(parsed_args.file)
- backup_id = self.app.client.backups.create(backup)
- logging.info('Backup {0} created'.format(backup_id))
+ backup_metadata = utils.doc_from_json_file(parsed_args.file)
+ backup_id = self.app.client.backups.create(backup_metadata)
+ backup = self.app.client.backups.get(backup_id)
+ if not backup:
+ raise exceptions.ApiClientException('Backup created but not found')
+ return format_backup(backup)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/python_freezerclient-6.1.0/freezerclient/v2/clients.py
new/python_freezerclient-6.3.0/freezerclient/v2/clients.py
--- old/python_freezerclient-6.1.0/freezerclient/v2/clients.py 2025-09-01
15:31:07.000000000 +0200
+++ new/python_freezerclient-6.3.0/freezerclient/v2/clients.py 2026-05-19
11:26:00.000000000 +0200
@@ -25,6 +25,23 @@
logging = logging.getLogger(__name__)
+def format_client(client):
+ column = (
+ 'Client ID',
+ 'Client UUID',
+ 'hostname',
+ 'description'
+ )
+ data = (
+ client.get('client', {}).get('client_id'),
+ client.get('client', {}).get('uuid'),
+ client.get('client', {}).get('hostname'),
+ client.get('client', {}).get('description', '')
+ )
+
+ return column, data
+
+
class ClientShow(show.ShowOne):
"""Show a single client"""
def get_parser(self, prog_name):
@@ -39,20 +56,7 @@
if not client:
raise exceptions.ApiClientException('Client not found')
- column = (
- 'Client ID',
- 'Client UUID',
- 'hostname',
- 'description'
- )
- data = (
- client.get('client', {}).get('client_id'),
- client.get('client', {}).get('uuid'),
- client.get('client', {}).get('hostname'),
- client.get('client', {}).get('description', '')
- )
-
- return column, data
+ return format_client(client)
class ClientList(lister.Lister):
@@ -114,10 +118,9 @@
def take_action(self, parsed_args):
self.app.client.clients.delete(parsed_args.client_id)
- logging.info('Client {0} deleted'.format(parsed_args.client_id))
-class ClientRegister(command.Command):
+class ClientRegister(show.ShowOne):
"""Register a new client"""
def get_parser(self, prog_name):
parser = super(ClientRegister, self).get_parser(prog_name)
@@ -128,10 +131,14 @@
return parser
def take_action(self, parsed_args):
- client = utils.doc_from_json_file(parsed_args.file)
+ client_data = utils.doc_from_json_file(parsed_args.file)
try:
- client_id = self.app.client.clients.create(client)
+ client_id = self.app.client.clients.create(client_data)
except Exception as err:
raise exceptions.ApiClientException(err.message)
else:
- logging.info("Client {0} registered".format(client_id))
+ client = self.app.client.clients.get(client_id)
+ if not client:
+ raise exceptions.ApiClientException(
+ 'Client registered but not found')
+ return format_client(client)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python_freezerclient-6.1.0/freezerclient/v2/jobs.py
new/python_freezerclient-6.3.0/freezerclient/v2/jobs.py
--- old/python_freezerclient-6.1.0/freezerclient/v2/jobs.py 2025-09-01
15:31:07.000000000 +0200
+++ new/python_freezerclient-6.3.0/freezerclient/v2/jobs.py 2026-05-19
11:26:00.000000000 +0200
@@ -26,6 +26,40 @@
logging = logging.getLogger(__name__)
+def format_job(job):
+ column = (
+ 'Job ID',
+ 'Client ID',
+ 'User ID',
+ 'Session ID',
+ 'Description',
+ 'Actions',
+ 'Start Date',
+ 'End Date',
+ 'Interval',
+ 'Status',
+ 'Result',
+ 'Current pid',
+ 'Event',
+ )
+ data = (
+ job.get('job_id'),
+ job.get('client_id'),
+ job.get('user_id'),
+ job.get('session_id', ''),
+ job.get('description'),
+ pprint.pformat(job.get('job_actions')),
+ job.get('job_schedule', {}).get('schedule_start_date', ''),
+ job.get('job_schedule', {}).get('schedule_end_date', ''),
+ job.get('job_schedule', {}).get('schedule_interval', ''),
+ job.get('job_schedule', {}).get('status', ''),
+ job.get('job_schedule', {}).get('result', ''),
+ job.get('job_schedule', {}).get('current_pid', ''),
+ job.get('job_schedule', {}).get('event', ''),
+ )
+ return column, data
+
+
class JobShow(show.ShowOne):
"""Show a single job"""
def get_parser(self, prog_name):
@@ -40,37 +74,7 @@
if not job:
raise exceptions.ApiClientException('Job not found')
- column = (
- 'Job ID',
- 'Client ID',
- 'User ID',
- 'Session ID',
- 'Description',
- 'Actions',
- 'Start Date',
- 'End Date',
- 'Interval',
- 'Status',
- 'Result',
- 'Current pid',
- 'Event',
- )
- data = (
- job.get('job_id'),
- job.get('client_id'),
- job.get('user_id'),
- job.get('session_id', ''),
- job.get('description'),
- pprint.pformat(job.get('job_actions')),
- job.get('job_schedule', {}).get('schedule_start_date', ''),
- job.get('job_schedule', {}).get('schedule_end_date', ''),
- job.get('job_schedule', {}).get('schedule_interval', ''),
- job.get('job_schedule', {}).get('status', ''),
- job.get('job_schedule', {}).get('result', ''),
- job.get('job_schedule', {}).get('current_pid', ''),
- job.get('job_schedule', {}).get('event', ''),
- )
- return column, data
+ return format_job(job)
class JobList(lister.Lister):
@@ -105,6 +109,13 @@
default='',
help='Get jobs for a specific client',
)
+
+ parser.add_argument(
+ '--all-projects',
+ dest='all_projects',
+ action='store_true',
+ help='Get jobs for all projects',
+ )
return parser
def take_action(self, parsed_args):
@@ -116,13 +127,15 @@
limit=parsed_args.limit,
offset=parsed_args.offset,
search=search,
- client_id=parsed_args.client_id
+ client_id=parsed_args.client_id,
+ all_projects=parsed_args.all_projects,
)
else:
jobs = self.app.client.jobs.list_all(
limit=parsed_args.limit,
offset=parsed_args.offset,
- search=search
+ search=search,
+ all_projects=parsed_args.all_projects,
)
columns = ('Job ID', 'Description', '# Actions', 'Result', 'Status',
@@ -188,10 +201,9 @@
def take_action(self, parsed_args):
self.app.client.jobs.delete(parsed_args.job_id)
- logging.info('Job {0} deleted'.format(parsed_args.job_id))
-class JobCreate(command.Command):
+class JobCreate(show.ShowOne):
"""Create a new job from a file"""
def get_parser(self, prog_name):
parser = super(JobCreate, self).get_parser(prog_name)
@@ -211,10 +223,13 @@
return parser
def take_action(self, parsed_args):
- job = utils.doc_from_json_file(parsed_args.file)
- job['client_id'] = parsed_args.client_id
- job_id = self.app.client.jobs.create(job)
- logging.info('Job {0} created'.format(job_id))
+ job_data = utils.doc_from_json_file(parsed_args.file)
+ job_data['client_id'] = parsed_args.client_id
+ job_id = self.app.client.jobs.create(job_data)
+ job = self.app.client.jobs.get(job_id)
+ if not job:
+ raise exceptions.ApiClientException('Job created but not found')
+ return format_job(job)
class JobStart(command.Command):
@@ -227,8 +242,6 @@
def take_action(self, parsed_args):
self.app.client.jobs.start_job(parsed_args.job_id)
- logging.info("Start request sent "
- "for job {0}".format(parsed_args.job_id))
class JobStop(command.Command):
@@ -241,8 +254,6 @@
def take_action(self, parsed_args):
self.app.client.jobs.stop_job(parsed_args.job_id)
- logging.info("Stop request sent "
- "for job {0}".format(parsed_args.job_id))
class JobAbort(command.Command):
@@ -255,11 +266,9 @@
def take_action(self, parsed_args):
self.app.client.jobs.abort_job(parsed_args.job_id)
- logging.info("Abort request sent "
- "for job {0}".format(parsed_args.job_id))
-class JobUpdate(command.Command):
+class JobUpdate(show.ShowOne):
"""Update a job from a file"""
def get_parser(self, prog_name):
parser = super(JobUpdate, self).get_parser(prog_name)
@@ -271,6 +280,9 @@
return parser
def take_action(self, parsed_args):
- job = utils.doc_from_json_file(parsed_args.file)
- self.app.client.jobs.update(parsed_args.job_id, job)
- logging.info('Job {0} updated'.format(parsed_args.job_id))
+ job_data = utils.doc_from_json_file(parsed_args.file)
+ self.app.client.jobs.update(parsed_args.job_id, job_data)
+ job = self.app.client.jobs.get(parsed_args.job_id)
+ if not job:
+ raise exceptions.ApiClientException('Job not found')
+ return format_job(job)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/python_freezerclient-6.1.0/freezerclient/v2/managers/jobs.py
new/python_freezerclient-6.3.0/freezerclient/v2/managers/jobs.py
--- old/python_freezerclient-6.1.0/freezerclient/v2/managers/jobs.py
2025-09-01 15:31:07.000000000 +0200
+++ new/python_freezerclient-6.3.0/freezerclient/v2/managers/jobs.py
2026-05-19 11:26:00.000000000 +0200
@@ -51,21 +51,26 @@
if r.status_code != 204:
raise exceptions.ApiClientException(r)
- def list_all(self, limit=10, offset=0, search=None):
+ def list_all(self, limit=10, offset=0, search=None, all_projects=False):
data = json.dumps(search) if search else None
- query = {'limit': int(limit), 'offset': int(offset)}
+ query = {
+ 'limit': int(limit),
+ 'offset': int(offset),
+ 'all_projects': all_projects,
+ }
r = requests.get(self.endpoint, headers=self.headers,
params=query, data=data, verify=self.verify)
if r.status_code != 200:
raise exceptions.ApiClientException(r)
return r.json()['jobs']
- def list(self, limit=10, offset=0, search={}, client_id=None):
+ def list(self, limit=10, offset=0, search={}, client_id=None,
+ all_projects=False):
client_id = client_id or self.client.client_id
new_search = search.copy()
new_search['match'] = search.get('match', [])
new_search['match'].append({'client_id': client_id})
- return self.list_all(limit, offset, new_search)
+ return self.list_all(limit, offset, new_search, all_projects)
def get(self, job_id):
endpoint = self.endpoint + job_id
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/python_freezerclient-6.1.0/freezerclient/v2/sessions.py
new/python_freezerclient-6.3.0/freezerclient/v2/sessions.py
--- old/python_freezerclient-6.1.0/freezerclient/v2/sessions.py 2025-09-01
15:31:07.000000000 +0200
+++ new/python_freezerclient-6.3.0/freezerclient/v2/sessions.py 2026-05-19
11:26:00.000000000 +0200
@@ -26,6 +26,41 @@
logging = logging.getLogger(__name__)
+def format_session(session):
+ column = (
+ 'Session ID',
+ 'Session tag',
+ 'Description',
+ 'Status',
+ 'Result',
+ 'Jobs',
+ 'Hold off',
+ 'Schedule',
+ 'Last start',
+ 'Time start',
+ 'Time end',
+ 'Project id',
+ 'User id',
+ )
+
+ data = (
+ session.get('session_id'),
+ session.get('session_tag'),
+ session.get('description'),
+ session.get('status'),
+ session.get('result'),
+ pprint.pformat(session.get('jobs')),
+ session.get('hold_off'),
+ pprint.pformat(session.get('schedule')),
+ session.get('last_start'),
+ session.get('time_start'),
+ session.get('time_end'),
+ session.get('project_id'),
+ session.get('user_id'),
+ )
+ return column, data
+
+
class SessionShow(show.ShowOne):
"""Show a single session"""
def get_parser(self, prog_name):
@@ -40,38 +75,7 @@
if not session:
raise exceptions.ApiClientException('Session not found')
- column = (
- 'Session ID',
- 'Session tag',
- 'Description',
- 'Status',
- 'Result',
- 'Jobs',
- 'Hold off',
- 'Schedule',
- 'Last start',
- 'Time start',
- 'Time end',
- 'Project id',
- 'User id',
- )
-
- data = (
- session.get('session_id'),
- session.get('session_tag'),
- session.get('description'),
- session.get('status'),
- session.get('result'),
- pprint.pformat(session.get('jobs')),
- session.get('hold_off'),
- pprint.pformat(session.get('schedule')),
- session.get('last_start'),
- session.get('time_start'),
- session.get('time_end'),
- session.get('project_id'),
- session.get('user_id'),
- )
- return column, data
+ return format_session(session)
class SessionList(lister.Lister):
@@ -126,7 +130,7 @@
return columns, data
-class SessionCreate(command.Command):
+class SessionCreate(show.ShowOne):
"""Create a session from a file"""
def get_parser(self, prog_name):
parser = super(SessionCreate, self).get_parser(prog_name)
@@ -137,9 +141,13 @@
return parser
def take_action(self, parsed_args):
- session = utils.doc_from_json_file(parsed_args.file)
- session_id = self.app.client.sessions.create(session)
- logging.info('Session {0} created'.format(session_id))
+ session_data = utils.doc_from_json_file(parsed_args.file)
+ session_id = self.app.client.sessions.create(session_data)
+ session = self.app.client.sessions.get(session_id)
+ if not session:
+ raise exceptions.ApiClientException(
+ 'Session created but not found')
+ return format_session(session)
class SessionDelete(command.Command):
@@ -153,11 +161,9 @@
def take_action(self, parsed_args):
session = self.app.client.sessions.get(parsed_args.session_id)
if not session:
- logging.info('Unable to delete specified session.')
raise exceptions.ApiClientException('Session not found')
self.app.client.sessions.delete(parsed_args.session_id)
- logging.info('Session {0} deleted'.format(parsed_args.session_id))
class SessionAddJob(command.Command):
@@ -177,8 +183,6 @@
def take_action(self, parsed_args):
self.app.client.sessions.add_job(parsed_args.session_id,
parsed_args.job_id)
- logging.info('Job {0} added correctly to session {1}'.format(
- parsed_args.job_id, parsed_args.session_id))
class SessionRemoveJob(command.Command):
@@ -209,12 +213,9 @@
pass
else:
raise exceptions.ApiClientException(error.message)
- else:
- logging.info('Job {0} removed correctly from session {1}'.format(
- parsed_args.job_id, parsed_args.session_id))
-class SessionUpdate(command.Command):
+class SessionUpdate(show.ShowOne):
"""Update a session from a file"""
def get_parser(self, prog_name):
parser = super(SessionUpdate, self).get_parser(prog_name)
@@ -226,9 +227,12 @@
return parser
def take_action(self, parsed_args):
- session = utils.doc_from_json_file(parsed_args.file)
- self.app.client.sessions.update(parsed_args.session_id, session)
- logging.info('Session {0} updated'.format(parsed_args.session_id))
+ session_data = utils.doc_from_json_file(parsed_args.file)
+ self.app.client.sessions.update(parsed_args.session_id, session_data)
+ session = self.app.client.sessions.get(parsed_args.session_id)
+ if not session:
+ raise exceptions.ApiClientException('Session not found')
+ return format_session(session)
class SessionStart(command.Command):
@@ -253,7 +257,6 @@
def take_action(self, parsed_args):
session = self.app.client.sessions.get(parsed_args.session_id)
if not session:
- logging.info('Unable to start specified session.')
raise exceptions.ApiClientException('Session not found')
self.app.client.sessions.start_session(
@@ -261,5 +264,3 @@
parsed_args.job_id,
parsed_args.job_tag
)
- logging.info('Session {0} start requested'.format(
- parsed_args.session_id))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/python_freezerclient-6.1.0/python_freezerclient.egg-info/PKG-INFO
new/python_freezerclient-6.3.0/python_freezerclient.egg-info/PKG-INFO
--- old/python_freezerclient-6.1.0/python_freezerclient.egg-info/PKG-INFO
2025-09-01 15:31:33.000000000 +0200
+++ new/python_freezerclient-6.3.0/python_freezerclient.egg-info/PKG-INFO
2026-05-19 11:26:35.000000000 +0200
@@ -1,6 +1,6 @@
-Metadata-Version: 2.1
+Metadata-Version: 2.4
Name: python-freezerclient
-Version: 6.1.0
+Version: 6.3.0
Summary: OpenStack Disaster Recovery API Client Library
Home-page: https://docs.openstack.org/python-freezerclient/latest/
Author: OpenStack
@@ -11,11 +11,10 @@
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3.8
-Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
+Classifier: Programming Language :: Python :: 3.13
Classifier: Development Status :: 5 - Production/Stable
Classifier: Natural Language :: English
Classifier: Environment :: OpenStack
@@ -33,7 +32,7 @@
Classifier: Topic :: System :: Archiving :: Backup
Classifier: Topic :: System :: Archiving :: Compression
Classifier: Topic :: System :: Archiving
-Requires-Python: >=3.8
+Requires-Python: >=3.10
License-File: LICENSE
Requires-Dist:
setuptools!=24.0.0,!=34.0.0,!=34.0.1,!=34.0.2,!=34.0.3,!=34.1.0,!=34.1.1,!=34.2.0,!=34.3.0,!=34.3.1,!=34.3.2,!=36.2.0,>=21.0.0
Requires-Dist: pbr!=2.1.0,>=2.0.0
@@ -41,11 +40,26 @@
Requires-Dist: cliff!=2.9.0,>=2.8.0
Requires-Dist: oslo.serialization>=2.25.0
Requires-Dist: oslo.utils>=3.33.0
+Dynamic: author
+Dynamic: author-email
+Dynamic: classifier
+Dynamic: description
+Dynamic: home-page
+Dynamic: keywords
+Dynamic: license
+Dynamic: license-file
+Dynamic: requires-dist
+Dynamic: requires-python
+Dynamic: summary
=============================================================
Python bindings to the OpenStack Backup/Restore API (Freezer)
=============================================================
+.. image:: https://governance.openstack.org/tc/badges/python-freezerclient.svg
+
+.. Change things from this point on
+
.. image:: https://img.shields.io/pypi/v/python-freezerclient.svg
:target: https://pypi.org/project/python-freezerclient/
:alt: Latest Version
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/python_freezerclient-6.1.0/python_freezerclient.egg-info/SOURCES.txt
new/python_freezerclient-6.3.0/python_freezerclient.egg-info/SOURCES.txt
--- old/python_freezerclient-6.1.0/python_freezerclient.egg-info/SOURCES.txt
2025-09-01 15:31:33.000000000 +0200
+++ new/python_freezerclient-6.3.0/python_freezerclient.egg-info/SOURCES.txt
2026-05-19 11:26:35.000000000 +0200
@@ -87,10 +87,12 @@
python_freezerclient.egg-info/requires.txt
python_freezerclient.egg-info/top_level.txt
releasenotes/notes/drop-py-2-7-9a3fc069f66d62bc.yaml
+releasenotes/notes/drop-python-38-and-39.yaml
releasenotes/notes/freezerclient-v2-d0729e1ee77d341b.yaml
releasenotes/source/2023.1.rst
releasenotes/source/2023.2.rst
releasenotes/source/2025.1.rst
+releasenotes/source/2026.1.rst
releasenotes/source/conf.py
releasenotes/source/index.rst
releasenotes/source/newton.rst
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/python_freezerclient-6.1.0/python_freezerclient.egg-info/pbr.json
new/python_freezerclient-6.3.0/python_freezerclient.egg-info/pbr.json
--- old/python_freezerclient-6.1.0/python_freezerclient.egg-info/pbr.json
2025-09-01 15:31:33.000000000 +0200
+++ new/python_freezerclient-6.3.0/python_freezerclient.egg-info/pbr.json
2026-05-19 11:26:35.000000000 +0200
@@ -1 +1 @@
-{"git_version": "958e9ca", "is_release": true}
\ No newline at end of file
+{"git_version": "b8f4e60", "is_release": true}
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/python_freezerclient-6.1.0/releasenotes/notes/drop-python-38-and-39.yaml
new/python_freezerclient-6.3.0/releasenotes/notes/drop-python-38-and-39.yaml
---
old/python_freezerclient-6.1.0/releasenotes/notes/drop-python-38-and-39.yaml
1970-01-01 01:00:00.000000000 +0100
+++
new/python_freezerclient-6.3.0/releasenotes/notes/drop-python-38-and-39.yaml
2026-05-19 11:26:00.000000000 +0200
@@ -0,0 +1,4 @@
+---
+upgrade:
+ - |
+ Support for Python 3.8 and 3.9 has been dropped.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/python_freezerclient-6.1.0/releasenotes/source/2026.1.rst
new/python_freezerclient-6.3.0/releasenotes/source/2026.1.rst
--- old/python_freezerclient-6.1.0/releasenotes/source/2026.1.rst
1970-01-01 01:00:00.000000000 +0100
+++ new/python_freezerclient-6.3.0/releasenotes/source/2026.1.rst
2026-05-19 11:26:00.000000000 +0200
@@ -0,0 +1,6 @@
+===========================
+2026.1 Series Release Notes
+===========================
+
+.. release-notes::
+ :branch: stable/2026.1
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/python_freezerclient-6.1.0/releasenotes/source/index.rst
new/python_freezerclient-6.3.0/releasenotes/source/index.rst
--- old/python_freezerclient-6.1.0/releasenotes/source/index.rst
2025-09-01 15:31:07.000000000 +0200
+++ new/python_freezerclient-6.3.0/releasenotes/source/index.rst
2026-05-19 11:26:00.000000000 +0200
@@ -8,6 +8,7 @@
:maxdepth: 2
unreleased
+ 2026.1
2025.1
2023.2
2023.1
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python_freezerclient-6.1.0/setup.cfg
new/python_freezerclient-6.3.0/setup.cfg
--- old/python_freezerclient-6.1.0/setup.cfg 2025-09-01 15:31:33.163253300
+0200
+++ new/python_freezerclient-6.3.0/setup.cfg 2026-05-19 11:26:35.345000500
+0200
@@ -7,17 +7,16 @@
author = OpenStack
author_email = [email protected]
home_page = https://docs.openstack.org/python-freezerclient/latest/
-python_requires = >=3.8
+python_requires = >=3.10
classifier =
Programming Language :: Python
Programming Language :: Python :: Implementation :: CPython
Programming Language :: Python :: 3 :: Only
Programming Language :: Python :: 3
- Programming Language :: Python :: 3.8
- Programming Language :: Python :: 3.9
Programming Language :: Python :: 3.10
Programming Language :: Python :: 3.11
Programming Language :: Python :: 3.12
+ Programming Language :: Python :: 3.13
Development Status :: 5 - Production/Stable
Natural Language :: English
Environment :: OpenStack
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python_freezerclient-6.1.0/tox.ini
new/python_freezerclient-6.3.0/tox.ini
--- old/python_freezerclient-6.1.0/tox.ini 2025-09-01 15:31:07.000000000
+0200
+++ new/python_freezerclient-6.3.0/tox.ini 2026-05-19 11:26:00.000000000
+0200
@@ -1,14 +1,12 @@
[tox]
-minversion = 3.1.1
+minversion = 4.6.0
envlist = py3,pep8,pylint,docs
skipsdist = True
-ignore_basepython_conflict = True
[testenv]
-basepython = python3
usedevelop = True
deps =
-
-c{env:UPPER_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master}
+
-c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master}
-r{toxinidir}/requirements.txt
-r{toxinidir}/test-requirements.txt
@@ -38,14 +36,10 @@
[testenv:venv]
commands = {posargs}
-[testenv:py39]
-basepython = python3.9
-
[testenv:py312]
basepython = python3.12
[testenv:cover]
-basepython = python3
setenv =
{[testenv]setenv}
PYTHON=coverage run --source freezerclient --parallel-mode
@@ -57,7 +51,9 @@
coverage report
[testenv:docs]
-deps = -r{toxinidir}/doc/requirements.txt
+deps =
+
-c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master}
+ -r{toxinidir}/doc/requirements.txt
commands = sphinx-build -b html doc/source doc/build/html
[testenv:pep8]
@@ -79,10 +75,6 @@
show-source = True
exclude = .venv,.tox,dist,doc,*egg,releasenotes
-
[testenv:releasenotes]
-deps =
-
-c{env:UPPER_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master}
- -r{toxinidir}/requirements.txt
- -r{toxinidir}/doc/requirements.txt
+deps = {[testenv:docs]deps}
commands = sphinx-build -a -E -d releasenotes/build/doctrees -b html
releasenotes/source releasenotes/build/html