From f8665b2c35a7658d10d3aae2ab6b6faf6c8c7bf7 Mon Sep 17 00:00:00 2001 From: Saimon Michelson Date: Tue, 3 Jun 2025 12:00:43 -0400 Subject: [PATCH 01/15] support file-walk without an argument, support listing the root of an edge filer --- cterasdk/asynchronous/core/files/browser.py | 4 ++-- cterasdk/asynchronous/edge/files/browser.py | 4 ++-- cterasdk/cio/core.py | 2 ++ cterasdk/cio/edge.py | 8 +++++--- cterasdk/core/files/browser.py | 4 ++-- cterasdk/edge/files/browser.py | 4 ++-- 6 files changed, 15 insertions(+), 11 deletions(-) diff --git a/cterasdk/asynchronous/core/files/browser.py b/cterasdk/asynchronous/core/files/browser.py index ae128ba1..4d87a6d0 100644 --- a/cterasdk/asynchronous/core/files/browser.py +++ b/cterasdk/asynchronous/core/files/browser.py @@ -86,11 +86,11 @@ async def versions(self, path): """ return await io.versions(self._core, self.normalize(path)) - async def walk(self, path, include_deleted=False): + async def walk(self, path=None, include_deleted=False): """ Walk Directory Contents - :param str path: Path to walk + :param str,optional path: Path to walk, defaults to the root directory :param bool,optional include_deleted: Include deleted files, defaults to False """ return io.walk(self._core, self._scope, path, include_deleted=include_deleted) diff --git a/cterasdk/asynchronous/edge/files/browser.py b/cterasdk/asynchronous/edge/files/browser.py index 1e2e3035..5b4c34b2 100644 --- a/cterasdk/asynchronous/edge/files/browser.py +++ b/cterasdk/asynchronous/edge/files/browser.py @@ -15,11 +15,11 @@ async def listdir(self, path): """ return await io.listdir(self._edge, self.normalize(path)) - async def walk(self, path): + async def walk(self, path=None): """ Walk Directory Contents - :param str path: Path to walk + :param str, defaults to the root directory path: Path to walk """ return io.walk(self._edge, path) diff --git a/cterasdk/cio/core.py b/cterasdk/cio/core.py index 536135f0..358843e5 100644 --- a/cterasdk/cio/core.py +++ b/cterasdk/cio/core.py @@ -26,6 +26,8 @@ def __init__(self, scope, reference): super().__init__(*CorePath._from_server_object(reference)) elif isinstance(reference, str): super().__init__(scope, reference) + elif reference is None: + super().__init__(scope, '') else: message = 'Path validation failed: ensure the path exists and is correctly formatted.' logger.error(message) diff --git a/cterasdk/cio/edge.py b/cterasdk/cio/edge.py index a3b6f5e2..694899ed 100644 --- a/cterasdk/cio/edge.py +++ b/cterasdk/cio/edge.py @@ -24,6 +24,8 @@ def __init__(self, scope, reference): super().__init__(scope, reference.path) elif isinstance(reference, str): super().__init__(scope, reference) + elif reference is None: + super().__init__(scope, '') else: message = 'Path validation failed: ensure the path exists and is correctly formatted.' logger.error(message) @@ -35,15 +37,15 @@ def instance(scope, reference): def fetch_reference(href): - namespace = 'localFiles/' - return unquote(href[href.index(namespace)+len(namespace):]) + namespace = '/localFiles' + return unquote(href[href.index(namespace)+len(namespace) + 1:]) def format_listdir_response(parent, response): entries = [] for e in response: path = fetch_reference(e.href) - if parent != path: + if path and parent != path: is_dir = e.getcontenttype == 'httpd/unix-directory' param = Object( path=path, diff --git a/cterasdk/core/files/browser.py b/cterasdk/core/files/browser.py index 7e23d88f..5336d9d9 100644 --- a/cterasdk/core/files/browser.py +++ b/cterasdk/core/files/browser.py @@ -89,11 +89,11 @@ def versions(self, path): """ return io.versions(self._core, self.normalize(path)) - def walk(self, path, include_deleted=False): + def walk(self, path=None, include_deleted=False): """ Walk Directory Contents - :param str path: Path to walk + :param str,optional path: Path to walk, defaults to the root directory :param bool,optional include_deleted: Include deleted files, defaults to False """ return io.walk(self._core, self._scope, path, include_deleted=include_deleted) diff --git a/cterasdk/edge/files/browser.py b/cterasdk/edge/files/browser.py index f25b8c87..bc1eb061 100644 --- a/cterasdk/edge/files/browser.py +++ b/cterasdk/edge/files/browser.py @@ -15,11 +15,11 @@ def listdir(self, path): """ return io.listdir(self._edge, self.normalize(path)) - def walk(self, path): + def walk(self, path=None): """ Walk Directory Contents - :param str path: Path to walk + :param str,optional path: Path to walk, defaults to the root directory """ return io.walk(self._edge, path) From f5e20900998fd5d0f787f233effdec1118931595 Mon Sep 17 00:00:00 2001 From: Saimon Michelson Date: Fri, 6 Jun 2025 12:04:37 -0400 Subject: [PATCH 02/15] revamp exceptions module, update logging, update all modules, add docs and update unittests --- cterasdk/__init__.py | 2 +- cterasdk/asynchronous/core/files/io.py | 10 +- cterasdk/asynchronous/core/iterator.py | 2 +- cterasdk/asynchronous/core/notifications.py | 46 +++--- cterasdk/asynchronous/core/users.py | 7 +- cterasdk/asynchronous/edge/files/io.py | 6 +- cterasdk/asynchronous/edge/login.py | 8 +- cterasdk/cio/core.py | 22 +-- cterasdk/cio/edge.py | 15 +- cterasdk/clients/async_requests.py | 2 +- cterasdk/clients/decorators.py | 2 +- cterasdk/clients/errors.py | 59 +------- cterasdk/clients/tracers/session.py | 2 +- cterasdk/common/object.py | 11 +- cterasdk/common/utils.py | 19 +-- cterasdk/core/activation.py | 7 +- cterasdk/core/admins.py | 25 ++-- cterasdk/core/antivirus.py | 27 ++-- cterasdk/core/buckets.py | 26 ++-- cterasdk/core/cli.py | 9 +- cterasdk/core/cloudfs.py | 119 +++++++-------- cterasdk/core/connection.py | 7 +- cterasdk/core/credentials.py | 15 +- cterasdk/core/devices.py | 7 +- cterasdk/core/directoryservice.py | 60 ++++---- cterasdk/core/files/browser.py | 6 +- cterasdk/core/files/io.py | 10 +- cterasdk/core/groups.py | 24 ++-- cterasdk/core/kms.py | 27 ++-- cterasdk/core/licenses.py | 11 +- cterasdk/core/logs.py | 7 +- cterasdk/core/mail.py | 15 +- cterasdk/core/messaging.py | 9 +- cterasdk/core/plans.py | 41 +++--- cterasdk/core/portals.py | 19 +-- cterasdk/core/reports.py | 5 +- cterasdk/core/roles.py | 11 +- cterasdk/core/servers.py | 19 ++- cterasdk/core/setup.py | 33 +++-- cterasdk/core/ssl.py | 17 ++- cterasdk/core/startup.py | 14 +- cterasdk/core/storage_classes.py | 12 +- cterasdk/core/syslog.py | 15 +- cterasdk/core/taskmgr.py | 5 +- cterasdk/core/templates.py | 36 ++--- cterasdk/core/users.py | 16 ++- cterasdk/direct/__init__.py | 2 +- cterasdk/direct/crypto.py | 17 ++- cterasdk/direct/decompressor.py | 11 +- cterasdk/direct/filters.py | 11 +- cterasdk/direct/lib.py | 11 +- cterasdk/direct/stream.py | 7 +- cterasdk/edge/afp.py | 9 +- cterasdk/edge/aio.py | 15 +- cterasdk/edge/array.py | 25 ++-- cterasdk/edge/audit.py | 11 +- cterasdk/edge/backup.py | 55 +++---- cterasdk/edge/cache.py | 27 ++-- cterasdk/edge/cli.py | 9 +- cterasdk/edge/config.py | 5 +- cterasdk/edge/connection.py | 9 +- cterasdk/edge/ctera_migrate.py | 7 +- cterasdk/edge/dedup.py | 9 +- cterasdk/edge/directoryservice.py | 22 +-- cterasdk/edge/directorytree.py | 8 +- cterasdk/edge/drive.py | 5 +- cterasdk/edge/files/io.py | 6 +- cterasdk/edge/firmware.py | 7 +- cterasdk/edge/ftp.py | 7 +- cterasdk/edge/groups.py | 11 +- cterasdk/edge/licenses.py | 13 +- cterasdk/edge/login.py | 13 +- cterasdk/edge/logs.py | 7 +- cterasdk/edge/mail.py | 11 +- cterasdk/edge/network.py | 47 +++--- cterasdk/edge/nfs.py | 7 +- cterasdk/edge/ntp.py | 15 +- cterasdk/edge/power.py | 17 ++- cterasdk/edge/ransom_protect.py | 11 +- cterasdk/edge/remote.py | 13 +- cterasdk/edge/rsync.py | 7 +- cterasdk/edge/services.py | 27 ++-- cterasdk/edge/shares.py | 61 ++++---- cterasdk/edge/shell.py | 9 +- cterasdk/edge/smb.py | 35 ++--- cterasdk/edge/snmp.py | 15 +- cterasdk/edge/ssh.py | 11 +- cterasdk/edge/ssl.py | 23 +-- cterasdk/edge/support.py | 7 +- cterasdk/edge/sync.py | 33 +++-- cterasdk/edge/syslog.py | 15 +- cterasdk/edge/taskmgr.py | 5 +- cterasdk/edge/telnet.py | 17 ++- cterasdk/edge/timezone.py | 7 +- cterasdk/edge/users.py | 35 +++-- cterasdk/edge/volumes.py | 65 ++++----- cterasdk/exceptions.py | 91 ------------ cterasdk/exceptions/__init__.py | 53 +++++++ .../exceptions.py => exceptions/direct.py} | 42 +++--- .../{cio/exceptions.py => exceptions/io.py} | 13 +- cterasdk/exceptions/notifications.py | 15 ++ cterasdk/exceptions/session.py | 22 +++ cterasdk/exceptions/transport.py | 135 ++++++++++++++++++ cterasdk/lib/consent.py | 4 +- cterasdk/lib/crypto.py | 8 +- cterasdk/lib/iterator.py | 5 +- cterasdk/lib/task_manager_base.py | 19 +-- cterasdk/lib/tracker.py | 27 ++-- .../UserGuides/DataServices/DirectIO.rst | 2 +- .../source/api/cterasdk.exceptions.direct.rst | 7 + docs/source/api/cterasdk.exceptions.io.rst | 7 + .../api/cterasdk.exceptions.notifications.rst | 7 + docs/source/api/cterasdk.exceptions.rst | 15 +- .../api/cterasdk.exceptions.session.rst | 7 + .../api/cterasdk.exceptions.transport.rst | 7 + docs/source/api/cterasdk.rst | 1 + tests/ut/aio/direct/test_get_metadata.py | 25 ++-- tests/ut/aio/direct/test_get_object.py | 10 +- tests/ut/aio/test_login.py | 2 +- tests/ut/aio/test_notifications.py | 6 +- tests/ut/aio/test_users.py | 2 +- tests/ut/core/admin/test_buckets.py | 7 +- tests/ut/core/admin/test_cloudfs_backups.py | 2 +- .../core/admin/test_cloudfs_cloud_drives.py | 6 +- .../core/admin/test_cloudfs_folder_groups.py | 24 ++-- tests/ut/core/admin/test_cloudfs_zones.py | 8 +- tests/ut/core/admin/test_devices.py | 2 +- .../ut/core/admin/test_directory_services.py | 18 +-- tests/ut/core/admin/test_groups.py | 15 +- tests/ut/core/admin/test_plans.py | 20 +-- tests/ut/core/admin/test_portals.py | 2 +- tests/ut/core/admin/test_reports.py | 2 +- tests/ut/core/admin/test_servers.py | 9 +- tests/ut/core/admin/test_startup.py | 2 +- tests/ut/core/admin/test_storage_classes.py | 2 +- tests/ut/core/admin/test_syslog.py | 2 +- tests/ut/core/admin/test_templates.py | 2 +- tests/ut/core/admin/test_users.py | 11 +- tests/ut/edge/test_array.py | 4 +- tests/ut/edge/test_backup.py | 13 +- tests/ut/edge/test_cache.py | 6 +- tests/ut/edge/test_directory_service.py | 2 +- tests/ut/edge/test_firmware.py | 6 +- tests/ut/edge/test_ftp.py | 2 +- tests/ut/edge/test_licenses.py | 2 +- tests/ut/edge/test_login.py | 8 +- tests/ut/edge/test_network.py | 6 +- tests/ut/edge/test_nfs.py | 4 +- tests/ut/edge/test_ransom_protect.py | 2 +- tests/ut/edge/test_rsync.py | 2 +- tests/ut/edge/test_services.py | 7 +- tests/ut/edge/test_shares.py | 12 +- tests/ut/edge/test_shell.py | 2 +- tests/ut/edge/test_smb.py | 8 +- tests/ut/edge/test_support.py | 2 +- tests/ut/edge/test_syslog.py | 2 +- tests/ut/edge/test_telnet.py | 2 +- tests/ut/edge/test_users.py | 14 +- tests/ut/edge/test_volumes.py | 17 +-- 159 files changed, 1437 insertions(+), 1051 deletions(-) delete mode 100644 cterasdk/exceptions.py create mode 100644 cterasdk/exceptions/__init__.py rename cterasdk/{direct/exceptions.py => exceptions/direct.py} (100%) rename cterasdk/{cio/exceptions.py => exceptions/io.py} (80%) create mode 100644 cterasdk/exceptions/notifications.py create mode 100644 cterasdk/exceptions/session.py create mode 100644 cterasdk/exceptions/transport.py create mode 100644 docs/source/api/cterasdk.exceptions.direct.rst create mode 100644 docs/source/api/cterasdk.exceptions.io.rst create mode 100644 docs/source/api/cterasdk.exceptions.notifications.rst create mode 100644 docs/source/api/cterasdk.exceptions.session.rst create mode 100644 docs/source/api/cterasdk.exceptions.transport.rst diff --git a/cterasdk/__init__.py b/cterasdk/__init__.py index 2ed45f73..e76d253f 100644 --- a/cterasdk/__init__.py +++ b/cterasdk/__init__.py @@ -1,9 +1,9 @@ # pylint: disable=wrong-import-position import cterasdk.settings # noqa: E402, F401 +import cterasdk.exceptions # noqa: E402, F401 from .common import Object, PolicyRule # noqa: E402, F401 from .convert import fromjsonstr, tojsonstr, fromxmlstr, toxmlstr # noqa: E402, F401 -from .exceptions import CTERAException # noqa: E402, F401 from .core import query # noqa: E402, F401 from .edge import types as edge_types # noqa: E402, F401 from .edge import enum as edge_enum # noqa: E402, F401 diff --git a/cterasdk/asynchronous/core/files/io.py b/cterasdk/asynchronous/core/files/io.py index 5330344f..fba9eeae 100644 --- a/cterasdk/asynchronous/core/files/io.py +++ b/cterasdk/asynchronous/core/files/io.py @@ -1,7 +1,7 @@ import logging from ....cio.common import encode_request_parameter from ....cio import core as fs -from ....cio import exceptions +from ....exceptions.io import ResourceNotFoundError, RemoteStorageException, ResourceExistsError from .. import query from ....lib import FetchResourcesResponse @@ -20,14 +20,14 @@ async def exists(core, path): try: await metadata(core, path) return True - except exceptions.ResourceNotFoundError: + except ResourceNotFoundError: return False async def metadata(core, path): response = await listdir(core, path, 0) if response.root is None: - raise exceptions.ResourceNotFoundError(path.absolute) + raise ResourceNotFoundError(path.absolute) return response.root @@ -59,7 +59,7 @@ async def makedirs(core, path): path = fs.CorePath.instance(path.scope, '/'.join(directories[:i])) try: await mkdir(core, path) - except exceptions.ResourceExistsError: + except ResourceExistsError: logger.debug('Resource already exists: %s', path.reference.as_posix()) @@ -91,7 +91,7 @@ async def move(core, *paths, destination=None): async def retrieve_remote_dir(core, directory): resource = await metadata(core, directory) if not resource.isFolder: - raise exceptions.RemoteStorageException('The destination path is not a directory', None, path=directory.absolute) + raise RemoteStorageException('The destination path is not a directory', None, path=directory.absolute) return str(resource.cloudFolderInfo.uid) diff --git a/cterasdk/asynchronous/core/iterator.py b/cterasdk/asynchronous/core/iterator.py index 28055038..c29d2fb3 100644 --- a/cterasdk/asynchronous/core/iterator.py +++ b/cterasdk/asynchronous/core/iterator.py @@ -23,7 +23,7 @@ async def __anext__(self): self._objects.extend(page) if self._objects: return self.object - logging.getLogger('cterasdk.common').debug('Stopping iteration.') + logger.debug('Stopping iteration.') raise StopAsyncIteration @property diff --git a/cterasdk/asynchronous/core/notifications.py b/cterasdk/asynchronous/core/notifications.py index d3928cc0..ed05da87 100644 --- a/cterasdk/asynchronous/core/notifications.py +++ b/cterasdk/asynchronous/core/notifications.py @@ -6,7 +6,11 @@ from .base_command import BaseCommand from ...common import Object from ...lib import CursorResponse -from ...exceptions import HTTPError, NotificationsError +from ...exceptions.transport import HTTPError +from ...exceptions.notifications import NotificationsError + + +logger = logging.getLogger('cterasdk.notifications') class Notifications(BaseCommand): @@ -30,11 +34,11 @@ async def get(self, cloudfolders=None, cursor=None, max_results=None): """ param = await self._create_parameter(cloudfolders, cursor) param.max_results = max_results if max_results is not None else 2000 - logging.getLogger('cterasdk.metadata.connector').debug('Listing updates.') + logger.debug('Listing updates.') response = await self._core.v2.api.post('/metadata/list', param) if response is not None: return CursorResponse(response) - logging.getLogger('cterasdk.metadata.connector').error('An error occurred while trying to retrieve notifications.') + logger.error('An error occurred while trying to retrieve notifications.') raise NotificationsError(cloudfolders, cursor) async def _create_parameter(self, cloudfolders, cursor): @@ -64,7 +68,7 @@ async def changes(self, cursor, cloudfolders=None, timeout=None): param = Object() param = await self._create_parameter(cloudfolders, cursor) param.timeout = timeout if timeout else 10000 - logging.getLogger('cterasdk.metadata.connector').debug('Checking for updates. %s', {'timeout': param.timeout}) + logger.debug('Checking for updates. %s', {'timeout': param.timeout}) return (await self._core.v2.api.post('/metadata/longpoll', param)).changes async def ancestors(self, descendant): @@ -78,12 +82,11 @@ async def ancestors(self, descendant): param = Object() param.folder_id = descendant.folder_id param.guid = descendant.guid - logging.getLogger('cterasdk.metadata.connector').debug('Getting ancestors. %s', {'guid': param.guid, 'folder_id': param.folder_id}) + logger.debug('Getting ancestors. %s', {'guid': param.guid, 'folder_id': param.folder_id}) try: return await self._core.v2.api.post('/metadata/ancestors', param) except HTTPError: - logging.getLogger('cterasdk.metadata.connector').error('Could not retrieve ancestors. %s', - {'folder_id': param.folder_id, 'guid': param.guid}) + logger.error('Could not retrieve ancestors. %s', {'folder_id': param.folder_id, 'guid': param.guid}) raise @@ -128,7 +131,7 @@ async def retrieve_events(server_queue, core, cloudfolders, cursor): :param list[CloudFSFolderFindingHelper] cloudfolders: List of Cloud Drive folders. :param str cursor: Cursor """ - logging.getLogger('cterasdk.metadata.connector').debug('Event Retrieval Service.') + logger.debug('Event Retrieval Service.') last_response = LastResponse(cursor) try: while True: @@ -142,9 +145,9 @@ async def retrieve_events(server_queue, core, cloudfolders, cursor): except ConnectionError as error: await on_connection_error(error) except TimeoutError: - logging.getLogger('cterasdk.metadata.connector').debug('Request timed out. Retrying.') + logger.debug('Request timed out. Retrying.') except asyncio.CancelledError: - logging.getLogger('cterasdk.metadata.connector').debug('Cancelling Event Retrieval.') + logger.debug('Cancelling Event Retrieval.') async def forward_events(server_queue, client_queue, save_cursor): @@ -155,7 +158,7 @@ async def forward_events(server_queue, client_queue, save_cursor): :param asyncio.Queue client_queue: Client queue. :param callback save_cursor: Callback function to persist the cursor. """ - logging.getLogger('cterasdk.metadata.connector').debug('Event Forwarder Service.') + logger.debug('Event Forwarder Service.') try: while True: batch = await server_queue.get() @@ -163,7 +166,7 @@ async def forward_events(server_queue, client_queue, save_cursor): await process_events(client_queue) await persist_cursor(save_cursor, batch.cursor) except asyncio.CancelledError: - logging.getLogger('cterasdk.metadata.connector').debug('Cancelling Event Forwarding.') + logger.debug('Cancelling Event Forwarding.') async def enqueue_events(events, queue): @@ -174,9 +177,9 @@ async def enqueue_events(events, queue): :param cterasdk.asynchronous.core.iterator.CursorAsyncIterator events: Event Iterator. """ for event in events: - logging.getLogger('cterasdk.metadata.connector').debug('Enqueuing Event.') + logger.debug('Enqueuing Event.') await queue.put(Event.from_server_object(event)) - logging.getLogger('cterasdk.metadata.connector').debug('Enqueued Event.') + logger.debug('Enqueued Event.') async def process_events(queue): @@ -185,9 +188,9 @@ async def process_events(queue): :param asyncio.Queue queue: Queue. """ - logging.getLogger('cterasdk.metadata.connector').debug('Joining Queue.') + logger.debug('Joining Queue.') await queue.join() - logging.getLogger('cterasdk.metadata.connector').debug('Completed Processing.') + logger.debug('Completed Processing.') async def persist_cursor(save_cursor, cursor): @@ -197,19 +200,18 @@ async def persist_cursor(save_cursor, cursor): :param callback save_cursor: Asynchronous callback function to persist the cursor. :param str cursor: Cursor """ - logging.getLogger('cterasdk.metadata.connector').debug("Persisting Cursor. Calling function: '%s'", save_cursor) + logger.debug("Persisting Cursor. Calling function: '%s'", save_cursor) try: await save_cursor(cursor) - logging.getLogger('cterasdk.metadata.connector').debug("Called Persist Cursor Function.") + logger.debug("Called Persist Cursor Function.") except Exception: # pylint: disable=broad-exception-caught - logging.getLogger('cterasdk.metadata.connector').error("An error occurred while trying to persist cursor. Function: '%s'", - save_cursor) + logger.error("An error occurred while trying to persist cursor. Function: '%s'", save_cursor) async def on_connection_error(error): seconds = 5 - logging.getLogger('cterasdk.metadata.connector').error('Connection error. Reason: %s.', str(error)) - logging.getLogger('cterasdk.metadata.connector').debug("Retrying in %s seconds.", seconds) + logger.error('Connection error. Reason: %s.', str(error)) + logger.debug("Retrying in %s seconds.", seconds) await asyncio.sleep(seconds) diff --git a/cterasdk/asynchronous/core/users.py b/cterasdk/asynchronous/core/users.py index 082800ef..56cd4d2e 100644 --- a/cterasdk/asynchronous/core/users.py +++ b/cterasdk/asynchronous/core/users.py @@ -1,6 +1,7 @@ from .base_command import BaseCommand from ...common import union, Object -from ...exceptions import ObjectNotFoundException, ContextError +from ...exceptions import ObjectNotFoundException +from ...exceptions.session import ContextError class Users(BaseCommand): @@ -25,7 +26,7 @@ async def get(self, user_account, include=None): include = ['/' + attr for attr in include] user_object = await self._core.v1.api.get_multi(baseurl, include) if user_object.name is None: - raise ObjectNotFoundException('Could not find user', baseurl, user_directory=user_account.directory, username=user_account.name) + raise ObjectNotFoundException(baseurl) return user_object async def generate_ticket(self, username, tenant): @@ -36,7 +37,7 @@ async def generate_ticket(self, username, tenant): :param str portal: Tenant """ if self.session().in_tenant_context(): - raise ContextError('Context error: Browse the Global Administration to invoke this API.') + raise ContextError('Browse the Global Administration to invoke this API') param = Object() param.username = username param.portal = tenant diff --git a/cterasdk/asynchronous/edge/files/io.py b/cterasdk/asynchronous/edge/files/io.py index aaefad43..5842377e 100644 --- a/cterasdk/asynchronous/edge/files/io.py +++ b/cterasdk/asynchronous/edge/files/io.py @@ -1,8 +1,8 @@ import logging from ....cio.common import encode_request_parameter from ....cio import edge as fs -from ....cio import exceptions -from ....exceptions import HTTPError +from ....exceptions.transport import HTTPError +from ....exceptions.io import RestrictedPathError logger = logging.getLogger('cterasdk.edge') @@ -43,7 +43,7 @@ async def makedirs(edge, path): path = fs.EdgePath(path.scope, '/'.join(directories[:i])) try: await mkdir(edge, path) - except exceptions.RestrictedPathError: + except RestrictedPathError: logger.warning('Creating a folder in the specified location is forbidden: %s', path.reference.as_posix()) diff --git a/cterasdk/asynchronous/edge/login.py b/cterasdk/asynchronous/edge/login.py index e3ceed5b..24c3ee72 100644 --- a/cterasdk/asynchronous/edge/login.py +++ b/cterasdk/asynchronous/edge/login.py @@ -16,9 +16,9 @@ async def login(self, username, password): host = self._edge.host() try: await self._edge.api.form_data('/login', {'username': username, 'password': password}) - logging.getLogger('cterasdk.edge').info("User logged in. %s", {'host': host, 'user': username}) + logger.info("User logged in. %s", {'host': host, 'user': username}) except CTERAException: - logging.getLogger('cterasdk.edge').error("Login failed. %s", {'host': host, 'user': username}) + logger.error("Login failed. %s", {'host': host, 'user': username}) raise async def logout(self): @@ -29,7 +29,7 @@ async def logout(self): user = self._edge.session().account.name try: await self._edge.api.form_data('/logout', {'foo': 'bar'}) - logging.getLogger('cterasdk.edge').info("User logged out. %s", {'host': host, 'user': user}) + logger.info("User logged out. %s", {'host': host, 'user': user}) except CTERAException: - logging.getLogger('cterasdk.edge').error("Logout failed. %s", {'host': host, 'user': user}) + logger.error("Logout failed. %s", {'host': host, 'user': user}) raise diff --git a/cterasdk/cio/core.py b/cterasdk/cio/core.py index 358843e5..0aadb90a 100644 --- a/cterasdk/cio/core.py +++ b/cterasdk/cio/core.py @@ -5,8 +5,8 @@ from ..common import Object, DateTimeUtils from ..core.enum import ProtectionLevel, CollaboratorType, SearchType, PortalAccountType, FileAccessMode from ..core.types import PortalAccount, UserAccount, GroupAccount -from ..exceptions import CTERAException -from . import common, exceptions +from ..exceptions.io import ResourceExistsError, PathValidationError, NameSyntaxError, ReservedNameError +from . import common logger = logging.getLogger('cterasdk.core') @@ -412,7 +412,7 @@ def search_collaboration_member(account, cloud_folder_uid): elif account.account_type == PortalAccountType.Group: param.searchType = SearchType.Groups else: - raise CTERAException("Invalid account type", None, account_type=account.account_type) + raise ValueError(f'Invalid account type: {account.account_type}') param.searchTerm = account.name param.resourceUid = cloud_folder_uid @@ -485,23 +485,23 @@ def accept_response(response, reference): Check if response contains an error. """ error = { - "FileWithTheSameNameExist": exceptions.ResourceExistsError(), - "DestinationNotExists": exceptions.PathValidationError(), - "InvalidName": exceptions.NameSyntaxError(), - "ReservedName": exceptions.ReservedNameError() + "FileWithTheSameNameExist": ResourceExistsError(), + "DestinationNotExists": PathValidationError(), + "InvalidName": NameSyntaxError(), + "ReservedName": ReservedNameError() }.get(response, None) try: if error: raise error - except exceptions.ResourceExistsError as error: + except ResourceExistsError as error: logger.info('Resource already exists: a file or folder with this name already exists. %s', {'path': reference}) raise error - except exceptions.PathValidationError as error: + except PathValidationError as error: logger.error('Path validation failed: the specified destination path does not exist. %s', {'path': reference}) raise error - except exceptions.NameSyntaxError as error: + except NameSyntaxError as error: logger.error('Invalid name: the name contains characters that are not allowed. %s', {'name': reference}) raise error - except exceptions.ReservedNameError as error: + except ReservedNameError as error: logger.error('Reserved name error: the name is reserved and cannot be used. %s', {'name': reference}) raise error diff --git a/cterasdk/cio/edge.py b/cterasdk/cio/edge.py index 694899ed..61711834 100644 --- a/cterasdk/cio/edge.py +++ b/cterasdk/cio/edge.py @@ -4,7 +4,8 @@ from pathlib import Path from ..common import Object from ..objects.uri import unquote -from . import common, exceptions +from . import common +from ..exceptions.io import CTERAException, ResourceExistsError, RestrictedPathError logger = logging.getLogger('cterasdk.edge') @@ -66,10 +67,10 @@ def makedir(path): logger.info('Creating directory: %s', path.absolute) try: yield path.absolute - except exceptions.CTERAException as error: + except CTERAException as error: try: accept_response(error.response.message.msg, directory) - except exceptions.ResourceExistsError: + except ResourceExistsError: logger.info('Directory already exists: %s', directory) logger.info('Directory created: %s', directory) @@ -126,15 +127,15 @@ def upload(name, destination, fd): def accept_response(response, reference): error = { - "File exists": exceptions.ResourceExistsError(), - "Creating a folder in this location is forbidden": exceptions.RestrictedPathError(), + "File exists": ResourceExistsError(), + "Creating a folder in this location is forbidden": RestrictedPathError(), }.get(response, None) try: if error: raise error - except exceptions.ResourceExistsError as error: + except ResourceExistsError as error: logger.warning('Resource already exists: a file or folder with this name already exists. %s', {'path': reference}) raise error - except exceptions.RestrictedPathError as error: + except RestrictedPathError as error: logger.error('Creating a folder in the specified location is forbidden. %s', {'name': reference}) raise error diff --git a/cterasdk/clients/async_requests.py b/cterasdk/clients/async_requests.py index 6d3ac117..e9321e92 100644 --- a/cterasdk/clients/async_requests.py +++ b/cterasdk/clients/async_requests.py @@ -56,7 +56,7 @@ async def _request(self, r, *, on_response=None): logger.warning(error) raise ConnectionError(error) except asyncio.TimeoutError as error: - logger.warning('Request timed out while making an HTTP request.') + logger.debug('Request timed out while making an HTTP request.') raise TimeoutError(error) @property diff --git a/cterasdk/clients/decorators.py b/cterasdk/clients/decorators.py index bdac38de..d8d42162 100644 --- a/cterasdk/clients/decorators.py +++ b/cterasdk/clients/decorators.py @@ -1,7 +1,7 @@ import logging import functools -from ..exceptions import SessionExpired, NotLoggedIn +from ..exceptions.session import SessionExpired, NotLoggedIn logger = logging.getLogger('cterasdk.common') diff --git a/cterasdk/clients/errors.py b/cterasdk/clients/errors.py index 25356aa3..97d2c853 100644 --- a/cterasdk/clients/errors.py +++ b/cterasdk/clients/errors.py @@ -1,6 +1,9 @@ from abc import ABC, abstractmethod from http import HTTPStatus -from ..exceptions import HTTPError +from ..exceptions.transport import ( + BadRequest, Unauthorized, Forbidden, NotFound, Unprocessable, + InternalServerError, BadGateway, ServiceUnavailable, GatewayTimeout, HTTPError +) from ..common import Object from ..convert import fromjsonstr, fromxmlstr @@ -58,60 +61,6 @@ def _accept(self, response, message): return Error(response, fromjsonstr(message) or message) -class BadRequest(HTTPError): - - def __init__(self, error): - super().__init__(HTTPStatus.BAD_REQUEST, error) - - -class Unauthorized(HTTPError): - - def __init__(self, error): - super().__init__(HTTPStatus.UNAUTHORIZED, error) - - -class Forbidden(HTTPError): - - def __init__(self, error): - super().__init__(HTTPStatus.FORBIDDEN, error) - - -class NotFound(HTTPError): - - def __init__(self, error): - super().__init__(HTTPStatus.NOT_FOUND, error) - - -class Unprocessable(HTTPError): - - def __init__(self, error): - super().__init__(HTTPStatus.UNPROCESSABLE_ENTITY, error) - - -class InternalServerError(HTTPError): - - def __init__(self, error): - super().__init__(HTTPStatus.INTERNAL_SERVER_ERROR, error) - - -class BadGateway(HTTPError): - - def __init__(self, error): - super().__init__(HTTPStatus.BAD_GATEWAY, error) - - -class ServiceUnavailable(HTTPError): - - def __init__(self, error): - super().__init__(HTTPStatus.SERVICE_UNAVAILABLE, error) - - -class GatewayTimeout(HTTPError): - - def __init__(self, error): - super().__init__(HTTPStatus.GATEWAY_TIMEOUT, error) - - def raise_error(status, error): exceptions = { HTTPStatus.BAD_REQUEST: BadRequest, diff --git a/cterasdk/clients/tracers/session.py b/cterasdk/clients/tracers/session.py index ee1d56b4..4b8a02f6 100644 --- a/cterasdk/clients/tracers/session.py +++ b/cterasdk/clients/tracers/session.py @@ -1,5 +1,5 @@ import aiohttp -from ...exceptions import SessionExpired +from ...exceptions.session import SessionExpired def tracer(): diff --git a/cterasdk/common/object.py b/cterasdk/common/object.py index 65fd324b..8c641480 100644 --- a/cterasdk/common/object.py +++ b/cterasdk/common/object.py @@ -4,6 +4,9 @@ from collections.abc import MutableMapping +logger = logging.getLogger('cterasdk.common') + + class Object(MutableMapping): # pylint: disable=too-many-instance-attributes def __init__(self, **kwargs): @@ -91,7 +94,7 @@ def find_attr(obj, path): for part in parts: attr = get_attr(attr, part) if attr is None: - logging.getLogger('cterasdk.common').warning('Could not find attribute. %s', {'path': f'/{"/".join(parts)}'}) + logger.warning('Could not find attribute. %s', {'path': f'/{"/".join(parts)}'}) return attr return attr @@ -110,7 +113,7 @@ def get_attr(obj, attr): attr = int(attr) return obj[attr] except ValueError: - logging.getLogger('cterasdk.common').warning('Could not find attribute.') + logger.warning('Could not find attribute.') return None return getattr(obj, attr, None) @@ -129,7 +132,7 @@ def remove_attr(obj, attr): try: delattr(obj, attr) except AttributeError: - logging.getLogger('cterasdk.common').warning('Failed to remove attribute. Attribute not found. %s', {'attr': attr}) + logger.warning('Failed to remove attribute. Attribute not found. %s', {'attr': attr}) def remove_array_element(array, attr): @@ -139,7 +142,7 @@ def remove_array_element(array, attr): if attr <= len(array) - 1: array.pop(attr) else: - logging.getLogger('cterasdk.common').warning('Could not remove array item. Index out of range. %s', {'index': attr}) + logger.warning('Could not remove array item. Index out of range. %s', {'index': attr}) except ValueError: pass diff --git a/cterasdk/common/utils.py b/cterasdk/common/utils.py index ac5ea1da..7693691a 100644 --- a/cterasdk/common/utils.py +++ b/cterasdk/common/utils.py @@ -10,6 +10,9 @@ from .enum import DayOfWeek +logger = logging.getLogger('cterasdk.common') + + def union(g1, g2): """ Create a union of two lists, without duplicates. @@ -138,7 +141,7 @@ def __str__(self): def parse_base_object_ref(base_object_ref): if not base_object_ref.startswith('objs/'): - logging.getLogger('cterasdk.common').error('Invalid base object reference. %s', {'ref': base_object_ref}) + logger.error('Invalid base object reference. %s', {'ref': base_object_ref}) return None base_object_ref = base_object_ref[5:] p = re.compile('[^/]*') @@ -161,15 +164,15 @@ def parse_to_ipaddress(address): try: try: ip_addrr = ipaddress.ip_address(address) - logging.getLogger('cterasdk.common').debug('ip address validated. %s', {'ip': str(ip_addrr)}) + logger.debug('ip address validated. %s', {'ip': str(ip_addrr)}) return ip_addrr except (ValueError, TypeError): ip_network = ipaddress.ip_network(address) - logging.getLogger('cterasdk.common').debug('ip network validated. %s', {'network': str(ip_network)}) + logger.debug('ip network validated. %s', {'network': str(ip_network)}) return ip_network except (ValueError, TypeError): err = ValueError(f'{address} does not appear to be an IPv4 or IPv6 network or ip address') - logging.getLogger('cterasdk.common').error('Incorrect entry, please use IPv4 or IPv6 CIDR Formats. %s', {'Error': err}) + logger.error('Incorrect entry, please use IPv4 or IPv6 CIDR Formats. %s', {'Error': err}) raise err @@ -220,17 +223,17 @@ def utf8_encode(message): def tcp_connect(host, port): - logging.getLogger('cterasdk.common').debug('Testing connection. %s', {'host': host, 'port': port}) + logger.debug('Testing connection. %s', {'host': host, 'port': port}) message = f"Connection error to remote host {host} on port {port}." sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) rc = None try: rc = sock.connect_ex((host, port)) except socket.gaierror: - logging.getLogger('cterasdk.common').debug(message) + logger.debug(message) raise ConnectionError(message) if rc != 0: - logging.getLogger('cterasdk.common').debug(message) + logger.debug(message) raise ConnectionError(message) - logging.getLogger('cterasdk.common').debug("Connection established to remote host %s on port %s", host, port) + logger.debug("Connection established to remote host %s on port %s", host, port) diff --git a/cterasdk/core/activation.py b/cterasdk/core/activation.py index 9e799683..07597cf8 100644 --- a/cterasdk/core/activation.py +++ b/cterasdk/core/activation.py @@ -3,6 +3,9 @@ from .base_command import BaseCommand +logger = logging.getLogger('cterasdk.core') + + class Activation(BaseCommand): """ Portal activation """ @@ -21,8 +24,8 @@ def generate_code(self, username=None, tenant=None): if tenant: params['portal'] = tenant - logging.getLogger('cterasdk.core').info('Generating device activation code. %s', {'user': username, 'portal': tenant}) + logger.info('Generating device activation code. %s', {'user': username, 'portal': tenant}) response = self._core.api.get('/ssoActivation', params=params) - logging.getLogger('cterasdk.core').info('Generated device activation code. %s', {'user': username, 'portal': tenant}) + logger.info('Generated device activation code. %s', {'user': username, 'portal': tenant}) return response.code diff --git a/cterasdk/core/admins.py b/cterasdk/core/admins.py index add2688e..b51b97ed 100644 --- a/cterasdk/core/admins.py +++ b/cterasdk/core/admins.py @@ -7,6 +7,9 @@ from . import query +logger = logging.getLogger('cterasdk.core') + + class Administrators(BaseCommand): """ Portal Global Administrators User Management APIs @@ -19,7 +22,7 @@ def _get_entire_object(self, name): try: return self._core.api.get(ref) except CTERAException as error: - raise CTERAException('Failed to get the user', error) + raise CTERAException(f'Could not retrieve user: {ref}') from error def get(self, name, include=None): """ @@ -34,7 +37,7 @@ def get(self, name, include=None): include = ['/' + attr for attr in include] user_object = self._core.api.get_multi(baseurl, include) if user_object.name is None: - raise ObjectNotFoundException('Could not find user', baseurl, username=name) + raise ObjectNotFoundException(baseurl) return user_object def list_admins(self, include=None): @@ -78,9 +81,9 @@ def add(self, name, email, first_name, last_name, password, role, company=None, if password_change: param.requirePasswordChangeOn = DateTimeUtils.get_expiration_date(password_change).strftime('%Y-%m-%d') - logging.getLogger('cterasdk.core').info('Creating a global administrator. %s', {'user': name}) + logger.info('Creating a global administrator. %s', {'user': name}) response = self._core.api.add('/administrators', param) - logging.getLogger('cterasdk.core').info('Global administrator created. %s', {'user': name, 'email': email, 'role': role}) + logger.info('Global administrator created. %s', {'user': name, 'email': email, 'role': role}) return response @@ -117,13 +120,14 @@ def modify(self, current_username, new_username=None, email=None, first_name=Non if comment is not None: user.comment = comment + ref = f'/administrators/{current_username}' try: - response = self._core.api.put('/administrators/' + current_username, user) - logging.getLogger('cterasdk.core').info("User modified. %s", {'username': user.name}) + response = self._core.api.put(ref, user) + logger.info("User modified. %s", {'username': user.name}) return response except CTERAException as error: - logging.getLogger('cterasdk.core').error("Failed to modify user.") - raise CTERAException('Failed to modify user', error) + logger.error('Could not modify user: %s', ref) + raise CTERAException(f'Could not modify user: {ref}') from error def delete(self, name): """ @@ -131,9 +135,8 @@ def delete(self, name): :param str username: Global administrator username """ - logging.getLogger('cterasdk.core').info('Deleting user. %s', {'user': name}) + logger.info('Deleting user. %s', {'user': name}) baseurl = f'/administrators/{name}' response = self._core.api.execute(baseurl, 'delete', True) - logging.getLogger('cterasdk.core').info('User deleted. %s', {'user': name}) - + logger.info('User deleted. %s', {'user': name}) return response diff --git a/cterasdk/core/antivirus.py b/cterasdk/core/antivirus.py index 3e69582e..e5cd8d61 100644 --- a/cterasdk/core/antivirus.py +++ b/cterasdk/core/antivirus.py @@ -6,6 +6,9 @@ from .base_command import BaseCommand +logger = logging.getLogger('cterasdk.core') + + class Antivirus(BaseCommand): """ Portal Antivirus APIs @@ -33,25 +36,25 @@ def rescan(self): """ Scan all files using the latest antivirus update. This may take a while """ - logging.getLogger('cterasdk.core').info("Starting antivirus rescan.") + logger.info("Starting antivirus rescan.") self._core.api.execute('/servers', 'resetAllAVBG') - logging.getLogger('cterasdk.core').info("Started antivirus rescan.") + logger.info("Started antivirus rescan.") def suspend(self): """ Suspend antivirus scanning """ - logging.getLogger('cterasdk.core').info("Suspending antivirus scanning.") + logger.info("Suspending antivirus scanning.") self._core.api.put('/settings/cloudFSSettings/antivirusSettings/isEnabled', False) - logging.getLogger('cterasdk.core').info("Suspended antivirus scanning.") + logger.info("Suspended antivirus scanning.") def unsuspend(self): """ Unsuspend antivirus scanning """ - logging.getLogger('cterasdk.core').info("Unsuspending antivirus scanning.") + logger.info("Unsuspending antivirus scanning.") self._core.api.put('/settings/cloudFSSettings/antivirusSettings/isEnabled', True) - logging.getLogger('cterasdk.core').info("Unsuspended antivirus scanning.") + logger.info("Unsuspended antivirus scanning.") def status(self): """ @@ -87,9 +90,9 @@ def add(self, name, vendor, url, connection_timeout=5): param.type = vendor param.serverUrl = url param.connectionTimeoutSeconds = connection_timeout - logging.getLogger('cterasdk.core').info("Adding antivirus server. %s", {'name': name, 'type': vendor, 'url': url}) + logger.info("Adding antivirus server. %s", {'name': name, 'type': vendor, 'url': url}) response = self._core.api.add('/antiviruses', param) - logging.getLogger('cterasdk.core').info("Added antivirus server. %s", {'name': name, 'type': vendor, 'url': url}) + logger.info("Added antivirus server. %s", {'name': name, 'type': vendor, 'url': url}) return response def delete(self, name): @@ -102,14 +105,14 @@ def suspend(self, name): """ Suspend an antivirus server """ - logging.getLogger('cterasdk.core').info("Suspending antivirus server. %s", {'name': name}) + logger.info("Suspending antivirus server. %s", {'name': name}) self._core.api.put(f'/antiviruses/{name}/enabled', False) - logging.getLogger('cterasdk.core').info("Suspended antivirus server. %s", {'name': name}) + logger.info("Suspended antivirus server. %s", {'name': name}) def unsuspend(self, name): """ Unsuspend antivirus scanning """ - logging.getLogger('cterasdk.core').info("Unsuspending antivirus server. %s", {'name': name}) + logger.info("Unsuspending antivirus server. %s", {'name': name}) self._core.api.put(f'/antiviruses/{name}/enabled', True) - logging.getLogger('cterasdk.core').info("Unsuspended antivirus server. %s", {'name': name}) + logger.info("Unsuspended antivirus server. %s", {'name': name}) diff --git a/cterasdk/core/buckets.py b/cterasdk/core/buckets.py index 147d2d7f..6d7e4989 100644 --- a/cterasdk/core/buckets.py +++ b/cterasdk/core/buckets.py @@ -6,6 +6,9 @@ from . import query +logger = logging.getLogger('cterasdk.core') + + class Buckets(BaseCommand): """ Portal Storage Node APIs @@ -14,10 +17,11 @@ class Buckets(BaseCommand): default = ['name'] def _get_entire_object(self, name): + ref = f'/locations/{name}' try: - return self._core.api.get(f'/locations/{name}') + return self._core.api.get(ref) except CTERAException as error: - raise CTERAException('Failed to get bucket', error) + raise CTERAException(f'Bucket not found: {ref}') from error def _get_tenant_base_object_ref(self, name): return self._core.portals.get(name, include=['baseObjectRef']).baseObjectRef @@ -33,7 +37,7 @@ def get(self, name, include=None): include = ['/' + attr for attr in include] bucket = self._core.api.get_multi('/locations/' + name, include) if bucket.name is None: - raise ObjectNotFoundException('Could not find bucket', f'/locations/{name}', name=name) + raise ObjectNotFoundException(f'/locations/{name}') return bucket def add(self, name, bucket, read_only=False, dedicated_to=None): @@ -51,10 +55,10 @@ def add(self, name, bucket, read_only=False, dedicated_to=None): param.dedicated = bool(dedicated_to) param.dedicatedPortal = self._get_tenant_base_object_ref(dedicated_to) if dedicated_to else None - logging.getLogger('cterasdk.core').info('Adding bucket. %s', + logger.info('Adding bucket. %s', {'name': name, 'bucket': bucket.bucket, 'type': bucket.__class__.__name__}) response = self._core.api.add('/locations', param) - logging.getLogger('cterasdk.core').info('Bucket added. %s', + logger.info('Bucket added. %s', {'name': name, 'bucket': bucket.bucket, 'type': bucket.__class__.__name__}) return response @@ -86,9 +90,9 @@ def modify(self, current_name, new_name=None, read_only=None, dedicated_to=None, param.dedicatedPortal = self._get_tenant_base_object_ref(dedicated_to) if dedicated_to else None if verify_ssl is not None: param.trustAllCertificates = not verify_ssl - logging.getLogger('cterasdk.core').info("Modifying bucket. %s", {'name': current_name}) + logger.info("Modifying bucket. %s", {'name': current_name}) response = self._core.api.put(f'/locations/{current_name}', param) - logging.getLogger('cterasdk.core').info("Bucket modified. %s", {'name': current_name}) + logger.info("Bucket modified. %s", {'name': current_name}) return response def list_buckets(self, include=None): @@ -108,9 +112,9 @@ def delete(self, name): :param str name: Name of the bucket """ - logging.getLogger('cterasdk.core').info('Deleting bucket. %s', {'name': name}) + logger.info('Deleting bucket. %s', {'name': name}) response = self._core.api.delete(f'/locations/{name}') - logging.getLogger('cterasdk.core').info('Bucket deleted. %s', {'name': name}) + logger.info('Bucket deleted. %s', {'name': name}) return response def read_write(self, name): @@ -119,7 +123,7 @@ def read_write(self, name): :param str name: Name of the bucket """ - logging.getLogger('cterasdk.core').info('Setting bucket to read-write. %s', {'name': name}) + logger.info('Setting bucket to read-write. %s', {'name': name}) return self._read_only(name, False) def read_only(self, name): @@ -128,7 +132,7 @@ def read_only(self, name): :param str name: Name of the bucket """ - logging.getLogger('cterasdk.core').info('Setting bucket to read-delete only. %s', {'name': name}) + logger.info('Setting bucket to read-delete only. %s', {'name': name}) return self._read_only(name, True) def _read_only(self, name, enabled): diff --git a/cterasdk/core/cli.py b/cterasdk/core/cli.py index ff45f5f5..4f461b0a 100644 --- a/cterasdk/core/cli.py +++ b/cterasdk/core/cli.py @@ -3,6 +3,9 @@ from .base_command import BaseCommand +logger = logging.getLogger('cterasdk.core') + + class CLI(BaseCommand): """ Portal CLI APIs """ @@ -13,10 +16,10 @@ def run_command(self, cli_command): :param str cli_command: The CLI command to run on the gateway :return str: The response of the Portal """ - logging.getLogger('cterasdk.core').warning('Usage of the CLI module is discouraged. ' + logger.warning('Usage of the CLI module is discouraged. ' 'Review available modules to determine if there are existing ones that ' 'support this action.') - logging.getLogger('cterasdk.core').info("Executing CLI command. %s", {'cli_command': cli_command}) + logger.info("Executing CLI command. %s", {'cli_command': cli_command}) response = self._core.api.execute('', 'debugCmd', cli_command) - logging.getLogger('cterasdk.core').info("CLI command executed. %s", {'cli_command': cli_command}) + logger.info("CLI command executed. %s", {'cli_command': cli_command}) return response diff --git a/cterasdk/core/cloudfs.py b/cterasdk/core/cloudfs.py index fa656e75..7084d634 100644 --- a/cterasdk/core/cloudfs.py +++ b/cterasdk/core/cloudfs.py @@ -10,6 +10,9 @@ from ..exceptions import CTERAException, ObjectNotFoundException +logger = logging.getLogger('cterasdk.core') + + class CloudFS(BaseCommand): """ CloudFS APIs @@ -36,10 +39,11 @@ class FolderGroups(BaseCommand): default = ['name', 'owner'] def _get_entire_object(self, name): + ref = f'/foldersGroups/{name}' try: - return self._core.api.get(f'/foldersGroups/{name}') + return self._core.api.get(ref) except CTERAException as error: - raise CTERAException('Failed to get folder group', error) + raise CTERAException(f'Folder group not found: {ref}') from error def get(self, name, include=None): """ @@ -52,7 +56,7 @@ def get(self, name, include=None): include = ['/' + attr for attr in include] folder_group = self._core.api.get_multi(f'/foldersGroups/{name}', include) if folder_group.name is None: - raise ObjectNotFoundException('Could not find folder group', f'/foldersGroups/{name}', name=name) + raise ObjectNotFoundException(f'/foldersGroups/{name}') return folder_group def all(self, include=None, user=None): @@ -92,10 +96,10 @@ def add(self, name, user=None, deduplication_method_type=None, storage_class=Non try: response = self._core.api.execute('', 'createFolderGroup', param) - logging.getLogger('cterasdk.core').info('Folder group created. %s', {'name': name, 'owner': param.owner}) + logger.info('Folder group created. %s', {'name': name, 'owner': param.owner}) return response except CTERAException as error: - logging.getLogger('cterasdk.core').error('Folder group creation failed. %s', {'name': name, 'owner': str(user)}) + logger.error('Folder group creation failed. %s', {'name': name, 'owner': str(user)}) raise error def modify(self, current_name, new_name): @@ -109,13 +113,14 @@ def modify(self, current_name, new_name): if new_name: param.name = new_name + ref = f'/foldersGroups/{current_name}' try: - response = self._core.api.put(f'/foldersGroups/{current_name}', param) - logging.getLogger('cterasdk.core').info('Folder group updated. %s', {'name': current_name}) + response = self._core.api.put(ref, param) + logger.info('Folder group modified: %s', current_name) return response except CTERAException as error: - logging.getLogger('cterasdk.core').error('Folder group update failed. %s', {'name': current_name}) - raise error + logger.error('Folder group modification failed: %s', current_name) + raise CTERAException(f'Folder group modification failed: {ref}') def delete(self, name): """ @@ -124,9 +129,9 @@ def delete(self, name): :param str name: Name of the folder group to remove """ - logging.getLogger('cterasdk.core').info('Deleting folder group. %s', {'name': name}) + logger.info('Deleting folder group. %s', {'name': name}) self._core.api.execute('/foldersGroups/' + name, 'deleteGroup', True) - logging.getLogger('cterasdk.core').info('Folder group deleted. %s', {'name': name}) + logger.info('Folder group deleted. %s', {'name': name}) class CloudDrives(BaseCommand): @@ -176,13 +181,13 @@ def add(self, name, group, owner, winacls=True, description=None, quota=None, co try: response = self._core.api.execute('', 'addCloudDrive', param) - logging.getLogger('cterasdk.core').info( + logger.info( 'Cloud drive folder created. %s', {'name': name, 'owner': param.owner, 'folder_group': group, 'winacls': winacls} ) return response except CTERAException as error: - logging.getLogger('cterasdk.core').error( + logger.error( 'Cloud drive folder creation failed. %s', {'name': name, 'folder_group': group, 'owner': owner, 'win_acls': winacls} ) @@ -225,10 +230,10 @@ def modify(self, current_name, owner, new_name=None, new_owner=None, new_group=N param.extendedAttributes = xattrs try: response = self._core.api.put(f'/{param.baseObjectRef}', param) - logging.getLogger('cterasdk.core').info('Cloud drive folder updated. %s', {'name': current_name}) + logger.info('Cloud drive folder updated. %s', {'name': current_name}) return response except CTERAException as error: - logging.getLogger('cterasdk.core').error('Cloud drive folder update failed. %s', {'name': current_name}) + logger.error('Cloud drive folder update failed. %s', {'name': current_name}) raise error def all(self, include=None, list_filter=ListFilter.NonDeleted, user=None): @@ -274,8 +279,8 @@ def find(self, name, owner, include=None): try: return next(iterator) except StopIteration: - logging.getLogger('cterasdk.core').info('Could not find cloud folder. %s', {'folder': name, 'owner': str(owner)}) - raise CTERAException('Could not find cloud folder', None, folder=name, owner=str(owner)) + logger.info('Could not find cloud folder. %s', {'folder': name, 'owner': str(owner)}) + raise CTERAException(f"Unable to locate the Cloud Drive folder '{name}' owned by '{str(owner)}.") def delete(self, name, owner, *, permanently=False): """ @@ -286,13 +291,13 @@ def delete(self, name, owner, *, permanently=False): :param bool,optional permanently: Delete permanently """ cloudfolder = self.find(name, owner, include=['uid', 'isDeleted']) - logging.getLogger('cterasdk.core').info('Deleting cloud drive folder. %s', + logger.info('Deleting cloud drive folder. %s', {'name': name, 'owner': str(owner), 'permanently': permanently}) if permanently: return self._core.api.execute(f'/objs/{cloudfolder.uid}', 'deleteFolderPermanently') if not cloudfolder.isDeleted: return self._core.api.execute(f'/objs/{cloudfolder.uid}', 'delete') - logging.getLogger('cterasdk.core').info('Cloud Drive folder was already deleted. %s', {'name': cloudfolder.name}) + logger.info('Cloud Drive folder was already deleted. %s', {'name': cloudfolder.name}) return None def recover(self, name, owner): @@ -303,12 +308,12 @@ def recover(self, name, owner): :param cterasdk.core.types.UserAccount owner: User account, the owner of the Cloud Drive Folder to delete """ display_name = self._core.users.get(owner, ['displayName']).displayName - logging.getLogger('cterasdk.core').info('Recovering cloud drive folder. %s', {'name': name, 'owner': str(owner)}) + logger.info('Recovering cloud drive folder. %s', {'name': name, 'owner': str(owner)}) return self._core.files.undelete(f'Users/{display_name}/{name}') def setfacl(self, paths, acl, recursive=False): """ - Changing the file or Folder ACLs + Changing the file or folder ACLs :param list(str) paths: List of folder paths :param str acl: Access control list (ACL) represented as an SDDL String @@ -322,8 +327,8 @@ def setfacl(self, paths, acl, recursive=False): try: return self._core.api.execute('', 'setFoldersACL', param) except CTERAException as error: - logging.getLogger('cterasdk.core').error('setFoldersACL failed. %s', {'error': error}) - raise CTERAException('Failed to setFoldersACL', error) + logger.error('An error occurred attempting to update access control entries.') + raise CTERAException('ACL modification failed.') from error def setoacl(self, paths, owner_sid, recursive=False): """ @@ -341,8 +346,8 @@ def setoacl(self, paths, owner_sid, recursive=False): try: return self._core.api.execute('', 'setOwnerACL', param) except CTERAException as error: - logging.getLogger('cterasdk.core').error('setOwnerACL failed. %s', {'error': error}) - raise CTERAException('Failed to setOwnerACL', error) + logger.error('An error occurred attempting to update owner.') + raise CTERAException('Owner modification failed.') from error class Backups(BaseCommand): @@ -370,13 +375,13 @@ def add(self, name, group, owner, xattr=True): param.enableBackupExtendedAttributes = xattr try: response = self._core.api.add('/backups', param) - logging.getLogger('cterasdk.core').info( + logger.info( 'Backup folder created. %s', {'name': name, 'owner': param.owner, 'folder_group': group, 'xattr': xattr} ) return response except CTERAException as error: - logging.getLogger('cterasdk.core').error( + logger.error( 'Backup folder creation failed. %s', {'name': name, 'folder_group': group, 'owner': owner, 'xattr': xattr} ) @@ -404,10 +409,10 @@ def modify(self, current_name, new_name=None, new_owner=None, new_group=None, xa param.enableBackupExtendedAttributes = xattr try: response = self._core.api.put(f'/backups/{current_name}', param) - logging.getLogger('cterasdk.core').info('Backup folder updated. %s', {'name': current_name}) + logger.info('Backup folder updated. %s', {'name': current_name}) return response except CTERAException as error: - logging.getLogger('cterasdk.core').error('Backup folder update failed. %s', {'name': current_name}) + logger.error('Backup folder update failed. %s', {'name': current_name}) raise error def all(self, include=None, list_filter=ListFilter.NonDeleted, user=None): @@ -438,9 +443,9 @@ def delete(self, name): :param str name: Name of the Backup Folder to delete """ - logging.getLogger('cterasdk.core').info('Deleting Backup folder. %s', {'name': name}) + logger.info('Deleting Backup folder. %s', {'name': name}) response = self._core.api.execute(f'/backups/{name}', 'delete') - logging.getLogger('cterasdk.core').info('Backup folder deleted. %s', {'name': name}) + logger.info('Backup folder deleted. %s', {'name': name}) return response @@ -461,17 +466,17 @@ def get(self, name): query_filter = query.FilterBuilder('name').eq(name) param = query.QueryParamBuilder().include_classname().startFrom(0).countLimit(1).addFilter(query_filter).orFilter(False).build() - logging.getLogger('cterasdk.core').info('Retrieving zone. %s', {'name': name}) + logger.info('Retrieving zone. %s', {'name': name}) response = self._core.api.execute('', 'getZonesDisplayInfo', param) objects = response.objects if len(objects) < 1: - logging.getLogger('cterasdk.core').error('Zone not found. %s', {'name': name}) - raise CTERAException('Zone not found', None, name=name) + logger.error('Zone not found. %s', {'name': name}) + raise CTERAException(f'Zone not found: {name}') zone = objects[0] - logging.getLogger('cterasdk.core').info('Zone found. %s', {'name': name, 'id': zone.zoneId}) + logger.info('Zone found. %s', {'name': name, 'id': zone.zoneId}) return zone def all(self, filters=None): @@ -512,14 +517,14 @@ def add(self, name, policy_type=PolicyType.SELECT, description=None): """ param = self._zone_param(name, policy_type, description) - logging.getLogger('cterasdk.core').info('Adding zone. %s', {'name': name}) + logger.info('Adding zone. %s', {'name': name}) response = self._core.api.execute('', 'addZone', param) try: self._process_response(response) - logging.getLogger('cterasdk.core').info('Zone added. %s', {'name': name}) + logger.info('Zone added. %s', {'name': name}) except CTERAException as error: - logging.getLogger('cterasdk.core').error('Zone creation failed. %s', {'rc': response.rc}) + logger.error('Zone creation failed. %s', {'rc': response.rc}) raise error def delete(self, name): @@ -529,10 +534,10 @@ def delete(self, name): :param str name: The name of the zone to delete """ zone = self._core.cloudfs.zones.get(name) - logging.getLogger('cterasdk.core').info('Deleting zone. %s', {'zone': name}) + logger.info('Deleting zone. %s', {'zone': name}) response = self._core.api.execute('', 'deleteZones', [zone.zoneId]) if response == 'ok': - logging.getLogger('cterasdk.core').info('Zone deleted. %s', {'zone': name}) + logger.info('Zone deleted. %s', {'zone': name}) def add_devices(self, name, device_names): """ @@ -550,13 +555,13 @@ def add_devices(self, name, device_names): for portal_device in portal_devices: param.delta.devicesDelta.added.append(portal_device.uid) - logging.getLogger('cterasdk.core').info('Adding devices to zone. %s', {'zone': info.name}) + logger.info('Adding devices to zone. %s', {'zone': info.name}) try: self._save(param) except CTERAException as error: - logging.getLogger('cterasdk.core').error('Failed adding devices to zone.') - raise CTERAException('Failed adding devices to zone', error, zone=name, devices=device_names) + logger.error('Failed adding devices to zone.') + raise CTERAException(f"Failed adding devices: [{', '.join(device_names)}] to zone: {name}") from error def add_folders(self, name, folder_finding_helpers): """ @@ -586,13 +591,13 @@ def add_folders(self, name, folder_finding_helpers): try: self._save(param) except CTERAException as error: - logging.getLogger('cterasdk.core').error('Failed adding folders to zone.') - raise CTERAException('Failed adding folders to zone', error, zone=name) + logger.error('Failed adding folders to zone.') + raise CTERAException(f'Failed adding folders to zone: {name}') from error def _zone_info(self, zid): - logging.getLogger('cterasdk.core').debug('Obtaining zone info. %s', {'id': zid}) + logger.debug('Obtaining zone info. %s', {'id': zid}) response = self._core.api.execute('', 'getZoneBasicInfo', zid) - logging.getLogger('cterasdk.core').debug('Obtained zone info. %s', {'id': zid}) + logger.debug('Obtained zone info. %s', {'id': zid}) return response def _find_folders(self, folder_finding_helpers): @@ -616,21 +621,21 @@ def _find_folders(self, folder_finding_helpers): def _save(self, param): zone_name = param.basicInfo.name - logging.getLogger('cterasdk.core').debug('Applying changes to zone. %s', {'zone': param.basicInfo.name}) + logger.debug('Applying changes to zone. %s', {'zone': param.basicInfo.name}) response = self._core.api.execute('', 'saveZone', param) try: self._process_response(response) except CTERAException as error: - logging.getLogger('cterasdk.core').error('Failed applying changes to zone. %s', {'zone': zone_name, 'rc': response.rc}) + logger.error('Failed applying changes to zone. %s', {'zone': zone_name, 'rc': response.rc}) raise error - logging.getLogger('cterasdk.core').debug('Zone changes applied successfully. %s', {'zone': zone_name}) + logger.debug('Zone changes applied successfully. %s', {'zone': zone_name}) @staticmethod def _process_response(response): if response.rc != 'OK': - raise CTERAException('Zone creation failed', response) + raise CTERAException(f'Zone creation failed: {response}') @staticmethod def _zone_param(name, policy_type, description=None, zid=None): @@ -693,9 +698,9 @@ def add(self, name, drive_name, drive_owner, description=None): param.description = description param.name = name param.cloudDrive = self._core.cloudfs.drives.find(drive_name, drive_owner, include=['baseObjectRef']).baseObjectRef - logging.getLogger('cterasdk.core').info('Adding Bucket. %s', {'name': name}) + logger.info('Adding Bucket. %s', {'name': name}) response = self._core.api.add('/buckets', param) - logging.getLogger('cterasdk.core').info('Bucket Added. %s', {'name': name}) + logger.info('Bucket Added. %s', {'name': name}) return response def modify(self, name, description): @@ -708,9 +713,9 @@ def modify(self, name, description): bucket = self.get(name) bucket.description = description - logging.getLogger('cterasdk.core').info("Modifying Bucket. %s", {'name': name}) + logger.info("Modifying Bucket. %s", {'name': name}) response = self._core.api.put(f'/buckets/{name}', bucket) - logging.getLogger('cterasdk.core').info("Bucket modified. %s", {'name': name}) + logger.info("Bucket modified. %s", {'name': name}) return response def delete(self, name): @@ -719,7 +724,7 @@ def delete(self, name): :param str name: Bucket name """ - logging.getLogger('cterasdk.core').info('Deleting Bucket. %s', {'name': name}) + logger.info('Deleting Bucket. %s', {'name': name}) response = self._core.api.delete(f'/buckets/{name}') - logging.getLogger('cterasdk.core').info('Bucket deleted. %s', {'name': name}) + logger.info('Bucket deleted. %s', {'name': name}) return response diff --git a/cterasdk/core/connection.py b/cterasdk/core/connection.py index 2d48496f..85ff3d3d 100644 --- a/cterasdk/core/connection.py +++ b/cterasdk/core/connection.py @@ -2,10 +2,13 @@ from ..common.utils import tcp_connect +logger = logging.getLogger('cterasdk.core') + + def test(Portal): tcp_connect(Portal.host(), Portal.port()) - logging.getLogger('cterasdk.core').debug('Trying to obtain Portal public info.') + logger.debug('Trying to obtain Portal public info.') response = Portal.public_info() - logging.getLogger('cterasdk.core').debug('Successfully obtained Portal public info. %s', + logger.debug('Successfully obtained Portal public info. %s', {'version': response.version, 'name': response.name}) return response diff --git a/cterasdk/core/credentials.py b/cterasdk/core/credentials.py index 32ab39fa..7c0c333f 100644 --- a/cterasdk/core/credentials.py +++ b/cterasdk/core/credentials.py @@ -4,6 +4,9 @@ from .base_command import BaseCommand +logger = logging.getLogger('cterasdk.core') + + class Credentials(BaseCommand): """ Portal Credential Management APIs @@ -30,7 +33,7 @@ def all(self, user_account=None): """ user_account = self._user_account(user_account) param = self._core.users.get(user_account, ['uid']).uid - logging.getLogger('cterasdk.core').info('Listing Credentials. %s', {'user': str(user_account)}) + logger.info('Listing Credentials. %s', {'user': str(user_account)}) return self._core.api.execute('', 'getApiKeys', param) def create(self, user_account=None): @@ -41,9 +44,9 @@ def create(self, user_account=None): """ user_account = self._user_account(user_account) param = self._core.users.get(user_account, ['uid']).uid - logging.getLogger('cterasdk.core').info('Creating Credential. %s', {'type': 's3', 'user': str(user_account)}) + logger.info('Creating Credential. %s', {'type': 's3', 'user': str(user_account)}) response = self._core.api.execute('', 'createApiKey', param) - logging.getLogger('cterasdk.core').info('Credetial created. %s', {'type': 's3', 'user': str(user_account)}) + logger.info('Credetial created. %s', {'type': 's3', 'user': str(user_account)}) return response def delete(self, access_key_id, user_account=None): @@ -56,9 +59,9 @@ def delete(self, access_key_id, user_account=None): user_account = self._user_account(user_account) for key in self.all(user_account): if key.accessKey == access_key_id: - logging.getLogger('cterasdk.core').info('Deleting Credential. %s', {'type': 's3'}) + logger.info('Deleting Credential. %s', {'type': 's3'}) response = self._core.api.execute('', 'deleteApiKey', key.uid) - logging.getLogger('cterasdk.core').info('Credetial deleted. %s', {'type': 's3'}) + logger.info('Credetial deleted. %s', {'type': 's3'}) return response - logging.getLogger('cterasdk.core').warning('Could not find access key. %s', {'key': access_key_id}) + logger.warning('Could not find access key. %s', {'key': access_key_id}) return None diff --git a/cterasdk/core/devices.py b/cterasdk/core/devices.py index bcfb270e..a23ef481 100644 --- a/cterasdk/core/devices.py +++ b/cterasdk/core/devices.py @@ -2,7 +2,8 @@ from .enum import DeviceType from . import remote, query from ..common import union -from ..exceptions import CTERAException, ObjectNotFoundException +from ..exceptions import ObjectNotFoundException +from ..exceptions.session import ContextError class Devices(BaseCommand): @@ -16,7 +17,7 @@ def _create_device_resource_uri(self, device_name, tenant): session = self._core.session() if not tenant: if not session.in_tenant_context(): - raise CTERAException('You must specify a tenant name or browse the tenant first.') + raise ContextError('Browse a Tenant, or specify the Tenant name to invoke this API') tenant = self._core.session().current_tenant() resource_uri = f'/portals/{tenant}/devices/{device_name}' # regular auth: support both tenant and Administration context return resource_uri @@ -39,7 +40,7 @@ def device(self, device_name, tenant=None, include=None): dev = self._core.api.get_multi(resource_uri, include) if dev.name is None: - raise ObjectNotFoundException('Device not found', resource_uri, tenant=tenant, name=device_name) + raise ObjectNotFoundException(resource_uri) return remote.remote_command(self._core, dev) diff --git a/cterasdk/core/directoryservice.py b/cterasdk/core/directoryservice.py index 9e1c849b..3aaae8b2 100644 --- a/cterasdk/core/directoryservice.py +++ b/cterasdk/core/directoryservice.py @@ -2,11 +2,14 @@ from .base_command import BaseCommand from ..common import Object -from ..exceptions import CTERAException +from ..exceptions import CTERAException, InputError from .enum import PortalAccountType, SearchType, DirectoryServiceType, DirectoryServiceFetchMode, Role, DirectorySearchEntityType from .types import AccessControlEntry, AccessControlRule, UserAccount, GroupAccount +logger = logging.getLogger('cterasdk.core') + + class DirectoryService(BaseCommand): """ Portal Active Directory APIs @@ -68,13 +71,13 @@ def connect(self, domain, username, password, directory=DirectoryServiceType.Mic param.ipAddresses.ipAddress2 = domain_controllers.secondary tenant = self._core.session().account.tenant - logging.getLogger('cterasdk.core').info('Connecting Portal to directory services. %s', { + logger.info('Connecting Portal to directory services. %s', { 'tenant': tenant, 'type': type, 'domain': domain }) self._connect_to_directory_services(param) - logging.getLogger('cterasdk.core').info('Connected Portal to directory services. %s', {'tennat': tenant, 'domain': domain}) + logger.info('Connected Portal to directory services. %s', {'tennat': tenant, 'domain': domain}) if mapping: self._configure_advanced_mapping(mapping) @@ -109,11 +112,11 @@ def _configure_advanced_mapping(self, mapping): param = Object() param._classname = 'ADIDMapping' # pylint: disable=protected-access param.map = mapping - logging.getLogger('cterasdk.core').debug('Updating advanced mapping. %s', { + logger.debug('Updating advanced mapping. %s', { 'domains': [mapping.domainFlatName for mapping in param.map] }) response = self._core.api.put('/directoryConnector/idMapping', param) - logging.getLogger('cterasdk.core').info('Updated advanced mapping.') + logger.info('Updated advanced mapping.') return response def get_access_control(self): @@ -157,14 +160,14 @@ def _configure_access_control(self, acl, default=None): account = self._search_groups(ace.account.directory, ace.account.name) access_control_rules.append(AccessControlRule(account, ace.role)) - logging.getLogger('cterasdk.core').info('Updating access control rules.') + logger.info('Updating access control rules.') response = self._core.api.put('/directoryConnector/accessControlRules', access_control_rules) - logging.getLogger('cterasdk.core').info('Updated access control rules.') + logger.info('Updated access control rules.') if default is not None: - logging.getLogger('cterasdk.core').info('Updating default role.') + logger.info('Updating default role.') response = self._core.api.put('/directoryConnector/noMatchRole', default) - logging.getLogger('cterasdk.core').info('Updated default role') + logger.info('Updated default role') return response @@ -209,12 +212,13 @@ def fetch(self, active_directory_accounts): param = [] for active_directory_account in active_directory_accounts: if active_directory_account.directory not in domains: - logging.getLogger('cterasdk.core').error('Invalid domain name. %s', {'domain': active_directory_account.directory}) - raise CTERAException('Invalid domain', None, domain=active_directory_account.directory, domains=domains) + logger.error('Invalid domain name. %s', {'domain': active_directory_account.directory}) + raise InputError(f'Invalid domain: {active_directory_account.directory}', active_directory_account.directory, domains) if active_directory_account.account_type not in account_types: - logging.getLogger('cterasdk.core').error('Invalid account type. %s', {'type': active_directory_account.account_type}) - raise CTERAException('Invalid account type', None, type=active_directory_account.account_type, options=account_types) + logger.error('Invalid account type. %s', {'type': active_directory_account.account_type}) + raise InputError(f'Invalid account type: {active_directory_account.account_type}', active_directory_account.account_type, + account_types) for active_directory_account in active_directory_accounts: if active_directory_account.account_type == PortalAccountType.User: @@ -222,9 +226,9 @@ def fetch(self, active_directory_accounts): elif active_directory_account.account_type == PortalAccountType.Group: param.append(self._search_groups(active_directory_account.directory, active_directory_account.name)) - logging.getLogger('cterasdk.core').info('Starting to fetch users and groups.') + logger.info('Starting to fetch users and groups.') response = self._core.api.execute('', 'syncAD', param) - logging.getLogger('cterasdk.core').info('Started fetching users and groups.') + logger.info('Started fetching users and groups.') return response @@ -240,35 +244,19 @@ def _search_directory_services(self, search_type, domain, name): param.name = name param.domain = domain - logging.getLogger('cterasdk.core').info( - 'Searching %(search_type)s: %(info)s', - dict(search_type=search_type, info={'domain': domain, 'name': name}) - ) + logger.info('Searching %(search_type)s: %(info)s', dict(search_type=search_type, info={'domain': domain, 'name': name})) objects = self._core.api.execute('', 'searchAD', param) if not objects: - logging.getLogger('cterasdk.core').info('Could not find results that match your search criteria. %s', - {'domain': domain, 'name': name}) - raise CTERAException( - 'Could not find results that match your search criteria', - None, - domain=domain, - type=search_type, - account=name - ) + logger.info('Could not find %s: %s@%s.', search_type, name, domain) + raise CTERAException(f'Could not find {search_type[:-1]}: {name}@{domain}') for principal in objects: if principal.name == name: - logging.getLogger('cterasdk.core').info('Found. %s', {'domain': domain, 'name': name}) + logger.info('Found. %s', {'domain': domain, 'name': name}) return principal - raise CTERAException( - 'Search returned multiple results, but none matched your search criteria', - None, - domain=domain, - type=search_type, - account=name - ) + raise CTERAException('Search returned multiple results, but none matched the specified criteria.') def disconnect(self): """ diff --git a/cterasdk/core/files/browser.py b/cterasdk/core/files/browser.py index 5336d9d9..e353c629 100644 --- a/cterasdk/core/files/browser.py +++ b/cterasdk/core/files/browser.py @@ -7,6 +7,9 @@ from . import io +logger = logging.getLogger('cterasdk.core') + + class FileBrowser(BaseCommand): def __init__(self, core): @@ -278,6 +281,5 @@ def device_config(self, device, destination=None): destination = destination if destination is not None else f'{commonfs.downloads()}/{device}.xml' return self.download(f'backups/{device}/Device Configuration/db.xml', destination) except CTERAException as error: - logging.getLogger('cterasdk.core').error('Failed downloading configuration file. %s', - {'device': device, 'error': error.response.reason}) + logger.error('Failed downloading configuration file. %s', {'device': device, 'error': error.response.reason}) raise error diff --git a/cterasdk/core/files/io.py b/cterasdk/core/files/io.py index 3f2f211d..054baee1 100644 --- a/cterasdk/core/files/io.py +++ b/cterasdk/core/files/io.py @@ -1,7 +1,7 @@ import logging from ...cio.common import encode_request_parameter from ...cio import core as fs -from ...cio import exceptions +from ...exceptions.io import ResourceNotFoundError, RemoteStorageException, ResourceExistsError from ...core import query from ..enum import CollaboratorType from ...lib import FetchResourcesResponse @@ -21,14 +21,14 @@ def exists(core, path): try: metadata(core, path) return True - except exceptions.ResourceNotFoundError: + except ResourceNotFoundError: return False def metadata(core, path): response = listdir(core, path, 0) if response.root is None: - raise exceptions.ResourceNotFoundError(path.absolute) + raise ResourceNotFoundError(path.absolute) return response.root @@ -60,7 +60,7 @@ def makedirs(core, path): path = fs.CorePath.instance(path.scope, '/'.join(directories[:i])) try: mkdir(core, path) - except exceptions.ResourceExistsError: + except ResourceExistsError: logger.debug('Resource already exists: %s', path.reference.as_posix()) @@ -92,7 +92,7 @@ def move(core, *paths, destination=None): def retrieve_remote_dir(core, directory): resource = metadata(core, directory) if not resource.isFolder: - raise exceptions.RemoteStorageException('The destination path is not a directory', None, path=directory.absolute) + raise RemoteStorageException('The destination path is not a directory', path=directory.absolute) return str(resource.cloudFolderInfo.uid) diff --git a/cterasdk/core/groups.py b/cterasdk/core/groups.py index bf49b1a6..4df8a67a 100644 --- a/cterasdk/core/groups.py +++ b/cterasdk/core/groups.py @@ -8,6 +8,9 @@ from . import query +logger = logging.getLogger('cterasdk.core') + + class Groups(BaseCommand): """ Portal Groups Management APIs @@ -25,7 +28,7 @@ def _get_entire_object(self, group_account): try: return self._core.api.get(ref) except CTERAException as error: - raise CTERAException('Failed to retrieve group', error) + raise CTERAException(f'Group not found: {ref}') from error def get(self, group_account, include=None): """ @@ -40,7 +43,7 @@ def get(self, group_account, include=None): include = ['/' + attr for attr in include] group_object = self._core.api.get_multi(baseurl, include) if group_object.name is None: - raise ObjectNotFoundException('Could not find group', baseurl, group_directory=group_account.directory, name=group_account.name) + raise ObjectNotFoundException(baseurl) return group_object def list_local_groups(self, include=None): @@ -88,9 +91,9 @@ def add(self, name, description=None, members=None): if members: param.members = self._members_reference(members) - logging.getLogger('cterasdk.core').info('Creating group. %s', {'group': name}) + logger.info('Creating group. %s', {'group': name}) response = self._core.api.execute('', 'addGroup', param) - logging.getLogger('cterasdk.core').info('Group created. %s', {'group': name}) + logger.info('Group created. %s', {'group': name}) return response @@ -116,13 +119,14 @@ def modify(self, current_groupname, new_groupname=None, description=None): param.membersToAdd = [] param.membersToDelete = [] + ref = f'/localGroups/{current_groupname}' try: - response = self._core.api.execute(f'/localGroups/{current_groupname}', 'updateGroup', param) - logging.getLogger('cterasdk.core').info("Group modified. %s", {'group_name': group.name}) + response = self._core.api.execute(ref, 'updateGroup', param) + logger.info("Group modified: %s", group.name) return response except CTERAException as error: - logging.getLogger('cterasdk.core').error("Failed to modify group.") - raise CTERAException('Failed to modify group', error) + logger.error('Group modification failed: %s', ref) + raise CTERAException(f'Group modification failed: {ref}') from error def get_members(self, group_account): """ @@ -169,10 +173,10 @@ def delete(self, group_account): :param cterasdk.core.types.GroupAccount group_account: Group account """ - logging.getLogger('cterasdk.core').info('Deleting group. %s', {'group': str(group_account.name)}) + logger.info('Deleting group. %s', {'group': str(group_account.name)}) baseurl = f'/localGroups/{group_account.name}' if group_account.is_local \ else f'/domains/{group_account.directory}/adGroups/{group_account.name}' response = self._core.api.execute(baseurl, 'delete', True) - logging.getLogger('cterasdk.core').info('Group deleted. %s', {'group': str(group_account.name)}) + logger.info('Group deleted. %s', {'group': str(group_account.name)}) return response diff --git a/cterasdk/core/kms.py b/cterasdk/core/kms.py index ef3cae7f..e997a46f 100644 --- a/cterasdk/core/kms.py +++ b/cterasdk/core/kms.py @@ -6,6 +6,9 @@ from . import query +logger = logging.getLogger('cterasdk.core') + + class KMS(BaseCommand): """ External Key Management APIs @@ -44,9 +47,9 @@ def enable(self, private_key, client_certificate, server_certificate, expiration param.integration.connectionSettings.timeout = timeout if timeout else 2 param.integration.connectionSettings.port = port if port else 5696 param.integration.tlsDetails = self._TLS_details(private_key, client_certificate, server_certificate) - logging.getLogger('cterasdk.core').info('Enabling Key Management Service') + logger.info('Enabling Key Management Service') response = self._core.api.put('/settings/keyManagerSettings', param) - logging.getLogger('cterasdk.core').info('Key Management Service enabled') + logger.info('Key Management Service enabled') return response @staticmethod @@ -64,9 +67,9 @@ def disable(self): """ Disable Key Management Service """ - logging.getLogger('cterasdk.core').info('Disabling Key Management Service') + logger.info('Disabling Key Management Service') response = self._core.api.execute('', 'removeKeyManagementService') - logging.getLogger('cterasdk.core').info('Key Management Service disabled') + logger.info('Key Management Service disabled') return response def modify(self, private_key=None, client_certificate=None, server_certificate=None, expiration=None, timeout=None, port=None): @@ -95,9 +98,9 @@ def modify(self, private_key=None, client_certificate=None, server_certificate=N if port: settings.integration.connectionSettings.port = port - logging.getLogger('cterasdk.core').info('Updating Key Management Service settings') + logger.info('Updating Key Management Service settings') response = self._core.api.put('/settings/keyManagerSettings', settings) - logging.getLogger('cterasdk.core').info('Updated Key Management Service settings') + logger.info('Updated Key Management Service settings') return response @@ -130,9 +133,9 @@ def add(self, name, ipaddr): param._classname = 'KeyManagerServer' # pylint: disable=protected-access param.name = name param.host = ipaddr - logging.getLogger('cterasdk.core').info('Adding Key Server. %s', {'name': name, 'host': ipaddr}) + logger.info('Adding Key Server. %s', {'name': name, 'host': ipaddr}) response = self._core.api.add('/keyManagerServers', param) - logging.getLogger('cterasdk.core').info('Key Server. %s Added', {'name': name, 'host': ipaddr}) + logger.info('Key Server. %s Added', {'name': name, 'host': ipaddr}) return response def modify(self, current_name, new_name): @@ -144,9 +147,9 @@ def modify(self, current_name, new_name): """ key_server = self.get(current_name) key_server.name = new_name - logging.getLogger('cterasdk.core').info("Modifying Key Server. %s", {'name': current_name}) + logger.info("Modifying Key Server. %s", {'name': current_name}) response = self._core.api.put(f'/keyManagerServers/{current_name}', key_server) - logging.getLogger('cterasdk.core').info("Key Server modified. %s", {'name': current_name}) + logger.info("Key Server modified. %s", {'name': current_name}) return response def delete(self, name): @@ -155,7 +158,7 @@ def delete(self, name): :param str name: Key-server name """ - logging.getLogger('cterasdk.core').info('Deleting Key Server. %s', {'name': name}) + logger.info('Deleting Key Server. %s', {'name': name}) response = self._core.api.delete(f'/keyManagerServers/{name}') - logging.getLogger('cterasdk.core').info('Key Server deleted. %s', {'name': name}) + logger.info('Key Server deleted. %s', {'name': name}) return response diff --git a/cterasdk/core/licenses.py b/cterasdk/core/licenses.py index f892df22..1c3bfb16 100644 --- a/cterasdk/core/licenses.py +++ b/cterasdk/core/licenses.py @@ -4,6 +4,9 @@ from .base_command import BaseCommand +logger = logging.getLogger('cterasdk.core') + + class Licenses(BaseCommand): """ Portal Licenses APIs @@ -24,11 +27,11 @@ def add(self, *keys): :param list[str] keys: List of license keys """ - logging.getLogger('cterasdk.core').info('Adding license(s)') + logger.info('Adding license(s)') param = Object() param.keys = list(keys) self._core.api.execute('', 'addLicenses', param) - logging.getLogger('cterasdk.core').info('License(s) added') + logger.info('License(s) added') def remove(self, *keys): """ @@ -40,8 +43,8 @@ def remove(self, *keys): licenses = self.all() param = [license for license in licenses if license.key not in keys and license.originalKey not in keys] if len(param) != len(licenses): - logging.getLogger('cterasdk.core').info('Updating licenses.') + logger.info('Updating licenses.') response = self._core.api.put('/portalLicenses', param) - logging.getLogger('cterasdk.core').info('Licenses updated.') + logger.info('Licenses updated.') return response return None diff --git a/cterasdk/core/logs.py b/cterasdk/core/logs.py index 1d2f88c1..37b5ff03 100644 --- a/cterasdk/core/logs.py +++ b/cterasdk/core/logs.py @@ -8,6 +8,9 @@ from . import query +logger = logging.getLogger('cterasdk.core') + + class Logs(BaseCommand): """ Portal Logs APIs @@ -132,9 +135,9 @@ def put(self, alerts): :param list[cterasdk.core.types.Alert] alerts: List of alerts """ - logging.getLogger('cterasdk.core').info('Updating log based alerts.') + logger.info('Updating log based alerts.') response = self._core.api.put(self._context, alerts) - logging.getLogger('cterasdk.core').info('Log based alerts updated.') + logger.info('Log based alerts updated.') return response def get(self): diff --git a/cterasdk/core/mail.py b/cterasdk/core/mail.py index 799a7ea9..fbd28424 100644 --- a/cterasdk/core/mail.py +++ b/cterasdk/core/mail.py @@ -2,6 +2,9 @@ from .base_command import BaseCommand +logger = logging.getLogger('cterasdk.core') + + class Mail(BaseCommand): """ Portal Mail Server Configuration APIs """ @@ -25,18 +28,18 @@ def enable(self, host=None, port=None, sender=None, username=None, password=None settings = self._core.api.get('/settings') settings.enableEmailSending = True settings = Mail._configure_settings(settings, host, port, sender, username, password, use_tls) - logging.getLogger('cterasdk.core').info('Enabling Mail Server.') + logger.info('Enabling Mail Server.') response = self._core.api.put('/settings', settings) - logging.getLogger('cterasdk.core').info('Mail Server enabled.') + logger.info('Mail Server enabled.') return response def disable(self): """ Disable Mail Server Notifications """ - logging.getLogger('cterasdk.core').info('Disabling Mail Server.') + logger.info('Disabling Mail Server.') response = self._core.api.put('/settings/enableEmailSending', False) - logging.getLogger('cterasdk.core').info('Mail Server disabled.') + logger.info('Mail Server disabled.') return response @staticmethod @@ -67,7 +70,7 @@ def modify(self, host=None, port=None, sender=None, username=None, password=None """ settings = self._core.api.get('/settings') settings = Mail._configure_settings(settings, host, port, sender, username, password, use_tls) - logging.getLogger('cterasdk.core').info('Modifying Mail Server settings.') + logger.info('Modifying Mail Server settings.') response = self._core.api.put('/settings', settings) - logging.getLogger('cterasdk.core').info('Mail Server settings modified.') + logger.info('Mail Server settings modified.') return response diff --git a/cterasdk/core/messaging.py b/cterasdk/core/messaging.py index 7401816a..c88be8eb 100644 --- a/cterasdk/core/messaging.py +++ b/cterasdk/core/messaging.py @@ -4,6 +4,9 @@ from ..common import Object +logger = logging.getLogger('cterasdk.core') + + class Messaging(BaseCommand): """ Portal Messaging Service Management APIs @@ -46,9 +49,9 @@ def add(self, servers): nodes.append(param) if len(nodes) in messaging_obj.globalStatus.validServerNumber: response = self._core.api.put('microservices/messaging/currentNodes', nodes) - logging.getLogger('cterasdk.core').info('Nodes added to cluster successfully') + logger.info('Nodes added to cluster successfully') return response - logging.getLogger('cterasdk.core').error('Wrong number of servers. expected:"1" or "3", %s', {'given': len(servers)}) + logger.error('Wrong number of servers. expected:"1" or "3", %s', {'given': len(servers)}) else: - logging.getLogger('cterasdk.core').error('Can not add new servers: %s', {messaging_obj.globalStatus.cantAddServersReason}) + logger.error('Can not add new servers: %s', {messaging_obj.globalStatus.cantAddServersReason}) return None diff --git a/cterasdk/core/plans.py b/cterasdk/core/plans.py index d9afe937..1b4a6bf0 100644 --- a/cterasdk/core/plans.py +++ b/cterasdk/core/plans.py @@ -6,6 +6,9 @@ from . import query +logger = logging.getLogger('cterasdk.core') + + class Plans(BaseCommand): """ Portal Plan APIs @@ -25,10 +28,11 @@ def _get_entire_object(self, name): :param str name: Name of the subscription plan """ + ref = f'/plans/{name}' try: - return self._core.api.get('/plans/' + name) + return self._core.api.get(ref) except CTERAException as error: - raise CTERAException('Could not find subscription plan', error) + raise CTERAException(f'Plan not found: {ref}') from error def by_name(self, names, include=None): """ @@ -75,7 +79,7 @@ def get(self, name, include=None): include = ['/' + attr for attr in include] plan = self._core.api.get_multi('/plans/' + name, include) if plan.name is None: - raise ObjectNotFoundException('Could not find subscription plan', f'/plans/{name}', name=name) + raise ObjectNotFoundException(f'/plans/{name}') return plan def add(self, name, services=None, retention=None, quotas=None): @@ -93,11 +97,11 @@ def add(self, name, services=None, retention=None, quotas=None): Plans._assign_quotas(plan, quotas) try: response = self._core.api.add('/plans', plan) - logging.getLogger('cterasdk.core').info("Plan created. %s", {'plan': name}) + logger.info("Plan created. %s", {'plan': name}) return response except CTERAException as error: - logging.getLogger('cterasdk.core').error("Plan creation failed.") - raise CTERAException('Plan creation failed', error) + logger.error("Plan creation failed: %s", name) + raise CTERAException(f'Plan creation failed: {name}') from error def modify(self, name, services=None, retention=None, quotas=None, apply_changes=True): """ @@ -112,9 +116,11 @@ def modify(self, name, services=None, retention=None, quotas=None, apply_changes Plans._assign_services(plan, services) Plans._assign_retention(plan, retention) Plans._assign_quotas(plan, quotas) + + ref = f'/plans/{name}' try: - response = self._core.api.put('/plans/' + name, plan) - logging.getLogger('cterasdk.core').info("Plan modified. %s", {'plan': name}) + response = self._core.api.put(ref, plan) + logger.info("Plan modified. %s", {'plan': name}) if apply_changes: if self._core.session().in_tenant_context(): self._core.users.apply_changes(True) @@ -122,8 +128,8 @@ def modify(self, name, services=None, retention=None, quotas=None, apply_changes self._core.portals.apply_changes(True) return response except CTERAException as error: - logging.getLogger('cterasdk.core').error("Could not modify subscription plan.") - raise CTERAException('Could not modify subscription plan', error) + logger.error("Plan modification failed: %s", ref) + raise CTERAException(f'Plan modification failed: {ref}') @staticmethod def _assign_services(plan, services): @@ -188,13 +194,14 @@ def delete(self, name): :param str username: The name of the subscription plan """ + ref = f'/plans/{name}' try: - response = self._core.api.delete('/plans/' + name) - logging.getLogger('cterasdk.core').info("Plan deleted. %s", {'name': name}) + response = self._core.api.delete(ref) + logger.info("Plan deleted: %s", name) return response except CTERAException as error: - logging.getLogger('cterasdk.core').error("Plan deletion failed.") - raise CTERAException('Plan deletion failed', error) + logger.error("Plan deletion failed: %s", ref) + raise CTERAException(f'Plan deletion failed: {ref}') from error class PlanAutoAssignPolicy(BaseCommand): @@ -222,8 +229,8 @@ def set_policy(self, rules, apply_default=None, default=None, apply_changes=True not_found = [plan for plan in plans if plan not in portal_plans.keys()] if not_found: - logging.getLogger('cterasdk.core').error('Could not find one or more plans. %s', {'plans': not_found}) - raise CTERAException('Could not find one or more plans', None, plans=not_found) + logger.error('Could not find one or more plans: %s', not_found) + raise CTERAException(f'Could not find one or more plans: {not_found}') policy = self.get_policy() @@ -237,7 +244,7 @@ def set_policy(self, rules, apply_default=None, default=None, apply_changes=True policy.planAutoAssignmentRules = policy_rules response = self._core.api.execute('', 'setPlanAutoAssignmentRules', policy) - logging.getLogger('cterasdk.core').info('Set plans auto assignment rules.') + logger.info('Set plans auto assignment rules.') if apply_changes: self._core.users.apply_changes(True) diff --git a/cterasdk/core/portals.py b/cterasdk/core/portals.py index 211016f0..5babbd8f 100644 --- a/cterasdk/core/portals.py +++ b/cterasdk/core/portals.py @@ -6,6 +6,9 @@ from . import enum, query, decorator +logger = logging.getLogger('cterasdk.core') + + class Portals(BaseCommand): """ Global Admin Portals APIs @@ -24,7 +27,7 @@ def get(self, name, include=None): include = ['/' + attr for attr in include] tenant = self._core.api.get_multi('/portals/' + name, include) if tenant.name is None: - raise ObjectNotFoundException('Could not find tenant', f'/portals/{name}', name=name) + raise ObjectNotFoundException(f'/portals/{name}') return tenant def list_tenants(self, include=None, portal_type=None): @@ -78,11 +81,11 @@ def add(self, name, display_name=None, billing_id=None, company=None, plan=None, param.companyName = company param.comment = comment - logging.getLogger('cterasdk.core').info('Creating Team Portal. %s', {'name': name}) + logger.info('Creating Team Portal. %s', {'name': name}) response = self._core.api.add('/teamPortals', param) - logging.getLogger('cterasdk.core').info('Team Portal created. %s', {'name': name}) + logger.info('Team Portal created. %s', {'name': name}) return response @@ -101,11 +104,11 @@ def delete(self, name): :param str name: Name of the tenant to delete """ - logging.getLogger('cterasdk.core').info('Deleting Portal. %s', {'name': name}) + logger.info('Deleting Portal. %s', {'name': name}) response = self._core.api.execute('/teamPortals/' + name, 'delete') - logging.getLogger('cterasdk.core').info('Portal deleted. %s', {'name': name}) + logger.info('Portal deleted. %s', {'name': name}) return response @@ -115,11 +118,11 @@ def undelete(self, name): :param str name: Name of the tenant to undelete """ - logging.getLogger('cterasdk.core').info('Recovering Portal. %s', {'name': name}) + logger.info('Recovering Portal. %s', {'name': name}) response = self._core.api.execute('/teamPortals/' + name, 'moveFromTrashcan') - logging.getLogger('cterasdk.core').info('Portal recovered. %s', {'name': name}) + logger.info('Portal recovered. %s', {'name': name}) return response @@ -147,7 +150,7 @@ def apply_changes(self, wait=False): param = Object() param.objectId = None param.type = 'portals' - logging.getLogger('cterasdk.core').info('Applying provisioning changes.') + logger.info('Applying provisioning changes.') task = self._core.api.execute('', 'updatePortals', param) if wait: task = self._core.tasks.wait(task) diff --git a/cterasdk/core/reports.py b/cterasdk/core/reports.py index 7a433f53..ee97a544 100644 --- a/cterasdk/core/reports.py +++ b/cterasdk/core/reports.py @@ -4,6 +4,9 @@ from ..exceptions import InputError +logger = logging.getLogger('cterasdk.core') + + class Reports(BaseCommand): """ Reports APIs @@ -45,7 +48,7 @@ def generate(self, name): options = {v: k for k, v in enum.Reports.__dict__.items() if not k.startswith('_')} if options.get(name, None) is None: raise InputError('Invalid report type', name, list(options.values())) - logging.getLogger('cterasdk.core').info('Running Portal Report. %s', {'name': name}) + logger.info('Running Portal Report. %s', {'name': name}) self._core.api.execute('', 'generateReport', name) def _get_report(self, report_name): diff --git a/cterasdk/core/roles.py b/cterasdk/core/roles.py index 9e1eb9ef..ee50fa96 100644 --- a/cterasdk/core/roles.py +++ b/cterasdk/core/roles.py @@ -5,6 +5,9 @@ from .base_command import BaseCommand +logger = logging.getLogger('cterasdk.core') + + class Roles(BaseCommand): """ Role Settings APIs @@ -36,7 +39,7 @@ def get(self, role): role = Roles.find(role) if role: return RoleSettings.from_server_object(self._core.api.get(f'/rolesSettings/{role}')) - logging.getLogger('cterasdk.core').warning('Could not find role. %s', {'role': role}) + logger.warning('Could not find role. %s', {'role': role}) return None def modify(self, role, settings): @@ -53,9 +56,9 @@ def modify(self, role, settings): if role: if settings.sudo: settings = RoleSettings(name=name, **{attr: True for attr in settings.__dict__.keys() if attr != 'name'}) - logging.getLogger('cterasdk.core').info('Updating role settings. %s', {'role': role}) + logger.info('Updating role settings. %s', {'role': role}) response = self._core.api.put(f'/rolesSettings/{role}', settings.to_server_object()) - logging.getLogger('cterasdk.core').info('Role settings updated. %s', {'role': role}) + logger.info('Role settings updated. %s', {'role': role}) return RoleSettings.from_server_object(response) - logging.getLogger('cterasdk.core').warning('Could not find role. %s', {'role': role}) + logger.warning('Could not find role. %s', {'role': role}) return None diff --git a/cterasdk/core/servers.py b/cterasdk/core/servers.py index cff35bc9..8dd41801 100644 --- a/cterasdk/core/servers.py +++ b/cterasdk/core/servers.py @@ -7,6 +7,9 @@ from ..exceptions import CTERAException, ObjectNotFoundException +logger = logging.getLogger('cterasdk.core') + + class Servers(BaseCommand): """ Global Admin Servers APIs @@ -19,10 +22,11 @@ def __init__(self, portal): self.tasks = Tasks(self._core) def _get_entire_object(self, server): + ref = f'/servers/{server}' try: - return self._core.api.get(f'/servers/{server}') + return self._core.api.get(ref) except CTERAException as error: - raise CTERAException('Failed to retrieve server', error) + raise CTERAException(f'Server not found: {ref}') def get(self, name, include=None): """ @@ -36,7 +40,7 @@ def get(self, name, include=None): include = ['/' + attr for attr in include] server = self._core.api.get_multi(f'/servers/{name}', include) if server.name is None: - raise ObjectNotFoundException('Could not find server', f'/servers/{name}', name=name) + raise ObjectNotFoundException(f'/servers/{name}') return server def list_servers(self, include=None): @@ -87,13 +91,14 @@ def modify(self, name, server_name=None, app=None, preview=None, enable_public_i if allow_user_login is not None: server.allowUserLogin = allow_user_login + ref = f'/servers/{name}' try: - response = self._core.api.put(f'/servers/{name}', server) - logging.getLogger('cterasdk.core').info("Server modified. %s", {'server': name}) + response = self._core.api.put(ref, server) + logger.info("Server modified. %s", {'server': name}) return response except CTERAException as error: - logging.getLogger('cterasdk.core').error("Could not modify server.") - raise CTERAException('Could not modify server', error) + logger.error("Server modification failed: %s", ref) + raise CTERAException(f'Server modification failed: {ref}') class Tasks(BaseCommand): diff --git a/cterasdk/core/setup.py b/cterasdk/core/setup.py index 5f784fd7..db914308 100644 --- a/cterasdk/core/setup.py +++ b/cterasdk/core/setup.py @@ -9,6 +9,9 @@ from ..exceptions import CTERAException +logger = logging.getLogger('cterasdk.core') + + class Setup(BaseCommand): """ Global Admin Setup APIs @@ -53,12 +56,12 @@ def init_master(self, name, email, first_name, last_name, password, domain): params.settings = Setup.default_settings() params.settings.dnsSuffix = domain - logging.getLogger('cterasdk.core').info('Initializing Portal. %s', {'domain': domain, 'user': name}) + logger.info('Initializing Portal. %s', {'domain': domain, 'user': name}) self._core.ctera.execute('/public', 'init', params) SetupWizardStatusMonitor(self._core).wait(SetupWizardStage.Portal) - logging.getLogger('cterasdk.core').info('Portal initialized.') + logger.info('Portal initialized.') elif self.stage == SetupWizardStage.Finish: - logging.getLogger('cterasdk.core').warning('Portal already initialized. %s', {'host': self._core.host()}) + logger.warning('Portal already initialized. %s', {'host': self._core.host()}) self._core.startup.wait() def _init_slave(self, ipaddr, secret): @@ -71,7 +74,7 @@ def _init_slave(self, ipaddr, secret): elif response == SlaveAuthenticaionMethod.PrivateKey: params.slaveSettings.masterKey = secret else: - logging.getLogger('cterasdk.core').error('Unknown authentication method. %s', {'method': response}) + logger.error('Unknown authentication method. %s', {'method': response}) self._init_server(params, True) def _init_server(self, params, wait=False): @@ -83,14 +86,14 @@ def _init_server(self, params, wait=False): if params.serverMode == ServerMode.Slave: form.add('masterIpAddr', params.slaveSettings.masterIpAddr) - logging.getLogger('cterasdk.core').info('Initializing server. %s', {'host': self._core.host(), 'mode': params.serverMode}) + logger.info('Initializing server. %s', {'host': self._core.host(), 'mode': params.serverMode}) self._core.ctera.multipart('/setup', form) if wait: status = SetupWizardStatusMonitor(self._core).wait(SetupWizardStage.Server) self.stage = status.wizard - logging.getLogger('cterasdk.core').info('Server initialized. %s', {'host': self._core.host(), 'mode': params.serverMode}) + logger.info('Server initialized. %s', {'host': self._core.host(), 'mode': params.serverMode}) else: - logging.getLogger('cterasdk.core').warning('Server already initialized. %s', {'host': self._core.host()}) + logger.warning('Server already initialized. %s', {'host': self._core.host()}) def init_application_server(self, ipaddr, secret): """ @@ -100,7 +103,7 @@ def init_application_server(self, ipaddr, secret): :param str secret: A password or a PEM-encoded private key """ self._init_slave(ipaddr, secret) - logging.getLogger('cterasdk.core').info('Initializing Application Server. %s', {'host': ipaddr}) + logger.info('Initializing Application Server. %s', {'host': ipaddr}) self._core.startup.wait() @staticmethod @@ -148,29 +151,29 @@ def wait(self, stage): while current_stage == stage: try: self._increment() - logging.getLogger('cterasdk.core').debug('Obtaining wizard status. %s', {'attempt': self._attempt}) + logger.debug('Obtaining wizard status. %s', {'attempt': self._attempt}) status = self._core.setup.get_setup_status() - logging.getLogger('cterasdk.core').debug('Current wizard status. %s', { + logger.debug('Current wizard status. %s', { 'stage': status.wizard, 'status': status.currentWizardProgress, 'description': status.description }) if status.currentWizardProgress == SetupWizardStatus.Failed: - raise CTERAException('Initialization failed.', status) + raise CTERAException(f'Initialization failed: {status}') current_stage = status.wizard except (ConnectionError, TimeoutError) as e: - logging.getLogger('cterasdk.core').debug('Exception. %s', e.__dict__) - logging.getLogger('cterasdk.core').debug('Wizard update. %s', {'previous_stage': stage, 'current_stage': current_stage}) + logger.debug('Exception. %s', e.__dict__) + logger.debug('Wizard update. %s', {'previous_stage': stage, 'current_stage': current_stage}) return status def _increment(self): self._attempt = self._attempt + 1 if self._attempt >= self._retries: SetupWizardStatusMonitor._unreachable() - logging.getLogger('cterasdk.core').debug('Sleep. %s', {'seconds': self._seconds}) + logger.debug('Sleep. %s', {'seconds': self._seconds}) time.sleep(self._seconds) @staticmethod def _unreachable(): - logging.getLogger('cterasdk.core').error('Timed out. Setup did not complete in a timely manner.') + logger.error('Timed out. Setup did not complete in a timely manner.') raise CTERAException('Timed out. Setup did not complete in a timely manner') diff --git a/cterasdk/core/ssl.py b/cterasdk/core/ssl.py index 35e86ff6..f347228a 100644 --- a/cterasdk/core/ssl.py +++ b/cterasdk/core/ssl.py @@ -6,6 +6,9 @@ from ..lib.storage import commonfs, synfs +logger = logging.getLogger('cterasdk.core') + + class SSL(BaseCommand): """ Portal SSL Certificate APIs @@ -17,9 +20,9 @@ def get(self): :return cterasdk.common.object.Object: An object including the SSL certificate details """ - logging.getLogger('cterasdk.core').info('Retrieving SSL certificate') + logger.info('Retrieving SSL certificate') response = self._core.api.get('/settings/ca') - logging.getLogger('cterasdk.core').info('Retrieved SSL certificate') + logger.info('Retrieved SSL certificate') return response @property @@ -37,10 +40,10 @@ def export(self, destination=None): File destination, defaults to the default directory """ directory, filename = commonfs.generate_file_destination(destination, 'certificate.zip') - logging.getLogger('cterasdk.core').info('Exporting SSL certificate.') + logger.info('Exporting SSL certificate.') handle = self._core.ctera.handle('/preview/exportCertificate') filepath = synfs.write(directory, filename, handle) - logging.getLogger('cterasdk.core').info('Exported SSL certificate. %s', {'filepath': filepath}) + logger.info('Exported SSL certificate. %s', {'filepath': filepath}) return filepath @staticmethod @@ -95,7 +98,7 @@ def import_from_chain(self, private_key, *certificates): def _import_certificate(self, zipfile): commonfs.properties(zipfile) - logging.getLogger('cterasdk.core').info('Uploading SSL certificate.') + logger.info('Uploading SSL certificate.') with open(zipfile, 'rb') as fd: response = self._core.api.form_data( '/settings/importCertificate', @@ -105,6 +108,6 @@ def _import_certificate(self, zipfile): ) ) if not isinstance(response, str): - logging.getLogger('cterasdk.core').error('Failed uploading SSL certificate. %s', {'reason': response.msg}) - logging.getLogger('cterasdk.core').info('Uploaded SSL certificate.') + logger.error('Failed uploading SSL certificate. %s', {'reason': response.msg}) + logger.info('Uploaded SSL certificate.') self._core.startup.wait() diff --git a/cterasdk/core/startup.py b/cterasdk/core/startup.py index af4f56a2..47572d06 100644 --- a/cterasdk/core/startup.py +++ b/cterasdk/core/startup.py @@ -1,10 +1,14 @@ import logging import time -from ..exceptions import CTERAException, HTTPError +from ..exceptions import CTERAException +from ..exceptions.transport import HTTPError from .base_command import BaseCommand +logger = logging.getLogger('cterasdk.core') + + class Startup(BaseCommand): """ Server Startup APIs @@ -27,16 +31,16 @@ def wait(self, retries=120, seconds=5): while True: try: if attempt >= retries: - logging.getLogger('cterasdk.core').error('Timed out. Server did not start in a timely manner.') + logger.error('Timed out. Server did not start in a timely manner.') raise CTERAException('Timed out. Server did not start in a timely manner') current_status = self.status() if current_status == Startup.Started: - logging.getLogger('cterasdk.core').info('Server started.') + logger.info('Server started.') break - logging.getLogger('cterasdk.core').debug('Current server status. %s', {'status': current_status}) + logger.debug('Current server status. %s', {'status': current_status}) attempt = attempt + 1 time.sleep(seconds) except (ConnectionError, TimeoutError, HTTPError) as e: - logging.getLogger('cterasdk.core').debug('Exception. %s', e.__dict__) + logger.debug('Exception. %s', e.__dict__) attempt = attempt + 1 time.sleep(seconds) diff --git a/cterasdk/core/storage_classes.py b/cterasdk/core/storage_classes.py index e624e864..4c1da4d4 100644 --- a/cterasdk/core/storage_classes.py +++ b/cterasdk/core/storage_classes.py @@ -5,6 +5,9 @@ from .base_command import BaseCommand +logger = logging.getLogger('cterasdk.core') + + class StorageClasses(BaseCommand): """ Portal Storage Classes APIs """ @@ -14,11 +17,11 @@ def add(self, name): :param str name: Name """ - logging.getLogger('cterasdk.core').info("Adding storage class. %s", {'name': name}) + logger.info("Adding storage class. %s", {'name': name}) param = Object() param.name = name response = self._core.api.add('/storageClasses', param) - logging.getLogger('cterasdk.core').info("Storage class added. %s", {'name': name}) + logger.info("Storage class added. %s", {'name': name}) return response def all(self): @@ -40,9 +43,10 @@ def get(self, name): :returns: Storage class :rtype: cterasdk.common.object.Object """ + ref = f'/storageClasses/{name}' if not self._core.session().in_tenant_context(): - return self._core.api.get(f'/storageClasses/{name}') + return self._core.api.get(ref) for storage_class in self.all(): if storage_class.name == name: return storage_class - raise CTERAException('Could not find storage class.', name=name) + raise CTERAException(f'Storage class not found: {ref}') diff --git a/cterasdk/core/syslog.py b/cterasdk/core/syslog.py index 3a386578..f35a4c06 100644 --- a/cterasdk/core/syslog.py +++ b/cterasdk/core/syslog.py @@ -10,6 +10,9 @@ from ..clients.common import MultipartForm +logger = logging.getLogger('cterasdk.core') + + class Syslog(BaseCommand): """ Portal Syslog Management APIs @@ -87,11 +90,11 @@ def enable(self, server, port=514, protocol=IPProtocol.UDP, min_severity=Severit param.port = port param.protocol = protocol param.useClientCertificate = False - logging.getLogger('cterasdk.core').info('Enabling syslog.') + logger.info('Enabling syslog.') response = self._core.api.put('/settings/logsSettings/syslogConfig', param) if protocol == IPProtocol.TCP and ca_cert is not None: self.import_ca(ca_cert) - logging.getLogger('cterasdk.core').info('Syslog enabled.') + logger.info('Syslog enabled.') return response def modify(self, server=None, port=None, protocol=None, min_severity=None): @@ -115,9 +118,9 @@ def modify(self, server=None, port=None, protocol=None, min_severity=None): if min_severity: current_config.minSeverity = min_severity - logging.getLogger('cterasdk.core').info("Updating syslog server configuration.") + logger.info("Updating syslog server configuration.") self._core.api.put('/settings/logsSettings/syslogConfig', current_config) - logging.getLogger('cterasdk.core').info( + logger.info( "Syslog server configured. %s", { 'server': current_config.server, @@ -128,7 +131,7 @@ def modify(self, server=None, port=None, protocol=None, min_severity=None): ) def disable(self): - logging.getLogger('cterasdk.core').info('Disabling syslog.') + logger.info('Disabling syslog.') response = self._core.api.put('/settings/logsSettings/syslogConfig/mode', Mode.Disabled) - logging.getLogger('cterasdk.core').info('Syslog disabled.') + logger.info('Syslog disabled.') return response diff --git a/cterasdk/core/taskmgr.py b/cterasdk/core/taskmgr.py index e4cf5ab1..ce547c13 100644 --- a/cterasdk/core/taskmgr.py +++ b/cterasdk/core/taskmgr.py @@ -6,12 +6,15 @@ from .base_command import BaseCommand +logger = logging.getLogger('cterasdk.core') + + class Task(TaskBase): def _get_task_id(self, ref): match = re.search('servers/[^/]*/bgTasks/[1-9][0-9]*$', ref) if not match: - logging.getLogger('cterasdk.core').error('Invalid task id. %s', {'ref': ref}) + logger.error('Invalid task id. %s', {'ref': ref}) raise InputError('Invalid task id', ref, ['servers/server/bgTasks/107781']) return match.group(0) diff --git a/cterasdk/core/templates.py b/cterasdk/core/templates.py index 476e0802..c59aa021 100644 --- a/cterasdk/core/templates.py +++ b/cterasdk/core/templates.py @@ -7,6 +7,9 @@ from .enum import Platform +logger = logging.getLogger('cterasdk.core') + + class Templates(BaseCommand): """ Portal Configuration Template APIs @@ -19,10 +22,11 @@ def __init__(self, portal): self.auto_assign = TemplateAutoAssignPolicy(self._core) def _get_entire_object(self, name): + ref = f'/deviceTemplates/{name}' try: - return self._core.api.get(f'/deviceTemplates/{name}') + return self._core.api.get(ref) except CTERAException as error: - raise CTERAException('Failed to get template', error) + raise CTERAException(f'Template not found: {ref}') from error def get(self, name, include=None): """ @@ -35,7 +39,7 @@ def get(self, name, include=None): include = ['/' + attr for attr in include] template = self._core.api.get_multi('/deviceTemplates/' + name, include) if template.name is None: - raise ObjectNotFoundException('Could not find template', f'/deviceTemplates/{name}', name=name) + raise ObjectNotFoundException(f'/deviceTemplates/{name}') return template def add(self, name, description=None, include_sets=None, exclude_sets=None, # pylint: disable=too-many-arguments @@ -73,9 +77,9 @@ def add(self, name, description=None, include_sets=None, exclude_sets=None, # p Templates._configure_software_update_schedule(param, update_settings) Templates._configure_consent_page(param, consent_page) - logging.getLogger('cterasdk.core').info('Adding template. %s', {'name': name}) + logger.info('Adding template. %s', {'name': name}) response = self._core.api.add('/deviceTemplates', param) - logging.getLogger('cterasdk.core').info('Template added. %s', {'name': name}) + logger.info('Template added. %s', {'name': name}) return response def _configure_firmware_settings(self, param, versions): @@ -156,7 +160,7 @@ def _convert_to_template_firmwares(self, versions): for platform, version in versions: base_object_ref = firmwares.get(f'{platform}-{version}') if base_object_ref is None: - raise CTERAException('Could not find firmware version', None, platform=platform, version=version) + raise CTERAException(f"No firmware found for platform '{platform}' and version '{version}'.") template_firmwares.append(Templates._create_template_firmware(platform, str(base_object_ref))) return template_firmwares @@ -205,9 +209,9 @@ def delete(self, name): :param str name: Name of the template """ - logging.getLogger('cterasdk.core').info('Deleting template. %s', {'name': name}) + logger.info('Deleting template. %s', {'name': name}) response = self._core.api.delete(f'/deviceTemplates/{name}') - logging.getLogger('cterasdk.core').info('Template deleted. %s', {'name': name}) + logger.info('Template deleted. %s', {'name': name}) return response def set_default(self, name, wait=False): @@ -217,10 +221,10 @@ def set_default(self, name, wait=False): :param str name: Name of the template :param bool,optional wait: Wait for all changes to apply, defaults to `False` """ - logging.getLogger('cterasdk.core').info('Setting default template. %s', {'name': name}) + logger.info('Setting default template. %s', {'name': name}) response = self._core.api.execute(f'/deviceTemplates/{name}', 'setAsDefault') self.auto_assign.apply_changes(wait=wait) - logging.getLogger('cterasdk.core').info('Set default template. %s', {'name': name}) + logger.info('Set default template. %s', {'name': name}) return response def remove_default(self, name, wait=False): @@ -232,12 +236,12 @@ def remove_default(self, name, wait=False): """ template = self.get(name, include=['isDefault']) if template.isDefault: - logging.getLogger('cterasdk.core').info('Removing default template. %s', {'name': name}) + logger.info('Removing default template. %s', {'name': name}) response = self._core.api.execute('', 'removeDefaultDeviceTemplate') - logging.getLogger('cterasdk.core').info('Removed default template. %s', {'name': name}) + logger.info('Removed default template. %s', {'name': name}) self.auto_assign.apply_changes(wait=wait) return response - logging.getLogger('cterasdk.core').info('Template not set as default. %s', {'name': name}) + logger.info('Template not set as default. %s', {'name': name}) return None @@ -266,8 +270,8 @@ def set_policy(self, rules, apply_default=None, default=None, apply_changes=True not_found = [template for template in templates if template not in portal_templates.keys()] if not_found: - logging.getLogger('cterasdk.core').error('Could not find one or more templates. %s', {'templates': not_found}) - raise CTERAException('Could not find one or more templates', None, templates=not_found) + logger.error('Could not find one or more templates: %s', not_found) + raise CTERAException(f'Could not find one or more templates: {not_found}.') policy = self.get_policy() @@ -281,7 +285,7 @@ def set_policy(self, rules, apply_default=None, default=None, apply_changes=True policy.deviceTemplatesAutoAssignmentRules = policy_rules response = self._core.api.execute('', 'setAutoAssignmentRules', policy) - logging.getLogger('cterasdk.core').info('Set templates auto assignment rules.') + logger.info('Set templates auto assignment rules.') if apply_changes: self.apply_changes(True) diff --git a/cterasdk/core/users.py b/cterasdk/core/users.py index 2257b980..407b8bb1 100644 --- a/cterasdk/core/users.py +++ b/cterasdk/core/users.py @@ -2,7 +2,8 @@ from .base_command import BaseCommand from .types import UserAccount -from ..exceptions import CTERAException, ObjectNotFoundException, ContextError +from ..exceptions import CTERAException, ObjectNotFoundException +from ..exceptions.session import ContextError from ..common import Object, DateTimeUtils from ..common import union from . import query @@ -30,7 +31,7 @@ def _get_entire_object(self, user_account): try: return self._core.api.get(ref) except CTERAException as error: - raise CTERAException('Failed to get the user', error) + raise CTERAException(f'Could not retrieve user: {ref}') from error def get(self, user_account, include=None): """ @@ -46,7 +47,7 @@ def get(self, user_account, include=None): include = ['/' + attr for attr in include] user_object = self._core.api.get_multi(baseurl, include) if user_object.name is None: - raise ObjectNotFoundException('Could not find user', baseurl, user_directory=user_account.directory, username=user_account.name) + raise ObjectNotFoundException(baseurl) return user_object def list_local_users(self, include=None): @@ -143,13 +144,14 @@ def modify(self, current_username, new_username=None, email=None, first_name=Non if comment is not None: user.comment = comment + ref = f'/users/{current_username}' try: - response = self._core.api.put('/users/' + current_username, user) + response = self._core.api.put(ref, user) logger.info("User modified. %s", {'username': user.name}) return response except CTERAException as error: - logger.error("Failed to modify user.") - raise CTERAException('Failed to modify user', error) + logger.error('User modification failed: %s', ref) + raise CTERAException(f'User modification failed: {ref}') def apply_changes(self, wait=False): """ @@ -187,7 +189,7 @@ def generate_ticket(self, username, tenant): :param str portal: Tenant """ if self.session().in_tenant_context(): - raise ContextError('Context error: Navigate to the Portal Administration to invoke this API.') + raise ContextError('Browse the Global Administration to invoke this API') param = Object() param.username = username param.portal = tenant diff --git a/cterasdk/direct/__init__.py b/cterasdk/direct/__init__.py index 08ef46a4..0f98ea58 100644 --- a/cterasdk/direct/__init__.py +++ b/cterasdk/direct/__init__.py @@ -1 +1 @@ -from . import client, types, exceptions # noqa: E402, F401 +from . import client # noqa: E402, F401 \ No newline at end of file diff --git a/cterasdk/direct/crypto.py b/cterasdk/direct/crypto.py index d844e2d5..5536876f 100644 --- a/cterasdk/direct/crypto.py +++ b/cterasdk/direct/crypto.py @@ -4,21 +4,24 @@ from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes from cryptography.exceptions import UnsupportedAlgorithm from ..common.utils import utf8_decode -from .exceptions import DirectIOError +from ..exceptions.direct import DirectIOError + + +logger = logging.getLogger('cterasdk.direct') def decrypt_key(wrapped_key, secret): try: - logging.getLogger('cterasdk.direct').debug('Decoding Secret.') + logger.debug('Decoding Secret.') decoded_secret = base64.b64decode(secret) decoded_secret = decoded_secret[:32] + b'\0' * (32 - len(decoded_secret)) decryptor = Cipher(algorithms.AES(decoded_secret), modes.ECB()).decryptor() - logging.getLogger('cterasdk.direct').debug('Decrypting Encryption Key.') + logger.debug('Decrypting Encryption Key.') decrypted_wrapped_key = utf8_decode(decryptor.update(base64.b64decode(wrapped_key))) decrypted_key = ''.join(c for c in decrypted_wrapped_key if c.isprintable())[1:-1] return base64.b64decode(decrypted_key) except (AssertionError, ValueError, binascii.Error) as error: - logging.getLogger('cterasdk.direct').error('Could not decrypt secret key. %s', error) + logger.error('Could not decrypt secret key. %s', error) raise DirectIOError() @@ -26,12 +29,12 @@ def decrypt_block(block, encryption_key): try: initialization_vector = block[1:17] encrypted_data = block[17:] - logging.getLogger('cterasdk.direct').debug('Decrypting Block.') + logger.debug('Decrypting Block.') decryptor = Cipher(algorithms.AES(encryption_key), modes.CBC(initialization_vector)).decryptor() decrypted_data = decryptor.update(encrypted_data) return decrypted_data[:-decrypted_data[-1]] # Remove CBC Padding except ValueError as error: - logging.getLogger('cterasdk.direct').error('Failed to decrypt block. Key error. %s', error) + logger.error('Failed to decrypt block. Key error. %s', error) except UnsupportedAlgorithm as error: - logging.getLogger('cterasdk.direct').error('Failed to decrypt block. Unsupported algorithm. %s', error) + logger.error('Failed to decrypt block. Unsupported algorithm. %s', error) raise DirectIOError() diff --git a/cterasdk/direct/decompressor.py b/cterasdk/direct/decompressor.py index f9b160da..ef4f607d 100644 --- a/cterasdk/direct/decompressor.py +++ b/cterasdk/direct/decompressor.py @@ -1,7 +1,10 @@ import logging import gzip import snappy -from .exceptions import DirectIOError +from ..exceptions.direct import DirectIOError + + +logger = logging.getLogger('cterasdk.direct') def decompress(compressed_block): @@ -16,14 +19,14 @@ def decompress(compressed_block): try: return gzip.decompress(compressed_block) except gzip.BadGzipFile: - logging.getLogger('cterasdk.direct').error('Failed to Decompress Block. Bad Gzip.') + logger.error('Failed to Decompress Block. Bad Gzip.') raise DirectIOError() try: return decompress_with_magic_header(compressed_block) \ if compressed_block.startswith(b'\x82SNAPPY\x00') else snappy.uncompress(compressed_block) # Snappy Magic except snappy.UncompressError: - logging.getLogger('cterasdk.direct').error('Failed to Decompress Block.') + logger.error('Failed to Decompress Block.') raise DirectIOError() @@ -35,7 +38,7 @@ def decompress_with_magic_header(compressed_block): :returns: Decompressed Block :rtype: bytes """ - logging.getLogger('cterasdk.direct').debug('Decompressing Block.') + logger.debug('Decompressing Block.') decompressed_block = bytearray() size_of_data = len(compressed_block) chunk_size, chunk_start = 4, 16 diff --git a/cterasdk/direct/filters.py b/cterasdk/direct/filters.py index 6a0de466..a8adc561 100644 --- a/cterasdk/direct/filters.py +++ b/cterasdk/direct/filters.py @@ -1,5 +1,8 @@ import logging -from .exceptions import BlockInfo +from ..exceptions.direct import BlockInfo + + +logger = logging.getLogger('cterasdk.direct') def blocks(file, array): @@ -7,7 +10,7 @@ def blocks(file, array): Filter Blocks by Block Number. :param list[cterasdk.direct.types.File] file: File Object. - :param list[cterasdk.direct.exceptions.BlockInfo] array: List of BlockInfo objects, + :param list[cterasdk.direct.types.BlockInfo] array: List of BlockInfo objects, or list of integers identifying the block position. :returns: List of Chunks. :rtype: list[cterasdk.direct.types.Chunk] @@ -45,9 +48,9 @@ def span(file, byte_range): def validate_byte_range(file, byte_range): size = file.size if byte_range.start > size: - logging.getLogger('cterasdk.direct').error('Byte range start %s is greater than the file size %s.', byte_range.start, size) + logger.error('Byte range start %s is greater than the file size %s.', byte_range.start, size) return False if not byte_range.eof and byte_range.end >= size: - logging.getLogger('cterasdk.direct').debug('Byte range end is greater than file size.') + logger.debug('Byte range end is greater than file size.') byte_range.eof = True return True diff --git a/cterasdk/direct/lib.py b/cterasdk/direct/lib.py index 803da1fa..42e37371 100644 --- a/cterasdk/direct/lib.py +++ b/cterasdk/direct/lib.py @@ -5,11 +5,12 @@ from .credentials import KeyPair, Bearer from .crypto import decrypt_key, decrypt_block from .decompressor import decompress -from .exceptions import UnAuthorized, UnprocessableContent, BlocksNotFoundError, DownloadError, DownloadTimeout, BlockListTimeout, \ - DownloadConnectionError, DecryptKeyError, DecryptBlockError, NotFoundError, DecompressBlockError, BlockValidationException, \ - BlockListConnectionError, DirectIOError - -from ..exceptions import HTTPError +from ..exceptions.transport import HTTPError +from ..exceptions.direct import ( + UnAuthorized, UnprocessableContent, BlocksNotFoundError, DownloadError, DownloadTimeout, BlockListTimeout, + DownloadConnectionError, DecryptKeyError, DecryptBlockError, NotFoundError, DecompressBlockError, + BlockValidationException, BlockListConnectionError, DirectIOError +) logger = logging.getLogger('cterasdk.direct') diff --git a/cterasdk/direct/stream.py b/cterasdk/direct/stream.py index a81059f0..14d437c7 100644 --- a/cterasdk/direct/stream.py +++ b/cterasdk/direct/stream.py @@ -1,5 +1,8 @@ import logging -from .exceptions import DirectIOAPIError, BlockError, StreamError +from ..exceptions.direct import DirectIOAPIError, BlockError, StreamError + + +logger = logging.getLogger('cterasdk.direct') class Streamer: @@ -35,7 +38,7 @@ async def start(self): for download in self._downloads: block = await download fragment = block.fragment(self._byte_range) - logging.getLogger('cterasdk.direct').debug('Streamer Fragment. %s', {'offset': fragment.offset, 'length': fragment.length}) + logger.debug('Streamer Fragment. %s', {'offset': fragment.offset, 'length': fragment.length}) yield fragment self._offset = fragment.offset + fragment.length except DirectIOAPIError as error: diff --git a/cterasdk/edge/afp.py b/cterasdk/edge/afp.py index 0c443770..7b0b4bce 100644 --- a/cterasdk/edge/afp.py +++ b/cterasdk/edge/afp.py @@ -4,6 +4,9 @@ from .base_command import BaseCommand +logger = logging.getLogger('cterasdk.edge') + + class AFP(BaseCommand): """ Edge Filer AFP APIs """ @@ -15,8 +18,6 @@ def disable(self): """ Disable AFP """ - logging.getLogger('cterasdk.edge').info('Disabling AFP server.') - + logger.info('Disabling AFP server.') self._edge.api.put('/config/fileservices/afp/mode', Mode.Disabled) - - logging.getLogger('cterasdk.edge').info('AFP server disabled.') + logger.info('AFP server disabled.') diff --git a/cterasdk/edge/aio.py b/cterasdk/edge/aio.py index b9380140..ea5bd7f5 100644 --- a/cterasdk/edge/aio.py +++ b/cterasdk/edge/aio.py @@ -2,6 +2,9 @@ from .base_command import BaseCommand +logger = logging.getLogger('cterasdk.edge') + + class AIO(BaseCommand): """ Edge Filer AIO APIs @@ -20,26 +23,26 @@ def enable(self): """ Enable AIO """ - logging.getLogger('cterasdk.edge').info('Enabling asynchronous io.') + logger.info('Enabling asynchronous io.') self._async_io(True, 1, 1) - logging.getLogger('cterasdk.edge').info('Asynchronous io enabled.') + logger.info('Asynchronous io enabled.') def disable(self): """ Disable AIO """ - logging.getLogger('cterasdk.edge').info('Disabling asynchronous io.') + logger.info('Disabling asynchronous io.') self._async_io(False, 0, 0) - logging.getLogger('cterasdk.edge').info('Asynchronous io disabled.') + logger.info('Asynchronous io disabled.') def _async_io(self, robustMutexes, aioReadThreshold, aioWriteThreshold): - logging.getLogger('cterasdk.edge').debug('Obtaining CIFS server settings.') + logger.debug('Obtaining CIFS server settings.') cifs = self._edge.api.get('/config/fileservices/cifs') cifs.robustMutexes = robustMutexes cifs.aioReadThreshold = aioReadThreshold cifs.aioWriteThreshold = aioWriteThreshold - logging.getLogger('cterasdk.edge').debug('Updating CIFS server settings.') + logger.debug('Updating CIFS server settings.') self._edge.api.put('/config/fileservices/cifs', cifs) diff --git a/cterasdk/edge/array.py b/cterasdk/edge/array.py index d5363284..78823450 100644 --- a/cterasdk/edge/array.py +++ b/cterasdk/edge/array.py @@ -5,6 +5,9 @@ from .base_command import BaseCommand +logger = logging.getLogger('cterasdk.edge') + + class Array(BaseCommand): """ Edge Filer Array APIs """ @@ -30,13 +33,14 @@ def add(self, array_name, level, members=None): param.members = [drive.name for drive in self._edge.drive.get_status()] if members is None else members try: - logging.getLogger('cterasdk.edge').info("Creating a storage array.") - response = self._edge.api.add("/config/storage/arrays", param) - logging.getLogger('cterasdk.edge').info("Storage array created.") + ref = "/config/storage/arrays" + logger.info("Creating a storage array.") + response = self._edge.api.add(ref, param) + logger.info("Storage array created: %s/%s", ref, array_name) return response except CTERAException as error: - logging.getLogger('cterasdk.edge').error("Storage array creation failed.") - raise CTERAException("Storage array creation failed.", error) + logger.error(f"Storage array creation failed: %s", array_name) + raise CTERAException(f"Storage array creation failed: {array_name}") from error def delete(self, array_name): """ @@ -44,14 +48,15 @@ def delete(self, array_name): :param str name: The name of the array to delete """ + ref = f"/config/storage/arrays/{array_name}" try: - logging.getLogger('cterasdk.edge').info("Deleting a storage array.") - response = self._edge.api.delete("/config/storage/arrays/" + array_name) - logging.getLogger('cterasdk.edge').info("Storage array deleted. %s", {'array_name': array_name}) + logger.info("Deleting a storage array.") + response = self._edge.api.delete(ref) + logger.info("Storage array deleted: %s", ref) return response except CTERAException as error: - logging.getLogger('cterasdk.edge').error("Storage array deletion failed.") - raise CTERAException("Storage array deletion failed.", error) + logger.error("Storage array deletion failed: %s", ref) + raise CTERAException(f"Storage array deletion failed: {ref}") from error def delete_all(self): """ diff --git a/cterasdk/edge/audit.py b/cterasdk/edge/audit.py index c41752fc..bca8118d 100644 --- a/cterasdk/edge/audit.py +++ b/cterasdk/edge/audit.py @@ -5,6 +5,9 @@ from .base_command import BaseCommand +logger = logging.getLogger('cterasdk.edge') + + class Audit(BaseCommand): """ Edge Filer Audit configuration APIs @@ -54,14 +57,14 @@ def enable( settings.includeAuditLogTag = includeAuditLogTag settings.humanReadableAuditLog = humanReadableAuditLog - logging.getLogger('cterasdk.edge').info('Enabling SMB audit logs') + logger.info('Enabling SMB audit logs') self._edge.api.put('/config/logging/files', settings) - logging.getLogger('cterasdk.edge').info('Audit logs enabled') + logger.info('Audit logs enabled') def disable(self): """ Disable Edge Filer Audit log """ - logging.getLogger('cterasdk.edge').info('Disabling SMB audit logs') + logger.info('Disabling SMB audit logs') self._edge.api.put('/config/logging/files/mode', enum.Mode.Disabled) - logging.getLogger('cterasdk.edge').info('Audit logs disabled') + logger.info('Audit logs disabled') diff --git a/cterasdk/edge/backup.py b/cterasdk/edge/backup.py index f92785f5..047a0879 100644 --- a/cterasdk/edge/backup.py +++ b/cterasdk/edge/backup.py @@ -7,6 +7,9 @@ from .directorytree import DirectoryTree +logger = logging.getLogger('cterasdk.edge') + + class AttachRC: OK = 'OK' NotFound = 'NotFound' @@ -76,7 +79,7 @@ def configure(self, passphrase=None): :param str,optional passphrase: Passphrase for the backup, defaults to None """ - logging.getLogger('cterasdk.edge').info('Configuring cloud backup.') + logger.info('Configuring cloud backup.') try: settings = self._attach(passphrase) @@ -85,7 +88,7 @@ def configure(self, passphrase=None): self._configure_backup_settings(settings) - logging.getLogger('cterasdk.edge').info('Cloud backup configuration completed successfully.') + logger.info('Cloud backup configuration completed successfully.') def is_configured(self): """ @@ -98,28 +101,28 @@ def is_configured(self): def start(self): """ Start backup """ - logging.getLogger('cterasdk.edge').info("Starting cloud backup.") + logger.info("Starting cloud backup.") self._edge.api.execute("/status/sync", "start") def suspend(self): """ Suspend backup """ - logging.getLogger('cterasdk.edge').info("Suspending cloud backup.") + logger.info("Suspending cloud backup.") self._edge.api.execute("/status/sync", "pause") def unsuspend(self): """ Unsuspend backup """ - logging.getLogger('cterasdk.edge').info("Suspending cloud backup.") + logger.info("Suspending cloud backup.") self._edge.api.execute("/status/sync", "resume") def _attach(self, sharedSecret): try: - logging.getLogger('cterasdk.edge').debug('Attaching to a backup folder.') + logger.debug('Attaching to a backup folder.') settings = self._attach_folder() except AttachEncrypted as param: - logging.getLogger('cterasdk.edge').debug('Attaching to an encrypted backup folder.') + logger.debug('Attaching to an encrypted backup folder.') settings = self._attach_encrypted_folder(param.encryptedFolderKey, param.passPhraseSalt, sharedSecret) settings.encryptionMode = param.encryptionMode - logging.getLogger('cterasdk.edge').debug('Successfully attached to a backup folder.') + logger.debug('Successfully attached to a backup folder.') return settings @@ -153,36 +156,36 @@ def _process_attach_response(response): return param if rc == AttachRC.NotFound: - logging.getLogger('cterasdk.edge').debug('Could not find an existing backup folder.') + logger.debug('Could not find an existing backup folder.') raise NotFound() if rc == AttachRC.IsEncrypted: raise AttachEncrypted(response.encryptionMode, response.encryptedFolderKey, response.passPhraseSalt) if rc == AttachRC.CheckCodeInCorrect: - logging.getLogger('cterasdk.edge').error('Incorrect passphrase.') + logger.error('Incorrect passphrase.') raise IncorrectPassphrase() if rc == AttachRC.ClocksOutOfSync: - logging.getLogger('cterasdk.edge').error('Intializing backup failed. Clocks are out of sync. %s', {'rc': rc}) + logger.error('Intializing backup failed. Clocks are out of sync (rc=%s).', rc) raise ClocksOutOfSync() if rc == AttachRC.InternalServerError: - logging.getLogger('cterasdk.edge').error('Attach failed. %s', {'rc': rc}) + logger.error('Attach failed (rc=%s).', rc) elif rc == AttachRC.PermissionDenied: - logging.getLogger('cterasdk.edge').error('Attach failed. %s', {'rc': rc}) + logger.error('Attach failed (rc=%s).', rc) else: - logging.getLogger('cterasdk.edge').error('Unknown error, %s', {'rc': rc}) - raise CTERAException('Failed to attach to backup folder', None, rc=rc) + logger.error('Unknown error (rc=%s)', rc) + raise CTERAException(f'Failed to attach to backup folder (rc={rc}).') def _create_folder(self, passphrase): param = Object() if passphrase is not None: - logging.getLogger('cterasdk.edge').debug('Creting a passphrase-encrypted backup folder.') + logger.debug('Creting a passphrase-encrypted backup folder.') param.encryptionMode = EncryptionMode.Secret param.sharedSecret = passphrase else: - logging.getLogger('cterasdk.edge').debug('Creating a backup folder.') + logger.debug('Creating a backup folder.') param.encryptionMode = EncryptionMode.Recoverable task = self._edge.api.execute('/status/services', 'createFolder', param) @@ -200,19 +203,19 @@ def _process_create_response(response): rc = response.createFolderRC if rc == CreateFolderRC.OK: - logging.getLogger('cterasdk.edge').debug('Backup folder created successfully.') + logger.debug('Backup folder created successfully.') param = Object() param.sharedSecret = response.sharedSecret param.passPhraseSalt = response.passPhraseSalt return param if rc == CreateFolderRC.InternalServerError: - logging.getLogger('cterasdk.edge').error('Backup folder creation failed. %s', {'rc': rc}) + logger.error('Backup folder creation failed (rc=%s).', rc) elif rc == CreateFolderRC.PermissionDenied: - logging.getLogger('cterasdk.edge').error('Backup folder creation failed. %s', {'rc': rc}) + logger.error('Backup folder creation failed (rc=%s).', rc) elif rc == CreateFolderRC.FolderAlreadyExists: return None - raise CTERAException('Failed to create backup folder', None, rc=rc) + raise CTERAException(f'Failed to create backup folder (rc={rc})') def _wait(self, task): task = self._edge.tasks.wait(task) @@ -227,7 +230,7 @@ def _configure_backup_settings(self, param): backup_settings.sharedSecret = param.sharedSecret backup_settings.passPhraseSalt = param.passPhraseSalt - logging.getLogger('cterasdk.edge').debug('Configuring backup settings.') + logger.debug('Configuring backup settings.') self._edge.api.put('/config/backup', backup_settings) @@ -239,7 +242,7 @@ class BackupFiles(BaseCommand): def unselect_all(self): """ Unselect all files from backup """ backup_config = self._fetch_backup_config(BackupFiles.ALL_FILES) - logging.getLogger('cterasdk.edge').info('Unselecting all files from backup.') + logger.info('Unselecting all files from backup.') directory_tree = DirectoryTree(backup_config.directoryTree) directory_tree.unselect_all() backup_config.directoryTree = directory_tree.root @@ -250,10 +253,10 @@ def _fetch_backup_config(self, name=None): if name: for backup_config in backup_configs: if backup_config.name == name: - logging.getLogger('cterasdk.edge').info('Found backup config. %s', {'name': name}) + logger.info('Found backup config: %s', name) return backup_config - logging.getLogger('cterasdk.edge').error('Could not find backup config. %s', {'name': name}) - raise CTERAException('Could not find backup config', None, name=name) + logger.error('Could not find backup config: %s', name) + raise CTERAException(f'Could not find backup config: {name}') return backup_configs def _update_backup_config(self, backup_config): diff --git a/cterasdk/edge/cache.py b/cterasdk/edge/cache.py index 54d8bf0b..f4ca3af4 100644 --- a/cterasdk/edge/cache.py +++ b/cterasdk/edge/cache.py @@ -6,31 +6,34 @@ from .base_command import BaseCommand +logger = logging.getLogger('cterasdk.edge') + + class Cache(BaseCommand): """ Edge Filer cache configuration """ def enable(self): """ Enable caching """ - logging.getLogger('cterasdk.edge').info('Enabling caching.') + logger.info('Enabling caching.') self._set_operation_mode(OperationMode.CachingGateway) def disable(self): """ Disable caching """ - logging.getLogger('cterasdk.edge').info('Disabling caching.') + logger.info('Disabling caching.') self._set_operation_mode(OperationMode.Disabled) def force_eviction(self): """ Force eviction """ - logging.getLogger('cterasdk.edge').info("Starting file eviction.") + logger.info("Starting file eviction.") self._edge.api.execute("/config/cloudsync", "forceExecuteEvictor", None) - logging.getLogger('cterasdk.edge').info("Eviction started.") + logger.info("Eviction started.") def is_enabled(self): return self._edge.api.get('/config/cloudsync/cloudExtender/operationMode') == OperationMode.CachingGateway def _set_operation_mode(self, mode): self._edge.api.put('/config/cloudsync/cloudExtender/operationMode', mode) - logging.getLogger('cterasdk.edge').info('Device opreation mode changed. %s', {'mode': mode}) + logger.info('Device opreation mode changed. %s', {'mode': mode}) def pin(self, path): """ @@ -39,7 +42,7 @@ def pin(self, path): :param str path: Directory path """ directory_tree = self._fetch_pinning_config() - logging.getLogger('cterasdk.edge').info('Pinning folder. %s', {'path': path}) + logger.info('Pinning folder. %s', {'path': path}) directory_tree.include_folder(path) self._update_pinning_config(directory_tree) @@ -50,7 +53,7 @@ def pin_exclude(self, path): :param str path: Directory path """ directory_tree = self._fetch_pinning_config() - logging.getLogger('cterasdk.edge').info('Excluding sub-folder. %s', {'path': path}) + logger.info('Excluding sub-folder. %s', {'path': path}) directory_tree.exclude_folder(path) self._update_pinning_config(directory_tree) @@ -61,21 +64,21 @@ def remove_pin(self, path): :param str path: Directory path """ directory_tree = self._fetch_pinning_config() - logging.getLogger('cterasdk.edge').info('Removing pin from previously pinned folder. %s', {'path': path}) + logger.info('Removing pin from previously pinned folder. %s', {'path': path}) directory_tree.remove_selection(path) self._update_pinning_config(directory_tree) def pin_all(self): """ Pin all folders """ directory_tree = self._fetch_pinning_config() - logging.getLogger('cterasdk.edge').info('Pinning all folders.') + logger.info('Pinning all folders.') directory_tree.select_all() self._update_pinning_config(directory_tree) def unpin_all(self): """ Remove all folder pins """ directory_tree = self._fetch_pinning_config() - logging.getLogger('cterasdk.edge').info('Removing all folder pins.') + logger.info('Removing all folder pins.') directory_tree.unselect_all() self._update_pinning_config(directory_tree) @@ -93,7 +96,7 @@ def pin_recursive(self, path): :param str path: Directory path """ directory_tree = self._fetch_pinning_config() - logging.getLogger('cterasdk.edge').info('Recursively pinning folder and all subfolders. %s', {'path': path}) + logger.info('Recursively pinning folder and all subfolders. %s', {'path': path}) directory_tree.include_folder_recursive(path) self._update_pinning_config(directory_tree) @@ -104,6 +107,6 @@ def unpin_recursive(self, path): :param str path: Directory path """ directory_tree = self._fetch_pinning_config() - logging.getLogger('cterasdk.edge').info('Recursively unpinning folder and all subfolders. %s', {'path': path}) + logger.info('Recursively unpinning folder and all subfolders. %s', {'path': path}) directory_tree.exclude_folder_recursive(path) self._update_pinning_config(directory_tree) diff --git a/cterasdk/edge/cli.py b/cterasdk/edge/cli.py index 219ca71d..63c19c8b 100644 --- a/cterasdk/edge/cli.py +++ b/cterasdk/edge/cli.py @@ -3,6 +3,9 @@ from .base_command import BaseCommand +logger = logging.getLogger('cterasdk.edge') + + class CLI(BaseCommand): """ CLI APIs """ @@ -13,10 +16,10 @@ def run_command(self, cli_command): :param str cli_command: Command :return str: Response """ - logging.getLogger('cterasdk.edge').warning('Usage of the CLI module is discouraged. ' + logger.warning('Usage of the CLI module is discouraged. ' 'Review available modules to determine if there are existing ones that ' 'support this action.') - logging.getLogger('cterasdk.edge').info("Executing CLI command. %s", {'cli_command': cli_command}) + logger.info("Executing CLI command. %s", {'cli_command': cli_command}) response = self._edge.api.execute('/config/device', 'debugCmd', cli_command) - logging.getLogger('cterasdk.edge').info("CLI command executed. %s", {'cli_command': cli_command}) + logger.info("CLI command executed. %s", {'cli_command': cli_command}) return response diff --git a/cterasdk/edge/config.py b/cterasdk/edge/config.py index ab4bc249..6f7a20a7 100644 --- a/cterasdk/edge/config.py +++ b/cterasdk/edge/config.py @@ -108,8 +108,9 @@ def load_config(config): if database: logger.info('Completed parsing the Edge Filer configuration. %s', {'firmware': database.firmware}) return database - logger.error("Failed parsing the Edge Filer's configuration.") - raise CTERAException("Failed parsing the Edge Filer's configuration") + message = "An error occurred attempting to parse the Edge Filer's configuration." + logger.error(message) + raise CTERAException(message) def export(self, destination=None): """ diff --git a/cterasdk/edge/connection.py b/cterasdk/edge/connection.py index acea3e11..a1cc3e65 100644 --- a/cterasdk/edge/connection.py +++ b/cterasdk/edge/connection.py @@ -3,11 +3,14 @@ from ..common.utils import tcp_connect +logger = logging.getLogger('cterasdk.edge') + + def test(Edge): tcp_connect(Edge.host(), Edge.port()) - logging.getLogger('cterasdk.edge').debug('Trying to obtain login info.') + logger.debug('Trying to obtain login info.') response = Edge.api.get('/nosession/logininfo') if response is not None: - logging.getLogger('cterasdk.edge').debug('Successfully obtained login info. %s', {'hostname': response.hostname}) + logger.debug('Successfully obtained login info for: %s', response.hostname) return response - raise CTERAException('Could not obtain login info for device {host}:{port}.') + raise CTERAException('Could not obtain login info for device (host={host}, port={port}).') diff --git a/cterasdk/edge/ctera_migrate.py b/cterasdk/edge/ctera_migrate.py index dead3d7a..9c82598c 100644 --- a/cterasdk/edge/ctera_migrate.py +++ b/cterasdk/edge/ctera_migrate.py @@ -5,6 +5,9 @@ from .enum import TaskType, SourceType +logger = logging.getLogger('cterasdk.edge') + + class CTERAMigrate(BaseCommand): """Edge Filer Migration Tool APIs """ @@ -95,7 +98,7 @@ def details(self, task): response = self._edge.migrate.get('/tasks/history', params={'id': task.id}) # pylint: disable=protected-access if response.history: return Jobs(response.history) - logging.getLogger('cterasdk.edge').error('Task not found. %s', {'task_id': task.id}) + logger.error('Task not found. %s', {'task_id': task.id}) return None def results(self, task): @@ -105,7 +108,7 @@ def results(self, task): if task.type == 'migration': return self._edge.migrate.get('/migration/results', # pylint: disable=protected-access params={'id': task.id}).migration - logging.getLogger('cterasdk.edge').error('Could not determine task type. %s', {'id': task.id, 'type': task.type, 'name': task.name}) + logger.error('Could not determine task type. %s', {'id': task.id, 'type': task.type, 'name': task.name}) return None diff --git a/cterasdk/edge/dedup.py b/cterasdk/edge/dedup.py index cec3575b..009742d7 100644 --- a/cterasdk/edge/dedup.py +++ b/cterasdk/edge/dedup.py @@ -5,6 +5,9 @@ from .base_command import BaseCommand +logger = logging.getLogger('cterasdk.edge') + + class Dedup(BaseCommand): """ Edge Filer Local Deduplication APIs """ @@ -19,7 +22,7 @@ def enable(self, reboot=False, wait=False): :param bool reboot: Reboot, defaults to ``False`` :param bool,optional wait: Wait for reboot to complete, defaults to False """ - logging.getLogger('cterasdk.edge').info("Enabling local deduplication.") + logger.info("Enabling local deduplication.") response = self._edge.api.put('/config/dedup/useLocalMapFileDedup', True) self._wait_for_reboot(reboot, wait) return response @@ -31,7 +34,7 @@ def disable(self, reboot=False, wait=False): :param bool reboot: Reboot, defaults to ``False`` :param bool,optional wait: Wait for reboot to complete, defaults to False """ - logging.getLogger('cterasdk.edge').info("Disabling local deduplication.") + logger.info("Disabling local deduplication.") response = self._edge.api.put('/config/dedup/useLocalMapFileDedup', False) self._wait_for_reboot(reboot, wait) return response @@ -59,7 +62,7 @@ def run(self): """ Run the regeneration process """ - logging.getLogger('cterasdk.edge').info("Executing the dedup regeneration process.") + logger.info("Executing the dedup regeneration process.") return self._edge.api.execute('/config/dedup', 'regenerate', Object()) def status(self): diff --git a/cterasdk/edge/directoryservice.py b/cterasdk/edge/directoryservice.py index b7c28005..c458c4f2 100644 --- a/cterasdk/edge/directoryservice.py +++ b/cterasdk/edge/directoryservice.py @@ -7,6 +7,9 @@ from .types import TCPService +logger = logging.getLogger('cterasdk.edge') + + class DirectoryService(BaseCommand): """ Edge Filer Active Directory configuration APIs @@ -42,14 +45,14 @@ def connect(self, domain, username, password, ou=None, check_connection=False): param.password = password if ou is not None: param.ouPath = ou - logging.getLogger('cterasdk.edge').info("Connecting to Active Directory. %s", {'domain': domain, 'user': username}) + logger.info("Connecting to Active Directory (domain=%s, user=%s).", domain, username) try: self._edge.api.execute("/status/fileservices/cifs", "joinDomain", param) except CTERAException as error: - logging.getLogger('cterasdk.edge').error("Failed connecting to Active Directory.") + logger.error("Failed connecting to Active Directory.") raise error - logging.getLogger('cterasdk.edge').info("Connected to Active Directory.") + logger.info("Connected to Active Directory.") def _check_domain_connectivity(self, domain): port = 389 @@ -58,8 +61,7 @@ def _check_domain_connectivity(self, domain): connection_results = self._edge.network.diagnose([TCPService(host, port) for host in domain_controllers]) for connection_result in connection_results: if not connection_result.is_open: - logging.getLogger('cterasdk.edge').error("Connection failed. No traffic allowed over port %(port)s", - dict(port=connection_result.port)) + logger.error("Connection failed. No traffic allowed over port %(port)s", dict(port=connection_result.port)) raise ConnectionError(f'Unable to establish LDAP connection {connection_result.host}:{connection_result.port}') def get_static_domain_controller(self): @@ -112,13 +114,13 @@ def set_advanced_mapping(self, mappings): if mapping.domainFlatName in domains: advanced_mapping.append(mapping) else: - logging.getLogger('cterasdk.edge').warning('Invalid mapping. Could not find domain. %s', {'domain': mapping.domainFlatName}) + logger.warning('Invalid mapping. Could not find domain. %s', {'domain': mapping.domainFlatName}) - logging.getLogger('cterasdk.edge').debug('Updating advanced mapping. %s', { + logger.debug('Updating advanced mapping. %s', { 'domains': [mapping.domainFlatName for mapping in advanced_mapping] }) response = self._edge.api.put('/config/fileservices/cifs/idMapping/map', advanced_mapping) - logging.getLogger('cterasdk.edge').info('Updated advanced mapping.') + logger.info('Updated advanced mapping.') return response @@ -147,7 +149,7 @@ def disconnect(self): """ Disconnect from Active Directory Service """ - logging.getLogger('cterasdk.edge').info("Disconnecting from Active Directory.") + logger.info("Disconnecting from Active Directory.") cifs = self._edge.api.get('/config/fileservices/cifs') cifs.type = "workgroup" @@ -155,4 +157,4 @@ def disconnect(self): cifs.domain = None self._edge.api.put('/config/fileservices/cifs', cifs) - logging.getLogger('cterasdk.edge').info("Disconnected from Active Directory.") + logger.info("Disconnected from Active Directory.") diff --git a/cterasdk/edge/directorytree.py b/cterasdk/edge/directorytree.py index 6162cbb9..496aee36 100644 --- a/cterasdk/edge/directorytree.py +++ b/cterasdk/edge/directorytree.py @@ -35,9 +35,9 @@ def _configure_selection(self, path, is_dir, include): node, parts = self._lookup(path) if not parts: if is_dir and not DirectoryTree._is_dir(node): - raise CTERAException('Expected to find a directory but found file', None, path=path) + raise CTERAException(f'Expected to find a directory but found file: {path}') if DirectoryTree._is_dir(node) and not is_dir: - raise CTERAException('Expected to find a file but found a directory', None, path=path) + raise CTERAException(f'Expected to find a file but found a directory: {path}') node.isIncluded = include node.children = None else: @@ -53,7 +53,7 @@ def remove_selection(self, path): node.isIncluded = False node.children = None else: - raise CTERAException('Could not find directory path', None, path=path) + raise CTERAException(f'Could not find directory path: {path}') def select_all(self): self.root.isIncluded = True @@ -66,7 +66,7 @@ def unselect_all(self): def _check_root_dir(self, path): parts = path.split('/') if self.root.name != parts[0]: - raise CTERAException('Invalid root directory', None, input=parts[0], should_start_with=self.root.name) + raise CTERAException(f'Invalid root directory: {parts[0]}.') def _lookup(self, path): parts = path.split('/')[1:] diff --git a/cterasdk/edge/drive.py b/cterasdk/edge/drive.py index 1db5815f..b25ad1ef 100644 --- a/cterasdk/edge/drive.py +++ b/cterasdk/edge/drive.py @@ -4,6 +4,9 @@ from .base_command import BaseCommand +logger = logging.getLogger('cterasdk.edge') + + class Drive(BaseCommand): """ Edge Filer Drive APIs @@ -36,7 +39,7 @@ def format(self, name): self._edge.api.execute("/proc/storage", "format", param) - logging.getLogger('cterasdk.edge').info('Formatting drive. %s', {'drive': name}) + logger.info('Formatting drive. %s', {'drive': name}) def format_all(self): """ Format all drives """ diff --git a/cterasdk/edge/files/io.py b/cterasdk/edge/files/io.py index 92b037ca..6bdbcc7b 100644 --- a/cterasdk/edge/files/io.py +++ b/cterasdk/edge/files/io.py @@ -1,8 +1,8 @@ import logging from ...cio.common import encode_request_parameter from ...cio import edge as fs -from ...cio import exceptions -from ...exceptions import HTTPError +from ...exceptions.transport import HTTPError +from ...exceptions.io import RestrictedPathError logger = logging.getLogger('cterasdk.edge') @@ -43,7 +43,7 @@ def makedirs(edge, path): path = fs.EdgePath(path.scope, '/'.join(directories[:i])) try: mkdir(edge, path) - except exceptions.RestrictedPathError: + except RestrictedPathError: logger.warning('Creating a folder in the specified location is forbidden: %s', path.reference.as_posix()) diff --git a/cterasdk/edge/firmware.py b/cterasdk/edge/firmware.py index 1eceab1b..f7238c6b 100644 --- a/cterasdk/edge/firmware.py +++ b/cterasdk/edge/firmware.py @@ -22,7 +22,7 @@ def upgrade(self, file_path, reboot=True, wait_for_reboot=True): """ upload_task_info = self._upload_firmware(file_path) if upload_task_info.rc != 0: - raise CTERAException(message='Failed to upload the new firmware', path=file_path) + raise CTERAException(f'Failed to upload firmware: {file_path}') self._wait_for_completion(upload_task_info.taskPointer) if reboot: self._edge.power.reboot(wait=wait_for_reboot) @@ -45,7 +45,4 @@ def _wait_for_completion(self, task_pointer): if not is_running: if task_status.status == UploadTaskStatus.COMPLETE: return - raise CTERAException( - message=f'Filer failed to receive the new firmware - {task_status.statusMessage}', - instance=task_status - ) + raise CTERAException(f'An error occurred during firmware upgrade. Status: {task_status.statusMessage}') diff --git a/cterasdk/edge/ftp.py b/cterasdk/edge/ftp.py index 7baa3935..01e8cddb 100644 --- a/cterasdk/edge/ftp.py +++ b/cterasdk/edge/ftp.py @@ -5,6 +5,9 @@ from .base_command import BaseCommand +logger = logging.getLogger('cterasdk.edge') + + class FTP(BaseCommand): """ Edge Filer FTP configuration APIs """ @@ -29,9 +32,9 @@ def is_disabled(self): return self._edge.api.get('/config/fileservices/ftp/mode') == Mode.Disabled def _set_mode(self, enabled): - logging.getLogger('cterasdk.edge').info('%s FTP server.', ('Enabling' if enabled else 'Disabling')) + logger.info('%s FTP server.', ('Enabling' if enabled else 'Disabling')) self._edge.api.put('/config/fileservices/ftp/mode', Mode.Enabled if enabled else Mode.Disabled) - logging.getLogger('cterasdk.edge').info('FTP server %s.', ('enabled' if enabled else 'disabled')) + logger.info('FTP server %s.', ('enabled' if enabled else 'disabled')) def modify( self, diff --git a/cterasdk/edge/groups.py b/cterasdk/edge/groups.py index 01cf992c..2bb93f56 100644 --- a/cterasdk/edge/groups.py +++ b/cterasdk/edge/groups.py @@ -5,6 +5,9 @@ from .types import UserGroupEntry +logger = logging.getLogger('cterasdk.edge') + + class Groups(BaseCommand): def get(self, name=None): @@ -37,11 +40,11 @@ def add_members(self, group, members): members = [v for k, v in new_member_dict.items()] - logging.getLogger('cterasdk.edge').info('Adding group members. %s', {'group': group}) + logger.info('Adding group members. %s', {'group': group}) self._edge.api.put('/config/auth/groups/' + group + '/members', members) - logging.getLogger('cterasdk.edge').info('Group members added. %s', {'group': group}) + logger.info('Group members added. %s', {'group': group}) def remove_members(self, group, members): """ @@ -63,11 +66,11 @@ def remove_members(self, group, members): if not remove_members_dict.get(user_group_entry.principal_type + '#' + user_group_entry.name, False): members.append(member) - logging.getLogger('cterasdk.edge').info('Removing group members. %s', {'group': group}) + logger.info('Removing group members. %s', {'group': group}) self._edge.api.put('/config/auth/groups/' + group + '/members', members) - logging.getLogger('cterasdk.edge').info('Group members removed. %s', {'group': group}) + logger.info('Group members removed. %s', {'group': group}) @staticmethod def _validate_members(members): diff --git a/cterasdk/edge/licenses.py b/cterasdk/edge/licenses.py index 60bbab5a..5bf8b1fe 100644 --- a/cterasdk/edge/licenses.py +++ b/cterasdk/edge/licenses.py @@ -5,6 +5,9 @@ from .base_command import BaseCommand +logger = logging.getLogger('cterasdk.edge') + + class Licenses(BaseCommand): """ Edge Filer License Configuration APIs """ @@ -15,7 +18,7 @@ def __init__(self, edge): @staticmethod def infer(ctera_license): if License.__dict__.get(ctera_license) is None: - logging.getLogger('cterasdk.edge').error('Invalid license type. %s', {'license': ctera_license}) + logger.error('Invalid license type. %s', {'license': ctera_license}) options = [v for k, v in License.__dict__.items() if not k.startswith('_')] raise InputError('Invalid license type', ctera_license, options) @@ -30,11 +33,11 @@ def apply(self, ctera_license): """ inferred_license = Licenses.infer(ctera_license) - logging.getLogger('cterasdk.edge').info('Applying license. %s', {'license': ctera_license}) + logger.info('Applying license. %s', {'license': ctera_license}) self._edge.api.put('/config/device/activeLicenseType', inferred_license) - logging.getLogger('cterasdk.edge').info('License applied. %s', {'license': ctera_license}) + logger.info('License applied. %s', {'license': ctera_license}) def get(self): """ @@ -63,9 +66,9 @@ def add(self, code): :param str code: License code """ - logging.getLogger('cterasdk.edge').info('Installing license. %s', {'code': code}) + logger.info('Installing license. %s', {'code': code}) response = self._edge.api.add('/config/device/licenses', code) - logging.getLogger('cterasdk.edge').info("License added. %s", {'code': code}) + logger.info("License added. %s", {'code': code}) return response def clear(self): diff --git a/cterasdk/edge/login.py b/cterasdk/edge/login.py index aa236482..92e346da 100644 --- a/cterasdk/edge/login.py +++ b/cterasdk/edge/login.py @@ -4,6 +4,9 @@ from .base_command import BaseCommand +logger = logging.getLogger('cterasdk.edge') + + class Login(BaseCommand): def info(self): @@ -16,10 +19,10 @@ def login(self, username, password): host = self._edge.host() try: self._edge.api.form_data('/login', {'username': username, 'password': password}) - logging.getLogger('cterasdk.edge').info("User logged in. %s", {'host': host, 'user': username}) + logger.info("User logged in. %s", {'host': host, 'user': username}) self._edge.ctera_migrate.login() except CTERAException: - logging.getLogger('cterasdk.edge').error("Login failed. %s", {'host': host, 'user': username}) + logger.error("Login failed. %s", {'host': host, 'user': username}) raise def sso(self, ticket): @@ -28,7 +31,7 @@ def sso(self, ticket): :param str ticket: SSO Ticket. """ - logging.getLogger('cterasdk.edge').info("Performing Single Sign On.") + logger.info("Performing Single Sign On.") self._edge.api.get('/ssologin', params={'ticket': ticket}) self._edge.ctera_migrate.login() @@ -37,7 +40,7 @@ def logout(self): user = self._edge.session().account.name try: self._edge.api.form_data('/logout', {'foo': 'bar'}) - logging.getLogger('cterasdk.edge').info("User logged out. %s", {'host': host, 'user': user}) + logger.info("User logged out. %s", {'host': host, 'user': user}) except CTERAException: - logging.getLogger('cterasdk.edge').error("Logout failed. %s", {'host': host, 'user': user}) + logger.error("Logout failed. %s", {'host': host, 'user': user}) raise diff --git a/cterasdk/edge/logs.py b/cterasdk/edge/logs.py index a633c89a..2032d9ff 100644 --- a/cterasdk/edge/logs.py +++ b/cterasdk/edge/logs.py @@ -7,6 +7,9 @@ from .base_command import BaseCommand +logger = logging.getLogger('cterasdk.edge') + + class Logs(BaseCommand): """ Edge Filer Logs APIs @@ -27,10 +30,10 @@ def settings(self, retention, min_severity=None): log_config.LogKeepPeriod = retention if min_severity: log_config.minSeverity = min_severity - logging.getLogger('cterasdk.edge').info('Updating log settings. %s', + logger.info('Updating log settings. %s', {'retention': retention, 'min_severity': log_config.minSeverity}) self._edge.api.put('/config/logging/general', log_config) - logging.getLogger('cterasdk.edge').info('Log settings updated. %s', + logger.info('Log settings updated. %s', {'retention': retention, 'min_severity': log_config.minSeverity}) def logs(self, topic, include=None, minSeverity=enum.Severity.INFO): diff --git a/cterasdk/edge/mail.py b/cterasdk/edge/mail.py index 3675a1b9..783d717c 100644 --- a/cterasdk/edge/mail.py +++ b/cterasdk/edge/mail.py @@ -4,6 +4,9 @@ from .base_command import BaseCommand +logger = logging.getLogger('cterasdk.edge') + + class Mail(BaseCommand): """ Edge Filer Mail Server configuration APIs """ @@ -33,17 +36,17 @@ def enable(self, smtp_server, port=25, username=None, password=None, use_tls=Tru if settings.useTLS != use_tls: settings.useTLS = use_tls - logging.getLogger('cterasdk.edge').info('Enabling mail server.') + logger.info('Enabling mail server.') self._edge.api.put('/config/logging/alert', settings) - logging.getLogger('cterasdk.edge').info( + logger.info( 'Updated mail server settings. %s', {'SMTPServer': smtp_server, 'port': port, 'username': username, 'useTLS': use_tls} ) def disable(self): """ Disable e-mail delivery using a custom SMTP server """ - logging.getLogger('cterasdk.edge').info('Disabling mail server.') + logger.info('Disabling mail server.') self._edge.api.put('/config/logging/alert/useCustomServer', False) - logging.getLogger('cterasdk.edge').info('Mail server disabled.') + logger.info('Mail server disabled.') diff --git a/cterasdk/edge/network.py b/cterasdk/edge/network.py index fd60c53f..ef801ca4 100644 --- a/cterasdk/edge/network.py +++ b/cterasdk/edge/network.py @@ -8,6 +8,9 @@ from .base_command import BaseCommand +logger = logging.getLogger('cterasdk.edge') + + class Network(BaseCommand): """ Edge Filer Network configuration APIs """ @@ -56,11 +59,11 @@ def set_static_ipaddr(self, address, subnet, gateway, primary_dns_server, second if secondary_dns_server is not None: ip.DNSServer2 = secondary_dns_server - logging.getLogger('cterasdk.edge').info('Configuring a static ip address.') + logger.info('Configuring a static ip address.') self._edge.api.put('/config/network/ports/0/ip', ip) - logging.getLogger('cterasdk.edge').info( + logger.info( 'Network settings updated. %s', {'address': address, 'subnet': subnet, 'gateway': gateway, 'DNS1': primary_dns_server, 'DNS2': secondary_dns_server} ) @@ -79,11 +82,11 @@ def set_static_nameserver(self, primary_dns_server, secondary_dns_server=None): if secondary_dns_server is not None: ip.DNSServer2 = secondary_dns_server - logging.getLogger('cterasdk.edge').info('Configuring nameserver settings.') + logger.info('Configuring nameserver settings.') self._edge.api.put('/config/network/ports/0/ip', ip) - logging.getLogger('cterasdk.edge').info('Nameserver settings updated. %s', + logger.info('Nameserver settings updated. %s', {'DNS1': primary_dns_server, 'DNS2': secondary_dns_server}) def enable_dhcp(self): @@ -94,11 +97,11 @@ def enable_dhcp(self): ip.DHCPMode = Mode.Enabled ip.autoObtainDNS = True - logging.getLogger('cterasdk.edge').info('Enabling DHCP.') + logger.info('Enabling DHCP.') self._edge.api.put('/config/network/ports/0/ip', ip) - logging.getLogger('cterasdk.edge').info('Network settings updated. Enabled DHCP.') + logger.info('Network settings updated. Enabled DHCP.') def diagnose(self, services): """ @@ -122,18 +125,18 @@ def tcp_connect(self, service): param.address = service.host param.port = service.port - logging.getLogger('cterasdk.edge').info("Testing connection. %s", {'host': service.host, 'port': service.port}) + logger.info("Testing connection. %s", {'host': service.host, 'port': service.port}) task = self._edge.api.execute("/status/network", "tcpconnect", param) try: task = self._edge.tasks.wait(task) - logging.getLogger('cterasdk.edge').debug("Obtained connection status. %s", {'status': task.result.rc}) + logger.debug("Obtained connection status. %s", {'status': task.result.rc}) if task.result.rc == "Open": return TCPConnectResult(service.host, service.port, True) except TaskError: pass - logging.getLogger('cterasdk.edge').warning("Couldn't establish TCP connection. %s", {'address': service.host, 'port': service.port}) + logger.warning("Couldn't establish TCP connection. %s", {'address': service.host, 'port': service.port}) return TCPConnectResult(service.host, service.port, False) @@ -209,9 +212,9 @@ def _configure(self, enabled, address=None, port=None, username=None, password=N param.username = username if password: param.password = password - logging.getLogger('cterasdk.edge').info('Updating Proxy Server Configuration.') + logger.info('Updating Proxy Server Configuration.') response = self._edge.api.put('/config/network/proxy', param) - logging.getLogger('cterasdk.edge').info('Updated Proxy Server Configuration.') + logger.info('Updated Proxy Server Configuration.') return response def disable(self): @@ -221,7 +224,7 @@ def disable(self): :returns: Proxy settings :rtype: cterasdk.common.object.Object """ - logging.getLogger('cterasdk.edge').info('Disabling Proxy.') + logger.info('Disabling Proxy.') return self._configure(False) @@ -246,7 +249,7 @@ def _configure(self, jumbo, mtu): settings = self._edge.api.get('/config/network/ports/0/ethernet') settings.jumbo = jumbo settings.mtu = mtu - logging.getLogger('cterasdk.edge').info('Configuring MTU. %s', {'MTU': mtu}) + logger.info('Configuring MTU. %s', {'MTU': mtu}) return self._edge.api.put('/config/network/ports/0/ethernet', settings) @@ -271,12 +274,12 @@ def add(self, source_ip, destination_ip_mask): param.GwIP = str(parse_to_ipaddress(source_ip)) param.DestIpMask = str(parse_to_ipaddress(destination_ip_mask)).replace("/", "_") res = self._edge.api.add('/config/network/static_routes', param) - logging.getLogger('cterasdk.edge').info( + logger.info( "Static route updated. %s", {'Source': param.GwIP, 'Destination': destination_ip_mask}) return res except CTERAException as error: - logging.getLogger('cterasdk.edge').error("Static route creation failed.") - raise CTERAException('Static route creation failed', error) + logger.error("Static route creation failed.") + raise CTERAException('Static route creation failed') from error def remove(self, destination_ip_mask): """ @@ -287,12 +290,12 @@ def remove(self, destination_ip_mask): try: dest_ip_mask = str(parse_to_ipaddress(destination_ip_mask)).replace("/", "_") response = self._edge.api.delete(f'/config/network/static_routes/{dest_ip_mask}') - logging.getLogger('cterasdk.edge').info( + logger.info( "Static route deleted. %s", {'Destination': dest_ip_mask}) return response except CTERAException as error: - logging.getLogger('cterasdk.edge').error("Static route deletion failed.") - raise CTERAException('Static route deletion failed', error) + logger.error("Static route deletion failed.") + raise CTERAException('Static route deletion failed') from error def clear(self): """ @@ -300,7 +303,7 @@ def clear(self): """ try: self._edge.api.execute('/config/network', 'cleanStaticRoutes') - logging.getLogger('cterasdk.edge').info('Static routes were deleted successfully') + logger.info('Static routes were deleted successfully') except CTERAException as error: - logging.getLogger('cterasdk.edge').error("Failed to clear static routes") - raise CTERAException('Failed to clear static routes', error) + logger.error("Failed to clear static routes") + raise CTERAException('Failed to clear static routes') from error diff --git a/cterasdk/edge/nfs.py b/cterasdk/edge/nfs.py index 6236871d..3f951e00 100644 --- a/cterasdk/edge/nfs.py +++ b/cterasdk/edge/nfs.py @@ -5,6 +5,9 @@ from .base_command import BaseCommand +logger = logging.getLogger('cterasdk.edge') + + class NFS(BaseCommand): """ Edge Filer NFS configuration """ @@ -29,9 +32,9 @@ def is_disabled(self): return self._edge.api.get('/config/fileservices/nfs/mode') == Mode.Disabled def _set_mode(self, enabled): - logging.getLogger('cterasdk.edge').info('%s NFS server.', ('Enabling' if enabled else 'Disabling')) + logger.info('%s NFS server.', ('Enabling' if enabled else 'Disabling')) self._edge.api.put('/config/fileservices/nfs/mode', Mode.Enabled if enabled else Mode.Disabled) - logging.getLogger('cterasdk.edge').info('NFS server %s.', ('enabled' if enabled else 'disabled')) + logger.info('NFS server %s.', ('enabled' if enabled else 'disabled')) def modify( self, diff --git a/cterasdk/edge/ntp.py b/cterasdk/edge/ntp.py index 2f6546de..783de73a 100644 --- a/cterasdk/edge/ntp.py +++ b/cterasdk/edge/ntp.py @@ -4,6 +4,9 @@ from .base_command import BaseCommand +logger = logging.getLogger('cterasdk.edge') + + class NTP(BaseCommand): """ Edge Filer NTP configuration """ @@ -20,17 +23,17 @@ def enable(self, servers=None): :param list[str] servers: List of NTP servers address """ - logging.getLogger('cterasdk.edge').info("Enabling time synchronization with ntp servers.") + logger.info("Enabling time synchronization with ntp servers.") self._edge.api.put('/config/time/NTPMode', Mode.Enabled) - logging.getLogger('cterasdk.edge').info("Time synchronization enabled.") + logger.info("Time synchronization enabled.") if servers: - logging.getLogger('cterasdk.edge').info("Updating time servers. %s", {'servers': servers}) + logger.info("Updating time servers. %s", {'servers': servers}) self._edge.api.put('/config/time/NTPServer', servers) - logging.getLogger('cterasdk.edge').info("Time servers updated. %s", {'servers': servers}) + logger.info("Time servers updated. %s", {'servers': servers}) def disable(self): """ Disable NTP """ - logging.getLogger('cterasdk.edge').info("Disabling time synchronization with ntp servers.") + logger.info("Disabling time synchronization with ntp servers.") self._edge.api.put('/config/time/NTPMode', Mode.Disabled) - logging.getLogger('cterasdk.edge').info("Time synchronization disabled.") + logger.info("Time synchronization disabled.") diff --git a/cterasdk/edge/power.py b/cterasdk/edge/power.py index 926b7baa..0e14feac 100644 --- a/cterasdk/edge/power.py +++ b/cterasdk/edge/power.py @@ -5,6 +5,9 @@ from .base_command import BaseCommand +logger = logging.getLogger('cterasdk.edge') + + class Power(BaseCommand): """ Edge Filer Power APIs """ @@ -14,7 +17,7 @@ def reboot(self, wait=False): :param bool,optional wait: Wait for reboot to complete, defaults to False """ - logging.getLogger('cterasdk.edge').info("Rebooting device. %s", {'host': self._edge.host()}) + logger.info("Rebooting device. %s", {'host': self._edge.host()}) self._edge.api.execute("/status/device", "reboot", None) if wait: Boot(self._edge).wait() @@ -30,7 +33,7 @@ def reset(self, wait=False): :param bool,optional wait: Wait for reset to complete, defaults to False """ self._edge.api.execute("/status/device", "reset2default", None) - logging.getLogger('cterasdk.edge').info("Resetting device to default settings. %s", {'host': self._edge.host()}) + logger.info("Resetting device to default settings. %s", {'host': self._edge.host()}) if wait: Boot(self._edge).wait() @@ -47,22 +50,22 @@ def wait(self): while True: try: self._increment() - logging.getLogger('cterasdk.edge').debug('Checking if device is up and running. %s', {'attempt': self._attempt}) + logger.debug('Checking if device is up and running. %s', {'attempt': self._attempt}) self._edge.test() - logging.getLogger('cterasdk.edge').info("Device is back up and running.") + logger.info("Device is back up and running.") break except (CTERAException, ConnectionError, TimeoutError) as e: - logging.getLogger('cterasdk.edge').debug('Exception. %s', {'exception': e.__class__.__name__, 'message': e.message}) + logger.debug('Exception. %s', {'exception': e.__class__.__name__, 'message': e.message}) def _increment(self): self._attempt = self._attempt + 1 if self._attempt >= self._retries: self._unreachable() - logging.getLogger('cterasdk.edge').debug('Sleep. %s', {'seconds': self._seconds}) + logger.debug('Sleep. %s', {'seconds': self._seconds}) time.sleep(self._seconds) def _unreachable(self): host = self._edge.host() port = self._edge.port() - logging.getLogger('cterasdk.edge').error('Timed out. Could not reach host. %s', {'host': host, 'port': port}) + logger.error('Timed out. Could not reach host. %s', {'host': host, 'port': port}) raise ConnectionError(f'Timed out. Could not reach host {host}:{port}.') diff --git a/cterasdk/edge/ransom_protect.py b/cterasdk/edge/ransom_protect.py index e2d88f68..f9622e1a 100644 --- a/cterasdk/edge/ransom_protect.py +++ b/cterasdk/edge/ransom_protect.py @@ -4,6 +4,9 @@ from ..exceptions import CTERAException +logger = logging.getLogger('cterasdk.edge') + + class RansomProtect(BaseCommand): """ Ransomware Protect APIs @@ -19,15 +22,15 @@ def get_configuration(self): def enable(self): """Enable Ransom Protect service""" - logging.getLogger('cterasdk.edge').info('Enabling Ransom Protect.') + logger.info('Enabling Ransom Protect.') self._edge.api.put('/config/ransomProtect/enabled', True) - logging.getLogger('cterasdk.edge').info('Ransom Protect enabled.') + logger.info('Ransom Protect enabled.') def disable(self): """Enable Ransom Protect service""" - logging.getLogger('cterasdk.edge').info('Disabling Ransom Protect.') + logger.info('Disabling Ransom Protect.') self._edge.api.put('/config/ransomProtect/enabled', False) - logging.getLogger('cterasdk.edge').info('Ransom Protect disabled.') + logger.info('Ransom Protect disabled.') def is_disabled(self): """Check if Ransom Protect is disabled""" diff --git a/cterasdk/edge/remote.py b/cterasdk/edge/remote.py index f3d13fed..67b4645f 100644 --- a/cterasdk/edge/remote.py +++ b/cterasdk/edge/remote.py @@ -5,14 +5,17 @@ from ..exceptions import CTERAException +logger = logging.getLogger('cterasdk.edge') + + def remote_access(device, Portal): device_tenant = parse_base_object_ref(device.portal).name device_name = device.name - logging.getLogger('cterasdk.edge').info("Enabling remote access. %s", {'tenant': device_tenant, 'device': device_name}) + logger.info("Enabling remote access. %s", {'tenant': device_tenant, 'device': device_name}) token = authn_token(Portal, device_tenant, device_name) device_object = create_device_object(device) device_object.sso(token) - logging.getLogger('cterasdk.edge').info("Enabled remote access. %s", {'tenant': device_tenant, 'device': device_name}) + logger.info("Enabled remote access. %s", {'tenant': device_tenant, 'device': device_name}) return device_object @@ -22,10 +25,10 @@ def create_device_object(device): def authn_token(Portal, device_tenant, device_name): - logging.getLogger('cterasdk.edge').debug("Retrieving SSO Ticket. %s", {'tenant': device_tenant, 'device': device_name}) + logger.debug("Retrieving SSO Ticket. %s", {'tenant': device_tenant, 'device': device_name}) token = Portal.api.execute(f"/portals/{device_tenant}/devices/{device_name}", 'singleSignOn') if not token: - logging.getLogger('cterasdk.edge').error('Failed to Retrieve SSO Ticket. %s', {'tenant': device_tenant, 'device': device_name}) + logger.error('Failed to Retrieve SSO Ticket. %s', {'tenant': device_tenant, 'device': device_name}) raise CTERAException('Failed to Retrieve SSO Ticket.') - logging.getLogger('cterasdk.edge').debug("Retrieved SSO Ticket. %s", {'tenant': device_tenant, 'device': device_name}) + logger.debug("Retrieved SSO Ticket. %s", {'tenant': device_tenant, 'device': device_name}) return token diff --git a/cterasdk/edge/rsync.py b/cterasdk/edge/rsync.py index ab7b91a5..0e7afd3c 100644 --- a/cterasdk/edge/rsync.py +++ b/cterasdk/edge/rsync.py @@ -5,6 +5,9 @@ from .base_command import BaseCommand +logger = logging.getLogger('cterasdk.edge') + + class RSync(BaseCommand): """ Edge Filer RSync configuration """ @@ -30,9 +33,9 @@ def is_disabled(self): def _set_mode(self, enabled): """ Disable RSync """ - logging.getLogger('cterasdk.edge').info('%s RSync server.', ('Enabling' if enabled else 'Disabling')) + logger.info('%s RSync server.', ('Enabling' if enabled else 'Disabling')) self._edge.api.put('/config/fileservices/rsync/server', Mode.Enabled if enabled else Mode.Disabled) - logging.getLogger('cterasdk.edge').info('RSync server %s.', ('enabled' if enabled else 'disabled')) + logger.info('RSync server %s.', ('enabled' if enabled else 'disabled')) def modify( self, diff --git a/cterasdk/edge/services.py b/cterasdk/edge/services.py index c81192ef..5650fef4 100644 --- a/cterasdk/edge/services.py +++ b/cterasdk/edge/services.py @@ -11,6 +11,9 @@ from .types import TCPService +logger = logging.getLogger('cterasdk.edge') + + class Services(BaseCommand): """ Edge Filer Cloud Services configuration APIs """ _UNTRUSTED_CERTIFICATE_ERRORS = [ @@ -49,7 +52,7 @@ def connected(self): def _before_connect_to_services(self, ctera_license, server): Services._validate_license(ctera_license) if self._edge.network.proxy.is_enabled(): - logging.getLogger('cterasdk.edge').debug('Skipping TCP connection verification over port 995.') + logger.debug('Skipping TCP connection verification over port 995.') else: self._check_cttp_traffic(address=server) @@ -119,9 +122,9 @@ def disable_sso(self): self._set_sso(False) def _set_sso(self, sso_state): - logging.getLogger('cterasdk.edge').info('%s single sign-on from CTERA Portal.', ('Enabling' if sso_state else 'Disabling')) + logger.info('%s single sign-on from CTERA Portal.', ('Enabling' if sso_state else 'Disabling')) self._edge.api.put('/config/gui/adminRemoteAccessSSO', sso_state) - logging.getLogger('cterasdk.edge').info('Single sign-on %s.', ('enabled' if sso_state else 'disabled')) + logger.info('Single sign-on %s.', ('enabled' if sso_state else 'disabled')) def _connect_to_services(self, param, ctera_license): task = self._attach(param) @@ -131,11 +134,11 @@ def _connect_to_services(self, param, ctera_license): [enum.ServicesConnectionState.ResolvingServers, enum.ServicesConnectionState.Connecting, enum.ServicesConnectionState.Attaching, enum.ServicesConnectionState.Authenticating], [], [enum.ServicesConnectionState.Disconnected], 20, 1) - logging.getLogger('cterasdk.edge').info("Connected to Portal.") + logger.info("Connected to Portal.") except TaskError as error: description = error.task.description - logging.getLogger('cterasdk.edge').error("Connection failed. Reason: %s", description) - raise CTERAException("Connection failed", None, reason=description) + logger.error("Connection failed. Reason: %s", description) + raise CTERAException(f"Connection failed. Reason: {description}") self._edge.licenses.apply(ctera_license) @staticmethod @@ -143,13 +146,13 @@ def _validate_license(ctera_license): try: Licenses.infer(ctera_license) except InputError as error: - logging.getLogger('cterasdk.edge').error('Connection failed. Invalid license type. %s', {'license': ctera_license}) + logger.error('Connection failed. Invalid license type. %s', {'license': ctera_license}) raise error def _check_cttp_traffic(self, address, port=995): tcp_connect_result = self._edge.network.tcp_connect(TCPService(address, port)) if not tcp_connect_result.is_open: - logging.getLogger('cterasdk.edge').error("Unable to establish connection over port %s", str(tcp_connect_result.port)) + logger.error("Unable to establish connection over port %s", str(tcp_connect_result.port)) raise ConnectionError(f'Unable to establish CTTP connection {tcp_connect_result.host}:{tcp_connect_result.port}') def _check_connection(self, server): @@ -165,12 +168,12 @@ def _check_web_sso(obj): try: if obj.result.hasWebSSO: message = "Connection failed. You must activate this Edge Filer using an activation code." - logging.getLogger('cterasdk.edge').error(message) + logger.error(message) raise CTERAException(message) if not obj.result.hasWebSSO and obj.rc == "connectOK": return True - raise CTERAException("Connection failed", None, reason=obj.rc) + raise CTERAException(f"Connection failed. Reason: {obj.rc}") except AttributeError: pass @@ -181,7 +184,7 @@ def _handle_untrusted_cert(self, server, obj): if obj.rc in Services._UNTRUSTED_CERTIFICATE_ERRORS: proceed = False if cterasdk.settings.edge.syn.services.ssl == 'prompt': - logging.getLogger('cterasdk.edge').warning(msg=obj.msg) + logger.warning(msg=obj.msg) proceed = ask(f"Connect {self._edge.host()} to {server}?") if cterasdk.settings.edge.syn.services.ssl is False or proceed: self._trust_cert[server] = True @@ -191,6 +194,6 @@ def _handle_untrusted_cert(self, server, obj): return False def _attach(self, param): - logging.getLogger('cterasdk.edge').info("Connecting to Portal. %s", {'server': param.server, 'user': param.user}) + logger.info("Connecting to Portal. %s", {'server': param.server, 'user': param.user}) obj = self._edge.api.execute("/status/services", "attachAndSave", param) return obj.id diff --git a/cterasdk/edge/shares.py b/cterasdk/edge/shares.py index 3833cd6c..456105e7 100644 --- a/cterasdk/edge/shares.py +++ b/cterasdk/edge/shares.py @@ -8,6 +8,9 @@ from .types import NFSv3AccessControlEntry, RemoveNFSv3AccessControlEntry, ShareAccessControlEntry, RemoveShareAccessControlEntry +logger = logging.getLogger('cterasdk.edge') + + class Shares(BaseCommand): def get(self, name=None): @@ -83,10 +86,10 @@ def add(self, try: self._edge.api.add('/config/fileservices/share', param) - logging.getLogger('cterasdk.edge').info("Share created. %s", {'name': name}) - except Exception as error: - logging.getLogger('cterasdk.edge').error("Share creation failed.") - raise CTERAException('Share creation failed', error) + logger.info("Share created. %s", {'name': param.name}) + except CTERAException as error: + logger.error("Share creation failed: %s", param.name) + raise CTERAException(f'Share creation failed: {param.name}') from error def set_share_winacls(self, name): """ @@ -94,7 +97,7 @@ def set_share_winacls(self, name): :param str name: The share name """ - logging.getLogger('cterasdk.edge').error("Updating Windows file sharing access mode. %s", + logger.error("Updating Windows file sharing access mode. %s", {'share': name, 'access': enum.Acl.WindowsNT}) self._edge.api.put('/config/fileservices/share/' + name + '/access', enum.Acl.WindowsNT) @@ -113,7 +116,7 @@ def set_access_type(self, name, access): :param str name: The share name :param cterasdk.edge.enum.Acl access: The Windows File Sharing authentication mode """ - logging.getLogger('cterasdk.edge').info("Updating Windows file sharing access mode. %s", {'share': name, 'access': access}) + logger.info("Updating Windows file sharing access mode. %s", {'share': name, 'access': access}) self._edge.api.put('/config/fileservices/share/' + name + '/access', access) def block_files(self, name, extensions): @@ -124,9 +127,8 @@ def block_files(self, name, extensions): :param list[str] extensions: List of file extensions to block """ share = self.get(name) - if share.access != enum.Acl.WindowsNT: - raise CTERAException('Cannot block file types on non Windows-ACL enabled shares', None, share=share.name, access=share.access) - logging.getLogger('cterasdk.edge').error("Updating the list of blocked file extensions. %s", + Shares._validate_share_access(share) + logger.error("Updating the list of blocked file extensions. %s", {'share': name, 'extensions': extensions, 'access': enum.Acl.WindowsNT}) self._edge.api.put('/config/fileservices/share/' + share.name + '/screenedFileTypes', extensions) @@ -272,11 +274,11 @@ def modify( try: self._edge.api.put('/config/fileservices/share/' + name, share) - logging.getLogger('cterasdk.edge').info("Share modified. %s", {'name': name}) + logger.info("Share modified. %s", {'name': name}) except Exception as error: - msg = f'Failed to modify the share {name}' - logging.getLogger('cterasdk.edge').error(msg) - raise CTERAException(msg, error) + message = f'Could not modify share: {name}' + logger.error(message) + raise CTERAException(message) from error def delete(self, name): """ @@ -284,12 +286,13 @@ def delete(self, name): :param str name: The share name """ + ref = f'/config/fileservices/share/{name}' try: - self._edge.api.delete('/config/fileservices/share/' + name) - logging.getLogger('cterasdk.edge').info("Share deleted. %s", {'name': name}) + self._edge.api.delete(ref) + logger.info("Share deleted: %s", ref) except Exception as error: - logging.getLogger('cterasdk.edge').error("Share deletion failed.") - raise CTERAException('Share deletion failed', error) + logger.error(f"Share deletion failed: {ref}") + raise CTERAException(f'Share deletion failed: {ref}') from error def get_trusted_nfs_clients(self, name): """ @@ -374,9 +377,8 @@ def set_screened_file_types(self, name, extensions): :param list[str] extensions: List of file extensions to block """ share = self.get(name) - if share.access != enum.Acl.WindowsNT: - raise CTERAException('Cannot block file types on non Windows-ACL enabled shares', None, share=share.name, access=share.access) - logging.getLogger('cterasdk.edge').info( + Shares._validate_share_access(share) + logger.info( "Updating the list of blocked file extensions. %s", {'share': name, 'extensions': extensions, 'access': enum.Acl.WindowsNT} ) @@ -390,12 +392,11 @@ def add_screened_file_types(self, name, extensions): :param list[str] extensions: List of file extensions to add """ share = self.get(name) - if share.access != enum.Acl.WindowsNT: - raise CTERAException('Cannot block file types on non Windows-ACL enabled shares', None, share=share.name, access=share.access) + Shares._validate_share_access(share) new_list = list(set(share.screenedFileTypes + extensions)) - logging.getLogger('cterasdk.edge').info( + logger.info( "Updating the list of blocked file extensions. %s", {'share': name, 'extensions': new_list, 'access': enum.Acl.WindowsNT} ) @@ -409,12 +410,11 @@ def remove_screened_file_types(self, name, extensions): :param list[str] extensions: List of file extensions to remove """ share = self.get(name) - if share.access != enum.Acl.WindowsNT: - raise CTERAException('Cannot block file types on non Windows-ACL enabled shares', None, share=share.name, access=share.access) + Shares._validate_share_access(share) new_list = list(set(share.screenedFileTypes) - set(extensions)) - logging.getLogger('cterasdk.edge').info( + logger.info( "Updating the list of blocked file extensions. %s", {'share': name, 'extensions': new_list, 'access': enum.Acl.WindowsNT} ) @@ -426,15 +426,20 @@ def _validate_root_directory(self, name): response = self._edge.api.execute('/status/fileManager', 'listPhysicalFolders', param) for root in response: if root.fullpath == f'/{name}': - logging.getLogger('cterasdk.edge').debug("Found root directory. %s", + logger.debug("Found root directory. %s", {'name': root.name, 'type': root.type, 'fullpath': root.fullpath}) return name - logging.getLogger('cterasdk.edge').error("Could not find root directory. %s", {'name': name}) + logger.error("Could not find root directory. %s", {'name': name}) options = [root.fullpath[1:] for root in response] raise InputError('Invalid root directory.', name, options) + @staticmethod + def _validate_share_access(share): + if share.access != enum.Acl.WindowsNT: + raise CTERAException('Cannot block file types on non Windows-ACL enabled shares.') + @staticmethod def _validate_acl(acl): if not isinstance(acl, list): diff --git a/cterasdk/edge/shell.py b/cterasdk/edge/shell.py index 7d9425be..0ad9fd25 100644 --- a/cterasdk/edge/shell.py +++ b/cterasdk/edge/shell.py @@ -5,6 +5,9 @@ from .base_command import BaseCommand +logger = logging.getLogger('cterasdk.edge') + + class Shell(BaseCommand): """ Edge Filer Shell command """ @@ -16,14 +19,14 @@ def run_command(self, shell_command, wait=True): :param bool,optional wait: Wait for the command to execute, defaults to ``True`` :return: The command result, or the task url path when wait equals ``False`` """ - logging.getLogger('cterasdk.edge').info("Executing shell command. %s", {'shell_command': shell_command}) + logger.info("Executing shell command. %s", {'shell_command': shell_command}) task = self._edge.api.execute("/config/device", "bgshell", shell_command) if not wait: return task try: task = self._edge.tasks.wait(task) - logging.getLogger('cterasdk.edge').info("Shell command executed. %s", {'shell_command': shell_command}) + logger.info("Shell command executed. %s", {'shell_command': shell_command}) return task.result.result except TaskError as error: - raise CTERAException('An error occurred while executing task', error) + raise CTERAException('An error occurred while executing task') from error diff --git a/cterasdk/edge/smb.py b/cterasdk/edge/smb.py index cf6c76f8..16b671e8 100644 --- a/cterasdk/edge/smb.py +++ b/cterasdk/edge/smb.py @@ -6,26 +6,29 @@ from .base_command import BaseCommand +logger = logging.getLogger('cterasdk.edge') + + class SMB(BaseCommand): """ Edge Filer SMB configuration APIs """ def enable(self): """ Enable SMB """ - logging.getLogger('cterasdk.edge').info('Enabling SMB server.') + logger.info('Enabling SMB server.') self._edge.api.put('/config/fileservices/cifs/mode', Mode.Enabled) - logging.getLogger('cterasdk.edge').info('SMB server enabled.') + logger.info('SMB server enabled.') def enable_abe(self): """ Enable ABE """ - logging.getLogger('cterasdk.edge').info('Enabling ABE.') + logger.info('Enabling ABE.') self._edge.api.put('/config/fileservices/cifs/hideUnreadable', True) - logging.getLogger('cterasdk.edge').info('Access Based Enumeration (ABE) enabled.') + logger.info('Access Based Enumeration (ABE) enabled.') def disable_abe(self): """ Disable ABE """ - logging.getLogger('cterasdk.edge').info('Disabling ABE.') + logger.info('Disabling ABE.') self._edge.api.put('/config/fileservices/cifs/hideUnreadable', False) - logging.getLogger('cterasdk.edge').info('Access Based Enumeration (ABE) disabled.') + logger.info('Access Based Enumeration (ABE) disabled.') def set_packet_signing(self, packet_signing): """ @@ -34,19 +37,19 @@ def set_packet_signing(self, packet_signing): :param cterasdk.edge.enum.CIFSPacketSigning packet_signing: Packet signing type """ self._verify_packet_signing_parameter(packet_signing) - logging.getLogger('cterasdk.edge').info('Updating SMB packet signing configuration.') + logger.info('Updating SMB packet signing configuration.') try: self._edge.api.put('/config/fileservices/cifs/packetSigning', packet_signing) - logging.getLogger('cterasdk.edge').info('SMB packet signing configuration updated. %s', {'packet_signing': packet_signing}) + logger.info('SMB packet signing configuration updated: %s', packet_signing) except CTERAException as error: - logging.getLogger('cterasdk.edge').error('Failed to update SMB packet signing configuration.') - raise CTERAException('Invalid packet signing co', error) + logger.error(f'Invalid SMB packet signing configuration: {packet_signing}') + raise CTERAException(f'Invalid packet SMB signing configuration: {packet_signing}') from error def disable(self): """ Disable SMB """ - logging.getLogger('cterasdk.edge').info('Disabling SMB server.') + logger.info('Disabling SMB server.') self._edge.api.put('/config/fileservices/cifs/mode', Mode.Disabled) - logging.getLogger('cterasdk.edge').info('SMB server disabled.') + logger.info('SMB server disabled.') def restart(self): self.disable() @@ -125,11 +128,11 @@ def modify( cifs.maxServerProtocol = max_server_protocol try: self._edge.api.put('/config/fileservices/cifs', cifs) - logging.getLogger('cterasdk.edge').info('SMB configuration updated.') + logger.info('SMB configuration updated.') except CTERAException as error: - msg = 'Failed to update SMB configuration.' - logging.getLogger('cterasdk.edge').error(msg) - raise CTERAException(message=msg, instace=error) + message = "An error occurred while trying to modify the SMB server's configuration." + logger.error(message) + raise CTERAException(message) from error @staticmethod def _verify_smb_protocol_version_parameter(smb_protocol_version): diff --git a/cterasdk/edge/snmp.py b/cterasdk/edge/snmp.py index f7bc5991..efdec5fb 100644 --- a/cterasdk/edge/snmp.py +++ b/cterasdk/edge/snmp.py @@ -6,6 +6,9 @@ from .base_command import BaseCommand +logger = logging.getLogger('cterasdk.edge') + + class SNMP(BaseCommand): """ Edge Filer SNMP Configuration APIs """ @@ -39,15 +42,15 @@ def enable(self, port=161, community_str=None, username=None, auth_password=None param.snmpV3.authenticationPassword = auth_password param.snmpV3.privacyPassword = privacy_password - logging.getLogger('cterasdk.edge').info("Enabling SNMP.") + logger.info("Enabling SNMP.") self._edge.api.put('/config/snmp', param) - logging.getLogger('cterasdk.edge').info("Enabled SNMP.") + logger.info("Enabled SNMP.") def disable(self): """ Disable SNMP """ - logging.getLogger('cterasdk.edge').info("Disabling SNMP.") + logger.info("Disabling SNMP.") self._edge.api.put('/config/snmp/mode', enum.Mode.Disabled) - logging.getLogger('cterasdk.edge').info("Disabled SNMP.") + logger.info("Disabled SNMP.") def get_configuration(self): return self._edge.api.get('/config/snmp') @@ -76,6 +79,6 @@ def modify(self, port=None, community_str=None, username=None, auth_password=Non current_config.snmpV3.authenticationPassword = auth_password current_config.snmpV3.privacyPassword = privacy_password - logging.getLogger('cterasdk.edge').info("Updating SNMP configuration.") + logger.info("Updating SNMP configuration.") self._edge.api.put('/config/snmp', current_config) - logging.getLogger('cterasdk.edge').info("SNMP configured.") + logger.info("SNMP configured.") diff --git a/cterasdk/edge/ssh.py b/cterasdk/edge/ssh.py index bb66990a..8b2d8787 100644 --- a/cterasdk/edge/ssh.py +++ b/cterasdk/edge/ssh.py @@ -5,6 +5,9 @@ from .base_command import BaseCommand +logger = logging.getLogger('cterasdk.edge') + + class SSH(BaseCommand): """ Edge Filer SSH daemon APIs """ @@ -31,11 +34,11 @@ def enable(self, public_key=None, public_key_file=None, exponent=65537, key_size param.publicKey = public_key - logging.getLogger('cterasdk.edge').info("Enabling SSH daemon.") + logger.info("Enabling SSH daemon.") self._edge.api.execute('/config/device', 'startSSHD', param) - logging.getLogger('cterasdk.edge').info("SSH daemon enabled.") + logger.info("SSH daemon enabled.") def disable(self): - logging.getLogger('cterasdk.edge').info("Disabling SSH daemon.") + logger.info("Disabling SSH daemon.") self._edge.api.execute('/config/device', 'stopSSHD') - logging.getLogger('cterasdk.edge').info("SSH daemon disabled.") + logger.info("SSH daemon disabled.") diff --git a/cterasdk/edge/ssl.py b/cterasdk/edge/ssl.py index 88c289b6..33fe2662 100644 --- a/cterasdk/edge/ssl.py +++ b/cterasdk/edge/ssl.py @@ -5,6 +5,9 @@ from ..common import Object +logger = logging.getLogger('cterasdk.edge') + + def initialize(edge): """ Conditional intialization of the Edge Filer SSL Module. @@ -52,9 +55,9 @@ def _import_certificate(self, path, private_key, *certificates): certificates = [X509Certificate.load_certificate(certificate) for certificate in certificates] certificate_chain = [certificate.pem_data.decode('utf-8') for certificate in create_certificate_chain(*certificates)] server_certificate = ''.join([key_object.pem_data.decode('utf-8')] + certificate_chain) - logging.getLogger('cterasdk.edge').info("Uploading Web Server SSL certificate.") + logger.info("Uploading Web Server SSL certificate.") response = self._edge.api.put(path, f"\n{server_certificate}") - logging.getLogger('cterasdk.edge').info("Uploaded Web Server SSL certificate.") + logger.info("Uploaded Web Server SSL certificate.") return response @@ -79,13 +82,13 @@ def import_storage_ca(self, certificate): :param str certificate: The PEM-encoded certificate or a path to the PEM-encoded server certificate file """ - logging.getLogger('cterasdk.edge').info('Setting trusted object storage CA certificate') + logger.info('Setting trusted object storage CA certificate') param = Object() param._classname = 'ExtTrustedCA' # pylint: disable=protected-access param.certificate = X509Certificate.load_certificate(certificate).pem_data.decode('utf-8') - logging.getLogger('cterasdk.edge').info("Uploading object storage certificate.") + logger.info("Uploading object storage certificate.") response = self._edge.api.put('/config/extStorageTrustedCA', param) - logging.getLogger('cterasdk.edge').info("Uploaded object storage certificate.") + logger.info("Uploaded object storage certificate.") return response def import_certificate(self, private_key, *certificates): @@ -120,9 +123,9 @@ def regenerate(self): """ Generate a Self Signed Certificate. """ - logging.getLogger('cterasdk.edge').info("Generating a Self Signed Certificate.") + logger.info("Generating a Self Signed Certificate.") response = self._edge.api.execute('/config/certificates', 'createSelfSign') - logging.getLogger('cterasdk.edge').info("Generated a Self Signed Certificate.") + logger.info("Generated a Self Signed Certificate.") return response def import_certificate(self, private_key, *certificates): @@ -167,9 +170,9 @@ def remove(self, ca): fingerprint = ca if fingerprint: - logging.getLogger('cterasdk.edge').info("Removing Trusted CA. %s", {'fingerprint': fingerprint}) + logger.info("Removing Trusted CA. %s", {'fingerprint': fingerprint}) response = self._edge.api.delete(f'/config/certificates/trustedCACertificates/{fingerprint}') - logging.getLogger('cterasdk.edge').info("Removed Trusted CA. %s", {'fingerprint': fingerprint}) + logger.info("Removed Trusted CA. %s", {'fingerprint': fingerprint}) return response raise ValueError('Could not identify CA fingerprint.') @@ -178,6 +181,6 @@ def clear(self): """ Remove all Trusted CAs. """ - logging.getLogger('cterasdk.edge').info("Removing all Trusted CAs.") + logger.info("Removing all Trusted CAs.") for ca in self.all(): self.remove(ca) diff --git a/cterasdk/edge/support.py b/cterasdk/edge/support.py index 9b552be3..2d3b677d 100644 --- a/cterasdk/edge/support.py +++ b/cterasdk/edge/support.py @@ -6,6 +6,9 @@ from .base_command import BaseCommand +logger = logging.getLogger('cterasdk.edge') + + class DebugLevel: none = "none" error = "error" @@ -59,8 +62,8 @@ def set_debug_level(self, *levels): def get_support_report(self): """ Download support report """ filename = 'Support-' + self._edge.host() + datetime.now().strftime('_%Y-%m-%dT%H_%M_%S') + '.zip' - logging.getLogger('cterasdk.edge').info('Downloading support report. %s', {'host': self._edge.host()}) + logger.info('Downloading support report. %s', {'host': self._edge.host()}) handle = self._edge.api.handle('/supportreport') filepath = synfs.write(commonfs.downloads(), filename, handle) - logging.getLogger('cterasdk.edge').info('Support report downloaded. %s', {'filepath': filepath}) + logger.info('Support report downloaded. %s', {'filepath': filepath}) return filepath diff --git a/cterasdk/edge/sync.py b/cterasdk/edge/sync.py index 75416a24..67f4609a 100644 --- a/cterasdk/edge/sync.py +++ b/cterasdk/edge/sync.py @@ -7,6 +7,9 @@ from ..common import Object, ThrottlingRule, FilterBackupSet, FileFilterBuilder +logger = logging.getLogger('cterasdk.edge') + + class Sync(BaseCommand): """ Edge Filer Cloud Sync APIs @@ -36,7 +39,7 @@ def suspend(self, wait=True): :param bool wait: Wait for synchronization to stop """ - logging.getLogger('cterasdk.edge').info("Suspending cloud sync.") + logger.info("Suspending cloud sync.") self._edge.api.put('/config/cloudsync/mode', Mode.Disabled) if wait: self._track_status( @@ -64,11 +67,11 @@ def suspend(self, wait=True): SyncStatus.NoFolder ] ) - logging.getLogger('cterasdk.edge').info("Cloud sync suspended.") + logger.info("Cloud sync suspended.") def unsuspend(self): """ Unsuspend Cloud Sync """ - logging.getLogger('cterasdk.edge').info("Unsuspending cloud sync.") + logger.info("Unsuspending cloud sync.") self._edge.api.put('/config/cloudsync/mode', Mode.Enabled) try: self._track_status( @@ -96,23 +99,23 @@ def unsuspend(self): SyncStatus.NoFolder ] ) - logging.getLogger('cterasdk.edge').info('Unsuspended cloud sync.') + logger.info('Unsuspended cloud sync.') except ErrorStatus as error: if error.status == SyncStatus.ShouldSupportWinNtAcl: - logging.getLogger('cterasdk.edge').warning('Windows ACL enabled folder cannot be synchronized to this device.') + logger.warning('Windows ACL enabled folder cannot be synchronized to this device.') self._edge.api.put('/config/fileservices/share/cloud/access', Acl.WindowsNT) - logging.getLogger('cterasdk.edge').info('Updated network share access. %s', {'share': 'cloud', 'access': Acl.WindowsNT}) + logger.info('Updated network share access. %s', {'share': 'cloud', 'access': Acl.WindowsNT}) else: - logging.getLogger('cterasdk.edge').error("An error occurred while unsuspendeding sync. %s", {'status': error.status}) + logger.error("An error occurred while unsuspendeding sync. %s", {'status': error.status}) def _track_status(self, success, progress, transient, failure): track(self._edge, '/proc/cloudsync/serviceStatus/id', success, progress, transient, failure) def refresh(self): """ Refresh Cloud Folders """ - logging.getLogger('cterasdk.edge').info("Refreshing cloud folders.") + logger.info("Refreshing cloud folders.") self._edge.api.execute("/config/cloudsync/cloudExtender", "refreshPaths", None) - logging.getLogger('cterasdk.edge').info("Completed refreshing cloud folders.") + logger.info("Completed refreshing cloud folders.") def exclude_files(self, extensions=None, filenames=None, paths=None, custom_exclusion_rules=None): """ @@ -141,24 +144,24 @@ def exclude_files(self, extensions=None, filenames=None, paths=None, custom_excl rules.extend(custom_exclusion_rules) if rules: - logging.getLogger('cterasdk.edge').info('Setting sync exclusion rules') + logger.info('Setting sync exclusion rules') self._edge.api.put('/config/cloudsync/excludeFiles', rules) - logging.getLogger('cterasdk.edge').info('Sync exclusion rules set') + logger.info('Sync exclusion rules set') def remove_file_exclusion_rules(self): """ Remove previously configured sync exclusion rules """ - logging.getLogger('cterasdk.edge').info('Removing sync exclusion rules') + logger.info('Removing sync exclusion rules') self._edge.api.put('/config/cloudsync/excludeFiles', None) - logging.getLogger('cterasdk.edge').info('Sync exclusion rules removed') + logger.info('Sync exclusion rules removed') def get_linux_avoid_using_fanotify(self): - logging.getLogger('cterasdk.edge').info('Getting LinuxAvoidUsingFAnotify') + logger.info('Getting LinuxAvoidUsingFAnotify') return self._edge.api.get('/config/cloudsync/LinuxAvoidUsingFAnotify') def set_linux_avoid_using_fanotify(self, avoid): - logging.getLogger('cterasdk.edge').info('Setting LinuxAvoidUsingFAnotify to %s', avoid) + logger.info('Setting LinuxAvoidUsingFAnotify to %s', avoid) self._edge.api.put('/config/cloudsync/LinuxAvoidUsingFAnotify', avoid) def evict(self, path, wait=False): diff --git a/cterasdk/edge/syslog.py b/cterasdk/edge/syslog.py index b4e735df..ae34e5a1 100644 --- a/cterasdk/edge/syslog.py +++ b/cterasdk/edge/syslog.py @@ -6,6 +6,9 @@ from .base_command import BaseCommand +logger = logging.getLogger('cterasdk.edge') + + class Syslog(BaseCommand): """ Edge Filer Syslog configuration APIs """ @@ -27,18 +30,18 @@ def enable(self, server, port=514, proto=enum.IPProtocol.UDP, min_severity=enum. obj.proto = proto obj.minSeverity = min_severity - logging.getLogger('cterasdk.edge').info("Configuring syslog server.") + logger.info("Configuring syslog server.") self._edge.api.put('/config/logging/syslog', obj) - logging.getLogger('cterasdk.edge').info( + logger.info( "Syslog server configured. %s", {'server': server, 'port': port, 'protocol': proto, 'minSeverity': min_severity} ) def disable(self): """ Disable Syslog """ - logging.getLogger('cterasdk.edge').info("Disabling syslog server.") + logger.info("Disabling syslog server.") self._edge.api.put('/config/logging/syslog/mode', enum.Mode.Disabled) - logging.getLogger('cterasdk.edge').info("Syslog server disabled.") + logger.info("Syslog server disabled.") def get_configuration(self): return self._edge.api.get('/config/logging/syslog') @@ -64,9 +67,9 @@ def modify(self, server=None, port=None, proto=None, min_severity=None): if min_severity: current_config.minSeverity = min_severity - logging.getLogger('cterasdk.edge').info("Updating syslog server configuration.") + logger.info("Updating syslog server configuration.") self._edge.api.put('/config/logging/syslog', current_config) - logging.getLogger('cterasdk.edge').info( + logger.info( "Syslog server configured. %s", { 'server': current_config.server, diff --git a/cterasdk/edge/taskmgr.py b/cterasdk/edge/taskmgr.py index 4e1613f0..689f3bc5 100644 --- a/cterasdk/edge/taskmgr.py +++ b/cterasdk/edge/taskmgr.py @@ -8,6 +8,9 @@ from .base_command import BaseCommand +logger = logging.getLogger('cterasdk.edge') + + class Task(TaskBase): def _get_task_id(self, ref): @@ -21,7 +24,7 @@ def _get_task_id(self, ref): uid = ref[start: end] if uid is not None: return '/proc/bgtasks/' + uid - logging.getLogger('cterasdk.edge').error('Could not parse task id. %s', {'ref': ref}) + logger.error('Could not parse task id. %s', {'ref': ref}) raise InputError('Invalid task id', ref, [64, '64', '/proc/bgtasks/64']) def get_task_status(self): diff --git a/cterasdk/edge/telnet.py b/cterasdk/edge/telnet.py index c2346771..f9694d8f 100644 --- a/cterasdk/edge/telnet.py +++ b/cterasdk/edge/telnet.py @@ -5,6 +5,9 @@ from .base_command import BaseCommand +logger = logging.getLogger('cterasdk.edge') + + class Telnet(BaseCommand): """ Edge Filer Telnet configuration APIs """ @@ -13,19 +16,19 @@ def enable(self, code): param = Object() param.code = code - logging.getLogger('cterasdk.edge').info("Enabling telnet access.") + logger.info("Enabling telnet access.") response = self._edge.api.execute("/config/device", "startTelnetd", param) if response == 'telnetd already running': - logging.getLogger('cterasdk.edge').info("Telnet access already enabled.") + logger.info("Telnet already enabled.") elif response != "OK": - logging.getLogger('cterasdk.edge').error("Failed enabling telnet access. %s", {'reason': response}) - raise CTERAException("Failed enabling telnet access", None, reason=response) + logger.error("Failed enabling telnet access. %s", {'reason': response}) + raise CTERAException(f"Failed to enable telnet. Reason: {response}") else: - logging.getLogger('cterasdk.edge').info("Telnet access enabled.") + logger.info("Telnet enabled.") def disable(self): """ Disable Telnet """ - logging.getLogger('cterasdk.edge').info("Disabling telnet access.") + logger.info("Disabling telnet access.") self._edge.api.execute("/config/device", "stopTelnetd") - logging.getLogger('cterasdk.edge').info("Telnet access disabled.") + logger.info("Telnet disabled.") diff --git a/cterasdk/edge/timezone.py b/cterasdk/edge/timezone.py index 801b2c5d..7cceb419 100644 --- a/cterasdk/edge/timezone.py +++ b/cterasdk/edge/timezone.py @@ -2,6 +2,9 @@ from .base_command import BaseCommand +logger = logging.getLogger('cterasdk.edge') + + class Timezone(BaseCommand): """ Edge Filer Timezone configuration """ @@ -19,8 +22,8 @@ def set_timezone(self, timezone): :param str timezone: New timezone to set """ - logging.getLogger('cterasdk.edge').info("Updating timezone. %s", {'timezone': timezone}) + logger.info("Updating timezone. %s", {'timezone': timezone}) self._edge.api.put('/config/time/TimeZone', timezone) - logging.getLogger('cterasdk.edge').info("Timezone updated. %s", {'timezone': timezone}) + logger.info("Timezone updated. %s", {'timezone': timezone}) diff --git a/cterasdk/edge/users.py b/cterasdk/edge/users.py index 85fb6fe3..18e49ec7 100644 --- a/cterasdk/edge/users.py +++ b/cterasdk/edge/users.py @@ -5,6 +5,9 @@ from .base_command import BaseCommand +logger = logging.getLogger('cterasdk.edge') + + class Users(BaseCommand): """ Edge Filer Users configuration APIs """ @@ -30,9 +33,9 @@ def add_first_user(self, username, password, email=''): user.password = password user.email = email self._edge.api.post('/nosession/createfirstuser', user) - logging.getLogger('cterasdk.edge').info('User created. %s', {'user': username}) + logger.info('User created: %s', username) else: - logging.getLogger('cterasdk.edge').info('Skipping. root account already exists.') + logger.info('User already exists.') self._edge.login(username, password) def add(self, username, password, full_name=None, email=None, uid=None): @@ -53,11 +56,11 @@ def add(self, username, password, full_name=None, email=None, uid=None): user.uid = uid try: response = self._edge.api.add('/config/auth/users', user) - logging.getLogger('cterasdk.edge').info("User created. %s", {'username': user.username}) + logger.info("User created: %s", user.username) return response except CTERAException as error: - logging.getLogger('cterasdk.edge').error("User creation failed.") - raise CTERAException('User creation failed', error) + logger.error("User creation failed: %s", username) + raise CTERAException(f'User creation failed: {username}') from error def modify(self, username, password=None, full_name=None, email=None, uid=None): """ @@ -69,10 +72,11 @@ def modify(self, username, password=None, full_name=None, email=None, uid=None): :param str,optional email: E-mail address of the user, defaults to None :param str,optional uid: The uid of the user, defaults to None """ + ref = f'/config/auth/users/{username}' try: - user = self._edge.api.get('/config/auth/users/' + username) + user = self._edge.api.get(ref) except CTERAException as error: - raise CTERAException('Failed to get the user', error) + raise CTERAException(f'User not found: {ref}') from error if password: user.password = password @@ -83,12 +87,12 @@ def modify(self, username, password=None, full_name=None, email=None, uid=None): if uid: user.uid = uid try: - response = self._edge.api.put('/config/auth/users/' + username, user) - logging.getLogger('cterasdk.edge').info("User modified. %s", {'username': user.username}) + response = self._edge.api.put(ref, user) + logger.info("User modified: %s", ref) return response except CTERAException as error: - logging.getLogger('cterasdk.edge').error("Failed to modify user.") - raise CTERAException('Failed to modify user', error) + logger.error('User modification failed: %s', ref) + raise CTERAException(f'User modification failed: {ref}') from error def delete(self, username): """ @@ -96,10 +100,11 @@ def delete(self, username): :param str username: User name of the user to delete """ + ref = f'/config/auth/users/{username}' try: - response = self._edge.api.delete('/config/auth/users/' + username) - logging.getLogger('cterasdk.edge').info("User deleted. %s", {'username': username}) + response = self._edge.api.delete(ref) + logger.info("User deleted: %s", ref) return response except Exception as error: - logging.getLogger('cterasdk.edge').error("User deletion failed.") - raise CTERAException('User deletion failed', error) + logger.error("User deletion failed: %s", ref) + raise CTERAException(f'User deletion failed: {ref}') from error diff --git a/cterasdk/edge/volumes.py b/cterasdk/edge/volumes.py index f13d5001..4c036923 100644 --- a/cterasdk/edge/volumes.py +++ b/cterasdk/edge/volumes.py @@ -8,6 +8,9 @@ from .base_command import BaseCommand +logger = logging.getLogger('cterasdk.edge') + + class Volumes(BaseCommand): """ Edge Filer Volumes configuration APIs """ @@ -45,12 +48,12 @@ def add(self, name, size=None, filesystem='xfs', device=None, passphrase=None): param.encrypted = True param.encPassphrase = passphrase - logging.getLogger('cterasdk.edge').info( + logger.info( 'Creating volume. %s', {'name': name, 'device': device_name, 'size': size, 'filesystem': filesystem, 'passphrase': passphrase} ) - ref = '/status/storage/volumes/' + param.name + '/status' + ref = f'/status/storage/volumes/{param.name}/status' response = self._edge.api.add('/config/storage/volumes', param) @@ -62,7 +65,7 @@ def add(self, name, size=None, filesystem='xfs', device=None, passphrase=None): [VolumeStatus.Mounting, VolumeStatus.Checking, VolumeStatus.Repairing], [VolumeStatus.Corrupted, VolumeStatus.Unknown] ) - logging.getLogger('cterasdk.edge').info('Volume created. %s', {'name': name, 'size': size, 'status': status}) + logger.info('Volume created. %s', {'name': name, 'size': size, 'status': status}) return response @@ -79,17 +82,17 @@ def modify(self, name, size=None): try: volume = self.get(name) except CTERAException as error: - raise CTERAException('Failed to get the volume', error) + raise CTERAException(f'Volume not found: {name}') from error volume.size = size + ref = f'/config/storage/volumes/{name}' try: - response = self._edge.api.put('/config/storage/volumes/' + name, volume) - logging.getLogger('cterasdk.edge').info("Volume modified. %s", {'volume': volume.name}) + response = self._edge.api.put(ref, volume) + logger.info("Volume modified: %s", ref) return response except CTERAException as error: - msg = "Failed to modify volume." - logging.getLogger('cterasdk.edge').error(msg) - raise CTERAException(msg, error) + logger.error("Volume modification failed: %s", ref) + raise CTERAException(f"Volume modification failed: {ref}") from error def delete(self, name): """ @@ -99,16 +102,14 @@ def delete(self, name): """ self._wait_pending_mount(name) try: - logging.getLogger('cterasdk.edge').info('Deleting volume. %s', {'name': name}) - - response = self._edge.api.delete('/config/storage/volumes/' + name) - - logging.getLogger('cterasdk.edge').info("Volume deleted. %s", {'name': name}) - + ref = f'/config/storage/volumes/{name}' + logger.info('Deleting volume: %s', ref) + response = self._edge.api.delete(ref) + logger.info("Volume deleted: %s", ref) return response except CTERAException as error: - logging.getLogger('cterasdk.edge').error("Volume deletion failed. %s", {'name': name}) - raise CTERAException('Volume deletion failed', error) + logger.error("Volume deletion failed: %s", ref) + raise CTERAException(f'Volume deletion failed: {ref}') from error def delete_all(self): """ Delete all volumes """ @@ -120,7 +121,7 @@ def delete_all(self): for volume in volumes: self.delete(volume.name) else: - logging.getLogger('cterasdk.edge').info('No volumes found.') + logger.info('No volumes found.') def _devices(self): arrays = self._edge.api.get('/status/storage/arrays') @@ -134,7 +135,7 @@ def _devices(self): ctera_devices[drive.name] = drive.availableCapacity if len(ctera_devices) == 0: - logging.getLogger('cterasdk.edge').error('Could not find any drives or arrays.') + logger.error('Could not find any drives or arrays.') raise CTERAException('Could not find any drives or arrays') @@ -145,39 +146,39 @@ def _device_volume(device_name, ctera_devices): if device_name is not None: device_size = ctera_devices.get(device_name) if device_size is not None: - logging.getLogger('cterasdk.edge').debug('Found drive. %s', {'name': device_name, 'size': device_size}) + logger.debug('Found drive. %s', {'name': device_name, 'size': device_size}) return (device_name, device_size) device_names = [k for k, v in ctera_devices.items()] - logging.getLogger('cterasdk.edge').error('Invalid device name. %s', {'name': device_name}) + logger.error('Invalid device name. %s', {'name': device_name}) raise InputError('Invalid device name', device_name, device_names) if len(ctera_devices) == 1: device_name, device_size = ctera_devices.popitem() - logging.getLogger('cterasdk.edge').debug('Found drive. %s', {'name': device_name, 'size': device_size}) + logger.debug('Found drive. %s', {'name': device_name, 'size': device_size}) return device_name, device_size device_names = [k for k, v in ctera_devices.items()] - logging.getLogger('cterasdk.edge').error('You must specify a drive or an array name. %s', {'options': device_names}) - raise CTERAException('You must specify a drive or an array name', None, options=device_names) + logger.error('You must specify a drive or an array name. %s', {'options': device_names}) + raise InputError('You must specify a drive or an array name', options=device_names) @staticmethod def _volume_size(size, device_name, device_size): if size is not None: if size > device_size: - logging.getLogger('cterasdk.edge').error('You cannot exceed the available storage capacity. %s', + logger.error('You cannot exceed the available storage capacity. %s', {'size': size, 'free_size': device_size}) raise InputError("You cannot exceed the available storage capacity", size, device_size) return size if device_size > 0: - logging.getLogger('cterasdk.edge').info('You did not specify a volume size.') - logging.getLogger('cterasdk.edge').info('Allocating available storage capacity. %s', + logger.info('You did not specify a volume size.') + logger.info('Allocating available storage capacity. %s', {'name': device_name, 'free_size': device_size}) return device_size - logging.getLogger('cterasdk.edge').error('Insufficient storage space. %s', {'name': device_name}) - raise CTERAException('Insufficient storage space', None, device=device_name, free_size=device_size) + logger.error('Insufficient storage space. %s', {'name': device_name}) + raise CTERAException(f'Insufficient storage space (freespace={device_size}) on device: {device_name}') @staticmethod def _volume_filesystem(filesystem): @@ -187,7 +188,7 @@ def _volume_filesystem(filesystem): raise InputError('Invalid file system type', filesystem, ['xfs', 'next3', 'ext3']) def _wait_pending_filesystem_mounts(self): - logging.getLogger('cterasdk.edge').debug('Checking for pending mount tasks.') + logger.debug('Checking for pending mount tasks.') tasks = self._edge.tasks.running() for mount in tasks: @@ -195,7 +196,7 @@ def _wait_pending_filesystem_mounts(self): self._wait_mount(mount.id) def _wait_pending_mount(self, volume): - logging.getLogger('cterasdk.edge').debug('Checking for pending mount tasks. %s', {'volume': volume}) + logger.debug('Checking for pending mount tasks. %s', {'volume': volume}) tasks = self._edge.tasks.by_name(' '.join(['Mounting', volume, 'file system'])) for mount in tasks: @@ -205,4 +206,4 @@ def _wait_mount(self, tid): try: self._edge.tasks.wait(tid) except TaskError: - logging.getLogger('cterasdk.edge').debug('Failed mounting volume. %s', {'tid': tid}) + logger.debug('Failed mounting volume. %s', {'tid': tid}) diff --git a/cterasdk/exceptions.py b/cterasdk/exceptions.py deleted file mode 100644 index 87a53229..00000000 --- a/cterasdk/exceptions.py +++ /dev/null @@ -1,91 +0,0 @@ -from .common import Object -from .convert import tojsonstr - - -class CTERAException(Exception): - - def __init__(self, message=None, instance=None, **kwargs): - super().__init__() - self.classname = self.__class__.__name__ - self.join(instance) - if message is not None: - self.message = message - self.put(**kwargs) - - def put(self, **kwargs): - for key, value in kwargs.items(): - setattr(self, key, value) - - def join(self, instance=None): - if instance is not None: - self.__dict__ = instance.__dict__.copy() - - def __str__(self): - return tojsonstr(self) - - -class ContextError(CTERAException): - """API Invocation Context Error""" - - -class SessionExpired(CTERAException): - """Raised on Session Expiration""" - - def __init__(self): - super().__init__('Session expired.') - - -class NotLoggedIn(CTERAException): - """Raised on No Session""" - - def __init__(self): - super().__init__('Not logged in.') - - -class HTTPError(CTERAException): - """ - HTTP Error - - :ivar int code: Status code - :ivar str name: Reason - :ivar cterasdk.clients.errors.Error error: Error object - """ - def __init__(self, status, error): - super().__init__(error.request.url) - self.code = status.value - self.name = status.name - self.error = error - - -class NotificationsError(CTERAException): - - def __init__(self, cloudfolders, cursor): - super().__init__('An error occurred while trying to retrieve notifications.', cloudfolders=cloudfolders, cursor=cursor) - - -class ObjectNotFoundException(CTERAException): - - def __init__(self, message, object_ref, **kwargs): - super().__init__(message, None, **kwargs) - self.response = Object() - self.response.code = 404 - self.response.reason = 'Not Found' - self.response.body = Object() - self.response.body.rc = 1 - self.response.body.msg = f"Object '{object_ref}' not found" - - -class PythonVersionException(CTERAException): - - def __init__(self, version): - super().__init__('You are running an unsupported version of Python', None, version=version) - - -class InputError(CTERAException): - - def __init__(self, message, expression, options): - CTERAException.__init__(self, message, None, expression=expression, options=options) - - -class ConsentException(CTERAException): - """Consent Exception""" diff --git a/cterasdk/exceptions/__init__.py b/cterasdk/exceptions/__init__.py new file mode 100644 index 00000000..604e7847 --- /dev/null +++ b/cterasdk/exceptions/__init__.py @@ -0,0 +1,53 @@ +from ..convert import tojsonstr + + +class CTERAException(Exception): + """ + Base Exception. + + :parm str message: Error message + """ + def __init__(self, message=None): + super().__init__(message) + + def __repr__(self): + return str(self) + + +class ObjectNotFoundException(CTERAException): + """ + Object not found. + + :param str urn: Resource + """ + + def __init__(self, urn): + super().__init__(f'Object not found: {urn}') + + +class InputError(ValueError): + """ + Input Error + + :param str message: Error message + :param object expression: Error expression + :param object options: Options + """ + + def __init__(self, message, expression=None, options=None): + super().__init__(self, message) + self.expression = expression + self.options = options + + +class UserConsentError(CTERAException): + """Console""" + + +from . import ( + direct, + io, + notifications, + session, + transport +) \ No newline at end of file diff --git a/cterasdk/direct/exceptions.py b/cterasdk/exceptions/direct.py similarity index 100% rename from cterasdk/direct/exceptions.py rename to cterasdk/exceptions/direct.py index c2c5f1ef..be186af8 100644 --- a/cterasdk/direct/exceptions.py +++ b/cterasdk/exceptions/direct.py @@ -66,27 +66,6 @@ def __init__(self, filename): super().__init__(errno.EIO, 'Failed to decrypt secret key', filename) -class BlockInfo: - """ - Block Info - - :ivar int file_id: File ID - :ivar int number: Block number - :ivar int offset: Block offset - :ivar int length: Block length - """ - def __init__(self, file_id, chunk): - """ - Initialize an Block Info Object for Direct IO Error Object. - - :param cterasdk.direct.types.Chunk chunk: Chunk. - """ - self.file_id = file_id - self.number = chunk.index - self.offset = chunk.offset - self.length = chunk.length - - class BlockError(DirectIOError): """ Direct IO Block Error @@ -132,3 +111,24 @@ class BlockValidationException(BlockError): def __init__(self, file_id, chunk): super().__init__(errno.EIO, 'Expected block length does not match decrypted and decompressed block length', file_id, chunk) + + +class BlockInfo: + """ + Block Info + + :ivar int file_id: File ID + :ivar int number: Block number + :ivar int offset: Block offset + :ivar int length: Block length + """ + def __init__(self, file_id, chunk): + """ + Initialize an Block Info Object for Direct IO Error Object. + + :param cterasdk.direct.types.Chunk chunk: Chunk. + """ + self.file_id = file_id + self.number = chunk.index + self.offset = chunk.offset + self.length = chunk.length diff --git a/cterasdk/cio/exceptions.py b/cterasdk/exceptions/io.py similarity index 80% rename from cterasdk/cio/exceptions.py rename to cterasdk/exceptions/io.py index 04e4ab56..523a468e 100644 --- a/cterasdk/cio/exceptions.py +++ b/cterasdk/exceptions/io.py @@ -1,14 +1,21 @@ -from ..exceptions import CTERAException +from . import CTERAException class RemoteStorageException(CTERAException): - """Base Exception for Remote File Storage""" + """ + Base Exception for Remote File Storage + + :ivar str path: Path + """ + def __init__(self, message, path): + super().__init__(message) + self.path = path class ResourceNotFoundError(RemoteStorageException): def __init__(self, path): - super().__init__('Remote directory not found. Please verify the path and try again.', None, path=path) + super().__init__('Remote directory not found. Please verify the path and try again.', path) class ResourceExistsError(CTERAException): diff --git a/cterasdk/exceptions/notifications.py b/cterasdk/exceptions/notifications.py new file mode 100644 index 00000000..7d8734da --- /dev/null +++ b/cterasdk/exceptions/notifications.py @@ -0,0 +1,15 @@ +from . import CTERAException + + +class NotificationsError(CTERAException): + """ + Notifications error + + :ivar list[int] cloudfolders: List of cloudfolders + :ivar str cursor: Cursor + """ + + def __init__(self, cloudfolders, cursor): + super().__init__('An error occurred while trying to retrieve notifications.') + self.cloudfolders = cloudfolders + self.cursor = cursor \ No newline at end of file diff --git a/cterasdk/exceptions/session.py b/cterasdk/exceptions/session.py new file mode 100644 index 00000000..bfffaa12 --- /dev/null +++ b/cterasdk/exceptions/session.py @@ -0,0 +1,22 @@ +from . import CTERAException + + +class SessionExpired(CTERAException): + """Session expiration""" + + def __init__(self): + super().__init__('Authentication error: Session expired.') + + +class NotLoggedIn(CTERAException): + """No session""" + + def __init__(self): + super().__init__('Authentication error: Not logged in.') + + +class ContextError(CTERAException): + """API invocation context rrror""" + + def __init__(self, message): + super().__init__(f'Context error: {message}.') \ No newline at end of file diff --git a/cterasdk/exceptions/transport.py b/cterasdk/exceptions/transport.py new file mode 100644 index 00000000..9d938609 --- /dev/null +++ b/cterasdk/exceptions/transport.py @@ -0,0 +1,135 @@ +from http import HTTPStatus +from . import CTERAException + + +class HTTPError(CTERAException): + """ + HTTP Error + + :ivar int code: Status code + :ivar str name: Reason + :ivar cterasdk.clients.errors.Error error: Error object + """ + + def __init__(self, status, error): + super().__init__(error.request.url) + self.code = status.value + self.name = status.name + self.error = error + + +class BadRequest(HTTPError): + """ + Bad Request + + :ivar int code: Status code + :ivar str name: Reason + :ivar cterasdk.clients.errors.Error error: Error object + """ + + def __init__(self, error): + super().__init__(HTTPStatus.BAD_REQUEST, error) + + +class Unauthorized(HTTPError): + """ + Unauthorized + + :ivar int code: Status code + :ivar str name: Reason + :ivar cterasdk.clients.errors.Error error: Error object + """ + + def __init__(self, error): + super().__init__(HTTPStatus.UNAUTHORIZED, error) + + +class Forbidden(HTTPError): + """ + Unauthorized + + :ivar int code: Status code + :ivar str name: Reason + :ivar cterasdk.clients.errors.Error error: Error object + """ + + def __init__(self, error): + super().__init__(HTTPStatus.FORBIDDEN, error) + + +class NotFound(HTTPError): + """ + NotFound + + :ivar int code: Status code + :ivar str name: Reason + :ivar cterasdk.clients.errors.Error error: Error object + """ + + def __init__(self, error): + super().__init__(HTTPStatus.NOT_FOUND, error) + + +class Unprocessable(HTTPError): + """ + Unprocessable + + :ivar int code: Status code + :ivar str name: Reason + :ivar cterasdk.clients.errors.Error error: Error object + """ + + def __init__(self, error): + super().__init__(HTTPStatus.UNPROCESSABLE_ENTITY, error) + + +class InternalServerError(HTTPError): + """ + InternalServerError + + :ivar int code: Status code + :ivar str name: Reason + :ivar cterasdk.clients.errors.Error error: Error object + """ + + def __init__(self, error): + super().__init__(HTTPStatus.INTERNAL_SERVER_ERROR, error) + + +class BadGateway(HTTPError): + """ + BadGateway + + :ivar int code: Status code + :ivar str name: Reason + :ivar cterasdk.clients.errors.Error error: Error object + """ + + def __init__(self, error): + super().__init__(HTTPStatus.BAD_GATEWAY, error) + + +class ServiceUnavailable(HTTPError): + """ + ServiceUnavailable + + :ivar int code: Status code + :ivar str name: Reason + :ivar cterasdk.clients.errors.Error error: Error object + """ + + def __init__(self, error): + super().__init__(HTTPStatus.SERVICE_UNAVAILABLE, error) + + +class GatewayTimeout(HTTPError): + """ + GatewayTimeout + + :ivar int code: Status code + :ivar str name: Reason + :ivar cterasdk.clients.errors.Error error: Error object + """ + + def __init__(self, error): + super().__init__(HTTPStatus.GATEWAY_TIMEOUT, error) diff --git a/cterasdk/lib/consent.py b/cterasdk/lib/consent.py index 2f1056e3..fd1758d2 100644 --- a/cterasdk/lib/consent.py +++ b/cterasdk/lib/consent.py @@ -1,4 +1,4 @@ -from ..exceptions import ConsentException +from ..exceptions import UserConsentError def ask(question): @@ -11,7 +11,7 @@ def ask(question): try: answer = input(question) except EOFError: - raise ConsentException() + raise UserConsentError() except KeyboardInterrupt: answer = 'n' diff --git a/cterasdk/lib/crypto.py b/cterasdk/lib/crypto.py index 10514ca6..a7c9488c 100644 --- a/cterasdk/lib/crypto.py +++ b/cterasdk/lib/crypto.py @@ -101,9 +101,9 @@ def load_private_key(key, password=None): if commonfs.exists(key): return PrivateKey.from_file(key, password) return PrivateKey.from_string(key, password) - except ValueError as e: + except ValueError as error: logger.error('Failed loading private key.') - raise CTERAException('Failed loading private key', e, reason=str(e)) + raise CTERAException('Failed loading private key.') from error class X509Certificate: @@ -160,9 +160,9 @@ def load_certificate(cert): if commonfs.exists(cert): return X509Certificate.from_file(cert) return X509Certificate.from_string(cert) - except ValueError as e: + except ValueError as error: logger.error('Failed loading certificate.') - raise CTERAException('Failed loading certificate', e, reason=str(e)) + raise CTERAException('Failed loading certificate.') from error def __str__(self): return str( diff --git a/cterasdk/lib/iterator.py b/cterasdk/lib/iterator.py index 865c5a50..84987229 100644 --- a/cterasdk/lib/iterator.py +++ b/cterasdk/lib/iterator.py @@ -2,6 +2,9 @@ from abc import abstractmethod +logger = logging.getLogger('cterasdk.common') + + class BaseIterator: """Abstract Iterator""" @@ -22,7 +25,7 @@ def __next__(self): self._objects.extend(page) if self._objects: return self.object - logging.getLogger('cterasdk.common').debug('Stopping iteration.') + logger.debug('Stopping iteration.') raise StopIteration @property diff --git a/cterasdk/lib/task_manager_base.py b/cterasdk/lib/task_manager_base.py index a8034317..dc59ed94 100644 --- a/cterasdk/lib/task_manager_base.py +++ b/cterasdk/lib/task_manager_base.py @@ -5,6 +5,9 @@ from ..convert import tojsonstr +logger = logging.getLogger('cterasdk.common') + + class TaskRunningStatus: Running = 'running' Completed = 'completed' @@ -40,9 +43,9 @@ def get_task_status(self): def wait(self): task = None while self.running: - logging.getLogger('cterasdk.common').debug('Obtaining task status. %s', {'path': self.path, 'attempt': (self.attempt + 1)}) + logger.debug('Obtaining task status. %s', {'path': self.path, 'attempt': (self.attempt + 1)}) task = self.get_task_status() - logging.getLogger('cterasdk.common').debug('Task status. %s', tojsonstr(task, False)) + logger.debug('Task status. %s', tojsonstr(task, False)) self.increment() self.running = task.status == TaskRunningStatus.Running return TaskBase.resolve(task) @@ -51,19 +54,19 @@ def wait(self): def resolve(task): task_info = {'id': task.id, 'name': task.name, 'status': task.status, 'start_time': task.startTime, 'end_time': task.endTime} if task.status == TaskRunningStatus.Failed: - logging.getLogger('cterasdk.common').error('Task failed. %s', task_info) + logger.error('Task failed. %s', task_info) raise TaskError(task) if task.status == TaskRunningStatus.Warnings: - logging.getLogger('cterasdk.common').warning('Task completed with warnings. %s', task_info) + logger.warning('Task completed with warnings. %s', task_info) if task.status == TaskRunningStatus.Completed: - logging.getLogger('cterasdk.common').debug('Task completed successfully. %s', task_info) + logger.debug('Task completed successfully. %s', task_info) return task def increment(self): if self.attempt >= self.retries: duration = time.strftime("%H:%M:%S", time.gmtime(self.retries * self.seconds)) - logging.getLogger('cterasdk.common').error('Could not obtain task status in a timely manner. %s', {'duration': duration}) - raise CTERAException('Timed out. Could not obtain task status in a timely manner', None, duration=duration) + logger.error('Could not obtain task status in a timely manner. %s', {'duration': duration}) + raise CTERAException('Could not obtain task status in a timely manner.') self.attempt = self.attempt + 1 - logging.getLogger('cterasdk.common').debug('Sleep. %s', {'seconds': self.seconds}) + logger.debug('Sleep. %s', {'seconds': self.seconds}) time.sleep(self.seconds) diff --git a/cterasdk/lib/tracker.py b/cterasdk/lib/tracker.py index ea3a6294..85ba9d37 100644 --- a/cterasdk/lib/tracker.py +++ b/cterasdk/lib/tracker.py @@ -4,6 +4,9 @@ from ..exceptions import CTERAException +logger = logging.getLogger('cterasdk.common') + + class ErrorStatus(CTERAException): def __init__(self, status): @@ -28,38 +31,38 @@ def __init__(self, CTERAHost, ref, success, progress, transient, failure, retrie def track(self): running = True while running: - logging.getLogger('cterasdk.common').debug('Retrieving status. %s', {'ref': self.ref, 'attempt': (self.attempt + 1)}) + logger.debug('Retrieving status. %s', {'ref': self.ref, 'attempt': (self.attempt + 1)}) self.status = self.CTERAHost.api.get(self.ref) - logging.getLogger('cterasdk.common').debug('Current status. %s', {'ref': self.ref, 'status': self.status}) + logger.debug('Current status. %s', {'ref': self.ref, 'status': self.status}) self.increment() running = self.running() return self.resolve() def resolve(self): if self.successful(): - logging.getLogger('cterasdk.common').debug('Success. %s', {'ref': self.ref, 'status': self.status}) + logger.debug('Success. %s', {'ref': self.ref, 'status': self.status}) return self.status if self.failed(): - logging.getLogger('cterasdk.common').debug('Failure. %s', {'ref': self.ref, 'status': self.status}) + logger.debug('Failure. %s', {'ref': self.ref, 'status': self.status}) raise ErrorStatus(self.status) - logging.getLogger('cterasdk.common').debug('Unknown status. %s', {'ref': self.ref, 'status': self.status}) - raise CTERAException('Unknown status', None, status=self.status) + logger.debug('Unknown status. %s', {'ref': self.ref, 'status': self.status}) + raise CTERAException(f'Unknown status: {self.status}') def successful(self): return self.status in self.success def running(self): if self.status in self.progress: - logging.getLogger('cterasdk.common').debug('In progress. %s', {'ref': self.ref, 'status': self.status}) + logger.debug('In progress. %s', {'ref': self.ref, 'status': self.status}) return True if self.status in self.transient: - logging.getLogger('cterasdk.common').debug('Transient state. %s', {'ref': self.ref, 'status': self.status}) + logger.debug('Transient state. %s', {'ref': self.ref, 'status': self.status}) return True - logging.getLogger('cterasdk.common').debug('End state. %s', {'ref': self.ref, 'status': self.status}) + logger.debug('End state. %s', {'ref': self.ref, 'status': self.status}) return False def failed(self): @@ -68,10 +71,10 @@ def failed(self): def increment(self): self.attempt = self.attempt + 1 if self.attempt >= self.retries: - logging.getLogger('cterasdk.common').error('Status did not meet success criteria. %s', {'ref': self.ref, 'status': self.status}) - raise CTERAException('Timed out. Status did not meet success criteria', None, ref=self.ref, status=self.status) + logger.error('Success criteria for %s was not met. Status: %s', self.ref, self.status) + raise CTERAException(f'Success criteria for {self.ret} was not met. Status: {self.status}') - logging.getLogger('cterasdk.common').debug('Sleep. %s', {'seconds': self.seconds}) + logger.debug('Sleeping for %s seconds.', self.seconds) time.sleep(self.seconds) diff --git a/docs/source/UserGuides/DataServices/DirectIO.rst b/docs/source/UserGuides/DataServices/DirectIO.rst index 1c49c565..478768fb 100644 --- a/docs/source/UserGuides/DataServices/DirectIO.rst +++ b/docs/source/UserGuides/DataServices/DirectIO.rst @@ -168,7 +168,7 @@ Streamer API try: await start_stream(file_id, offset) success = True - except ctera_direct.exceptions.StreamError as error: + except cterasdk.exceptions.direct.StreamError as error: await handle_error(error) offset = error.offset # Try to play from where stream was interrupted. diff --git a/docs/source/api/cterasdk.exceptions.direct.rst b/docs/source/api/cterasdk.exceptions.direct.rst new file mode 100644 index 00000000..7d3a41c8 --- /dev/null +++ b/docs/source/api/cterasdk.exceptions.direct.rst @@ -0,0 +1,7 @@ +cterasdk.exceptions.direct module +================================= + +.. automodule:: cterasdk.exceptions.direct + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/api/cterasdk.exceptions.io.rst b/docs/source/api/cterasdk.exceptions.io.rst new file mode 100644 index 00000000..c3ff092a --- /dev/null +++ b/docs/source/api/cterasdk.exceptions.io.rst @@ -0,0 +1,7 @@ +cterasdk.exceptions.io module +============================= + +.. automodule:: cterasdk.exceptions.io + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/api/cterasdk.exceptions.notifications.rst b/docs/source/api/cterasdk.exceptions.notifications.rst new file mode 100644 index 00000000..83308350 --- /dev/null +++ b/docs/source/api/cterasdk.exceptions.notifications.rst @@ -0,0 +1,7 @@ +cterasdk.exceptions.notifications module +======================================== + +.. automodule:: cterasdk.exceptions.notifications + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/api/cterasdk.exceptions.rst b/docs/source/api/cterasdk.exceptions.rst index da84d648..af47f0d6 100644 --- a/docs/source/api/cterasdk.exceptions.rst +++ b/docs/source/api/cterasdk.exceptions.rst @@ -1,7 +1,18 @@ -cterasdk.exceptions module -========================== +cterasdk.exceptions package +=========================== .. automodule:: cterasdk.exceptions :members: :undoc-members: :show-inheritance: + +Submodules +---------- + +.. toctree:: + + cterasdk.exceptions.direct + cterasdk.exceptions.io + cterasdk.exceptions.notifications + cterasdk.exceptions.session + cterasdk.exceptions.transport diff --git a/docs/source/api/cterasdk.exceptions.session.rst b/docs/source/api/cterasdk.exceptions.session.rst new file mode 100644 index 00000000..babf963f --- /dev/null +++ b/docs/source/api/cterasdk.exceptions.session.rst @@ -0,0 +1,7 @@ +cterasdk.exceptions.session module +================================== + +.. automodule:: cterasdk.exceptions.session + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/api/cterasdk.exceptions.transport.rst b/docs/source/api/cterasdk.exceptions.transport.rst new file mode 100644 index 00000000..1c27e7d3 --- /dev/null +++ b/docs/source/api/cterasdk.exceptions.transport.rst @@ -0,0 +1,7 @@ +cterasdk.exceptions.transport module +==================================== + +.. automodule:: cterasdk.exceptions.transport + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/api/cterasdk.rst b/docs/source/api/cterasdk.rst index 7b641916..0f460f5c 100644 --- a/docs/source/api/cterasdk.rst +++ b/docs/source/api/cterasdk.rst @@ -18,6 +18,7 @@ Subpackages cterasdk.core cterasdk.direct cterasdk.edge + cterasdk.exceptions cterasdk.lib cterasdk.objects diff --git a/tests/ut/aio/direct/test_get_metadata.py b/tests/ut/aio/direct/test_get_metadata.py index 1975e993..9da62cb7 100644 --- a/tests/ut/aio/direct/test_get_metadata.py +++ b/tests/ut/aio/direct/test_get_metadata.py @@ -3,7 +3,6 @@ from http import HTTPStatus from unittest import mock import munch -from cterasdk import ctera_direct from cterasdk import exceptions from . import base @@ -18,7 +17,7 @@ def setUp(self): # pylint: disable=arguments-differ async def test_retries_on_error(self): self._direct._client._api.get.return_value = munch.Munch({'chunks': None}) # pylint: disable=protected-access with mock.patch('asyncio.sleep'): - with self.assertRaises(ctera_direct.exceptions.BlocksNotFoundError): + with self.assertRaises(exceptions.direct.BlocksNotFoundError): await self._direct.metadata(self._file_id) self._direct._client._api.get.assert_has_calls( # pylint: disable=protected-access self._retries * [ @@ -29,43 +28,43 @@ async def test_retries_on_error(self): async def test_get_file_metadata_not_found(self): self._direct._client._api.get.return_value = munch.Munch({'chunks': None}) # pylint: disable=protected-access with mock.patch('asyncio.sleep'): - with self.assertRaises(ctera_direct.exceptions.BlocksNotFoundError) as error: + with self.assertRaises(exceptions.direct.BlocksNotFoundError) as error: await self._direct.metadata(self._file_id) self.assertEqual(error.exception.errno, errno.ENODATA) self.assertEqual(error.exception.strerror, f'Could not find blocks for file ID: {self._file_id}') self.assertEqual(error.exception.filename, self._file_id) async def test_get_file_metadata_error_400(self): - self._direct._client._api.get.side_effect = exceptions.HTTPError( # pylint: disable=protected-access + self._direct._client._api.get.side_effect = exceptions.transport.HTTPError( # pylint: disable=protected-access HTTPStatus.BAD_REQUEST, BaseDirectMetadata._create_error_object() ) with mock.patch('asyncio.sleep'): - with self.assertRaises(ctera_direct.exceptions.NotFoundError) as error: + with self.assertRaises(exceptions.direct.NotFoundError) as error: await self._direct.metadata(self._file_id) self.assertEqual(error.exception.errno, errno.EBADF) self.assertEqual(error.exception.strerror, 'File not found') self.assertEqual(error.exception.filename, self._file_id) async def test_get_file_metadata_error_401(self): - self._direct._client._api.get.side_effect = exceptions.HTTPError( # pylint: disable=protected-access + self._direct._client._api.get.side_effect = exceptions.transport.HTTPError( # pylint: disable=protected-access HTTPStatus.UNAUTHORIZED, BaseDirectMetadata._create_error_object() ) with mock.patch('asyncio.sleep'): - with self.assertRaises(ctera_direct.exceptions.UnAuthorized) as error: + with self.assertRaises(exceptions.direct.UnAuthorized) as error: await self._direct.metadata(self._file_id) self.assertEqual(error.exception.errno, errno.EACCES) self.assertEqual(error.exception.strerror, 'Unauthorized: You do not have the necessary permissions to access this resource') self.assertEqual(error.exception.filename, self._file_id) async def test_get_file_metadata_error_422(self): - self._direct._client._api.get.side_effect = exceptions.HTTPError( # pylint: disable=protected-access + self._direct._client._api.get.side_effect = exceptions.transport.HTTPError( # pylint: disable=protected-access HTTPStatus.UNPROCESSABLE_ENTITY, BaseDirectMetadata._create_error_object() ) with mock.patch('asyncio.sleep'): - with self.assertRaises(ctera_direct.exceptions.UnprocessableContent) as error: + with self.assertRaises(exceptions.direct.UnprocessableContent) as error: await self._direct.metadata(self._file_id) self.assertEqual(error.exception.errno, errno.ENOTSUP) self.assertEqual(error.exception.strerror, 'Not all blocks of the requested file are stored on a storage node set to Direct Mode') @@ -73,19 +72,19 @@ async def test_get_file_metadata_error_422(self): async def test_get_file_metadata_unknown_error(self): url = '/xyz' - self._direct._client._api.get.side_effect = exceptions.HTTPError( # pylint: disable=protected-access + self._direct._client._api.get.side_effect = exceptions.transport.HTTPError( # pylint: disable=protected-access HTTPStatus.INTERNAL_SERVER_ERROR, BaseDirectMetadata._create_error_object() ) with mock.patch('asyncio.sleep'): - with self.assertRaises(exceptions.HTTPError) as error: + with self.assertRaises(exceptions.transport.HTTPError) as error: await self._direct.metadata(self._file_id) self.assertEqual(error.exception.error.request.url, url) async def test_get_file_metadata_connection_error(self): self._direct._client._api.get.side_effect = ConnectionError # pylint: disable=protected-access with mock.patch('asyncio.sleep'): - with self.assertRaises(ctera_direct.exceptions.BlockListConnectionError) as error: + with self.assertRaises(exceptions.direct.BlockListConnectionError) as error: await self._direct.metadata(self._file_id) self.assertEqual(error.exception.errno, errno.ENETRESET) self.assertEqual(error.exception.strerror, @@ -95,7 +94,7 @@ async def test_get_file_metadata_connection_error(self): async def test_get_file_metadata_timeout(self): self._direct._client._api.get.side_effect = asyncio.TimeoutError # pylint: disable=protected-access with mock.patch('asyncio.sleep'): - with self.assertRaises(ctera_direct.exceptions.BlockListTimeout) as error: + with self.assertRaises(exceptions.direct.BlockListTimeout) as error: await self._direct.metadata(self._file_id) self.assertEqual(error.exception.errno, errno.ETIMEDOUT) self.assertEqual(error.exception.strerror, diff --git a/tests/ut/aio/direct/test_get_object.py b/tests/ut/aio/direct/test_get_object.py index d3ccc073..75b52ffc 100644 --- a/tests/ut/aio/direct/test_get_object.py +++ b/tests/ut/aio/direct/test_get_object.py @@ -19,7 +19,7 @@ async def test_get_object_connection_error(self): chunk = BaseDirectMetadata._create_chunk() self._direct._client._client.get.side_effect = ConnectionError # pylint: disable=protected-access with mock.patch('asyncio.sleep'): - with self.assertRaises(ctera_direct.exceptions.DownloadConnectionError) as error: + with self.assertRaises(exceptions.direct.DownloadConnectionError) as error: await get_object(self._direct._client._client, self._file_id, chunk) # pylint: disable=protected-access self.assertEqual(error.exception.errno, errno.ENETRESET) self.assertEqual(error.exception.strerror, 'Failed to download block. Connection error') @@ -30,7 +30,7 @@ async def test_get_object_timeout(self): chunk = BaseDirectMetadata._create_chunk() self._direct._client._client.get.side_effect = asyncio.TimeoutError # pylint: disable=protected-access with mock.patch('asyncio.sleep'): - with self.assertRaises(ctera_direct.exceptions.DownloadTimeout) as error: + with self.assertRaises(exceptions.direct.DownloadTimeout) as error: await get_object(self._direct._client._client, self._file_id, chunk) # pylint: disable=protected-access self.assertEqual(error.exception.errno, errno.ETIMEDOUT) self.assertEqual(error.exception.strerror, 'Failed to download block. Timed out') @@ -46,7 +46,7 @@ async def stream_reader(): self._direct._client._client.get.return_value = munch.Munch({'read': stream_reader}) # pylint: disable=protected-access with mock.patch('asyncio.sleep'): - with self.assertRaises(ctera_direct.exceptions.DownloadError) as error: + with self.assertRaises(exceptions.direct.DownloadError) as error: await get_object(self._direct._client._client, self._file_id, chunk) # pylint: disable=protected-access self.assertEqual(error.exception.errno, errno.EIO) self.assertEqual(str(error.exception.strerror), message) @@ -55,12 +55,12 @@ async def stream_reader(): async def test_get_client_error(self): chunk = BaseDirectMetadata._create_chunk() - self._direct._client._client.get.side_effect = exceptions.HTTPError( # pylint: disable=protected-access + self._direct._client._client.get.side_effect = exceptions.transport.HTTPError( # pylint: disable=protected-access HTTPStatus.INTERNAL_SERVER_ERROR, BaseDirectMetadata._create_error_object(HTTPStatus.INTERNAL_SERVER_ERROR.value) ) with mock.patch('asyncio.sleep'): - with self.assertRaises(ctera_direct.exceptions.DownloadError) as error: + with self.assertRaises(exceptions.direct.DownloadError) as error: await get_object(self._direct._client._client, self._file_id, chunk) # pylint: disable=protected-access self.assertEqual(error.exception.errno, errno.EIO) self.assertEqual(error.exception.strerror.response.status, 500) diff --git a/tests/ut/aio/test_login.py b/tests/ut/aio/test_login.py index a29a0eaf..b3dfa1fd 100644 --- a/tests/ut/aio/test_login.py +++ b/tests/ut/aio/test_login.py @@ -28,7 +28,7 @@ async def test_login_success(self): async def test_login_failure(self): self._init_global_admin() - self._global_admin.v1.api.form_data = mock.AsyncMock(side_effect=exceptions.HTTPError( + self._global_admin.v1.api.form_data = mock.AsyncMock(side_effect=exceptions.transport.HTTPError( HTTPStatus.FORBIDDEN, munch.Munch( dict(request=munch.Munch( diff --git a/tests/ut/aio/test_notifications.py b/tests/ut/aio/test_notifications.py index 5c44eb7d..c7bb8c94 100644 --- a/tests/ut/aio/test_notifications.py +++ b/tests/ut/aio/test_notifications.py @@ -36,7 +36,7 @@ async def test_get_notifications_defaults(self): async def test_get_notifications_error(self): self._init_global_admin(post_response=None) - with self.assertRaises(exceptions.NotificationsError) as error: + with self.assertRaises(exceptions.notifications.NotificationsError) as error: await notifications.Notifications(self._global_admin).get() self._global_admin.v2.api.post.assert_called_once_with('/metadata/list', mock.ANY) self.assertEqual(error.exception.cloudfolders, None) @@ -98,7 +98,7 @@ async def test_ancestors_success(self): async def test_ancestors_error(self): url = '/xyz' self._init_global_admin() - self._global_admin.v2.api.post = mock.AsyncMock(side_effect=exceptions.HTTPError( + self._global_admin.v2.api.post = mock.AsyncMock(side_effect=exceptions.transport.HTTPError( HTTPStatus.FORBIDDEN, munch.Munch( dict(request=munch.Munch( @@ -106,7 +106,7 @@ async def test_ancestors_error(self): )) ) )) - with self.assertRaises(exceptions.HTTPError) as error: + with self.assertRaises(exceptions.transport.HTTPError) as error: await notifications.Notifications(self._global_admin).ancestors(self._descendant) self.assertEqual(error.exception.error.request.url, url) diff --git a/tests/ut/aio/test_users.py b/tests/ut/aio/test_users.py index cb5f1faf..839b58e4 100644 --- a/tests/ut/aio/test_users.py +++ b/tests/ut/aio/test_users.py @@ -36,7 +36,7 @@ async def test_user_not_found(self): self._init_global_admin(get_multi_response=TestAsyncCoreUsers._user_object(**{'name': None})) with self.assertRaises(exceptions.ObjectNotFoundException) as error: await users.Users(self._global_admin).get(self._local_user) - self.assertEqual(error.exception.message, 'Could not find user') + self.assertEqual(str(error.exception), f'Object not found: /users/{self._username}') @staticmethod def _user_object(**kwargs): diff --git a/tests/ut/core/admin/test_buckets.py b/tests/ut/core/admin/test_buckets.py index 48e6af9b..074ea592 100644 --- a/tests/ut/core/admin/test_buckets.py +++ b/tests/ut/core/admin/test_buckets.py @@ -34,8 +34,9 @@ def test_get_bucket_not_found(self): self._init_global_admin(get_multi_response=get_multi_response) with self.assertRaises(exceptions.CTERAException) as error: buckets.Buckets(self._global_admin).get(self._bucket_name) - self._global_admin.api.get_multi.assert_called_once_with(f'/locations/{self._bucket_name}', mock.ANY) - self.assertEqual('Could not find bucket', error.exception.message) + ref = f'/locations/{self._bucket_name}' + self._global_admin.api.get_multi.assert_called_once_with(ref, mock.ANY) + self.assertEqual(f'Object not found: {ref}', str(error.exception)) def test_add_bucket(self): add_response = 'Success' @@ -88,7 +89,7 @@ def test_modify_bucket_not_found(self): self._global_admin.api.get = mock.MagicMock(side_effect=exceptions.CTERAException()) with self.assertRaises(exceptions.CTERAException) as error: buckets.Buckets(self._global_admin).modify(self._bucket_name) - self.assertEqual('Failed to get bucket', error.exception.message) + self.assertEqual(f'Bucket not found: /locations/{self._bucket_name}', str(error.exception)) @staticmethod def _get_bucket_param(**kwargs): diff --git a/tests/ut/core/admin/test_cloudfs_backups.py b/tests/ut/core/admin/test_cloudfs_backups.py index cc0f8d73..7bb675cc 100644 --- a/tests/ut/core/admin/test_cloudfs_backups.py +++ b/tests/ut/core/admin/test_cloudfs_backups.py @@ -53,7 +53,7 @@ def test_add_backup_folder_failure(self): expected_param = TestCoreBackups._get_backup_folder_object(self._name, self._group_ref, self._user_ref, True) actual_param = self._global_admin.api.add.call_args[0][1] self._assert_equal_objects(actual_param, expected_param) - self.assertEqual(error_message, error.exception.message) + self.assertEqual(error_message, str(error.exception)) def test_modify_backup_folder_success(self): put_response = 'Success' diff --git a/tests/ut/core/admin/test_cloudfs_cloud_drives.py b/tests/ut/core/admin/test_cloudfs_cloud_drives.py index d3835a8b..802e8792 100644 --- a/tests/ut/core/admin/test_cloudfs_cloud_drives.py +++ b/tests/ut/core/admin/test_cloudfs_cloud_drives.py @@ -146,7 +146,7 @@ def test_add_cloud_drive_with_local_owner_raise(self): self._global_admin.users.get.assert_called_once_with(self._local_user_account, ['baseObjectRef']) self._global_admin.cloudfs.groups.get.assert_called_once_with(self._group, ['baseObjectRef']) - self.assertEqual(error_message, error.exception.message) + self.assertEqual(error_message, str(error.exception)) def test_delete_with_local_owner(self): self._init_global_admin() @@ -229,7 +229,7 @@ def test_set_folders_acl_raise(self): with self.assertRaises(exceptions.CTERAException) as error: cloudfs.CloudDrives(self._global_admin).setfacl(self._nt_acl_folders.foldersPath, self._nt_acl_folders.sddlString, self._nt_acl_folders.isRecursive) - self.assertEqual('Failed to setFoldersACL', error.exception.message) + self.assertEqual('ACL modification failed.', str(error.exception)) def test_set_owner_acl_success(self): execute_response = 'Success' @@ -247,7 +247,7 @@ def test_set_owner_acl_raise(self): with self.assertRaises(exceptions.CTERAException) as error: cloudfs.CloudDrives(self._global_admin).setoacl(self._nt_acl_owner.foldersPath, self._nt_acl_owner.ownerSid, self._nt_acl_owner.isRecursive) - self.assertEqual('Failed to setOwnerACL', error.exception.message) + self.assertEqual('Owner modification failed.', str(error.exception)) def test_modify_cloudfolder_failure(self): mock_find_cloudfolder = self.patch_call('cterasdk.core.cloudfs.CloudDrives.find') diff --git a/tests/ut/core/admin/test_cloudfs_folder_groups.py b/tests/ut/core/admin/test_cloudfs_folder_groups.py index 67009beb..92e8f923 100644 --- a/tests/ut/core/admin/test_cloudfs_folder_groups.py +++ b/tests/ut/core/admin/test_cloudfs_folder_groups.py @@ -62,8 +62,9 @@ def test_get_folder_group_not_found(self): self._init_global_admin(get_multi_response=get_multi_response) with self.assertRaises(exceptions.CTERAException) as error: cloudfs.FolderGroups(self._global_admin).get(self._name) - self._global_admin.api.get_multi.assert_called_once_with(f'/foldersGroups/{self._name}', mock.ANY) - self.assertEqual('Could not find folder group', error.exception.message) + ref = f'/foldersGroups/{self._name}' + self._global_admin.api.get_multi.assert_called_once_with(ref, mock.ANY) + self.assertEqual(f'Object not found: {ref}', str(error.exception)) def test_add_folder_group_no_owner(self): self._init_global_admin(execute_response='Success') @@ -99,7 +100,7 @@ def test_add_folder_group_no_owner_raise(self): self._global_admin.api.execute = mock.MagicMock(side_effect=expected_exception) with self.assertRaises(exceptions.CTERAException) as error: cloudfs.FolderGroups(self._global_admin).add(self._name) - self.assertEqual(error_message, error.exception.message) + self.assertEqual(error_message, str(error.exception)) def test_add_folder_group_no_owner_fixed_block_size(self): self._init_global_admin(execute_response='Success') @@ -129,35 +130,34 @@ def test_add_folder_group_no_owner_raise_fixed_block_size(self): self._global_admin.api.execute = mock.MagicMock(side_effect=expected_exception) with self.assertRaises(exceptions.CTERAException) as error: cloudfs.FolderGroups(self._global_admin).add(self._name, deduplication_method_type=self.fixed_block_size) - self.assertEqual(error_message, error.exception.message) + self.assertEqual(error_message, str(error.exception)) def test_modify_folder_group(self): get_response = munch.Munch(dict(name=self._name)) self._init_global_admin(get_response=get_response, put_response='Success') ret = cloudfs.FolderGroups(self._global_admin).modify(self._name, self._new_name) - self._global_admin.api.get.assert_called_once_with(f'/foldersGroups/{self._name}') - self._global_admin.api.put.assert_called_once_with(f'/foldersGroups/{self._name}', mock.ANY) + ref = f'/foldersGroups/{self._name}' + self._global_admin.api.get.assert_called_once_with(ref) + self._global_admin.api.put.assert_called_once_with(ref, mock.ANY) expected_param = munch.Munch(dict(name=self._new_name)) actual_param = self._global_admin.api.put.call_args[0][1] self._assert_equal_objects(actual_param, expected_param) self.assertEqual(ret, 'Success') def test_modify_folder_group_user_not_exists(self): - error_message = "Failed to get folder group" - expected_exception = exceptions.CTERAException(message=error_message) + expected_exception = exceptions.CTERAException() self._global_admin.api.get = mock.MagicMock(side_effect=expected_exception) with self.assertRaises(exceptions.CTERAException) as error: cloudfs.FolderGroups(self._global_admin).modify(self._name, self._new_name) - self.assertEqual(error_message, error.exception.message) + self.assertEqual(f'Folder group not found: /foldersGroups/{self._name}', str(error.exception)) def test_modify_folder_group_update_failure(self): - error_message = "Expected Failure" - expected_exception = exceptions.CTERAException(message=error_message) + expected_exception = exceptions.CTERAException() self._global_admin.api.get = mock.MagicMock(return_value=munch.Munch(dict(name=self._name))) self._global_admin.api.put = mock.MagicMock(side_effect=expected_exception) with self.assertRaises(exceptions.CTERAException) as error: cloudfs.FolderGroups(self._global_admin).modify(self._name, self._new_name) - self.assertEqual(error_message, error.exception.message) + self.assertEqual(f'Folder group modification failed: /foldersGroups/{self._name}', str(error.exception)) def test_delete(self): self._init_global_admin(execute_response='Success') diff --git a/tests/ut/core/admin/test_cloudfs_zones.py b/tests/ut/core/admin/test_cloudfs_zones.py index f030834e..8fc0f942 100644 --- a/tests/ut/core/admin/test_cloudfs_zones.py +++ b/tests/ut/core/admin/test_cloudfs_zones.py @@ -66,7 +66,7 @@ def test_get_zone_raise(self): or_filter=False) actual_param = self._global_admin.api.execute.call_args[0][2] self._assert_equal_objects(actual_param, expected_param) - self.assertEqual('Zone not found', error.exception.message) + self.assertEqual(f'Zone not found: {self._zone_name}', str(error.exception)) def test_add_zone_default_args_success(self): execute_response = TestCoreZones._get_add_zone_response('OK') @@ -86,7 +86,7 @@ def test_add_zone_raise(self): expected_param = self._get_zone_param() actual_param = self._global_admin.api.execute.call_args[0][2] self._assert_equal_objects(actual_param, expected_param) - self.assertEqual('Zone creation failed', error.exception.message) + self.assertEqual(f'Zone creation failed: {str(execute_response)}', str(error.exception)) def test_add_folders_success(self): self._init_global_admin() @@ -138,7 +138,7 @@ def test_add_folders_raise(self): expected_param = self._get_add_folders_param() actual_param = self._global_admin.api.execute.call_args[0][2] self._assert_equal_objects(actual_param, expected_param) - self.assertEqual('Failed adding folders to zone', error.exception.message) + self.assertEqual(f'Failed adding folders to zone: {self._zone_name}', str(error.exception)) def test_add_devices_success(self): self._init_global_admin() @@ -184,7 +184,7 @@ def test_add_devices_raise(self): expected_param = self._get_add_devices_param() actual_param = self._global_admin.api.execute.call_args[0][2] self._assert_equal_objects(actual_param, expected_param) - self.assertEqual('Failed adding devices to zone', error.exception.message) + self.assertEqual(f"Failed adding devices: [{', '.join(self._device_names)}] to zone: {self._zone_name}", str(error.exception)) def _get_add_devices_param(self): param = self._get_zone_param(zone_id=self._zone_id) diff --git a/tests/ut/core/admin/test_devices.py b/tests/ut/core/admin/test_devices.py index a155666f..95fb1764 100644 --- a/tests/ut/core/admin/test_devices.py +++ b/tests/ut/core/admin/test_devices.py @@ -44,7 +44,7 @@ def test_device_notfound(self): self._init_global_admin(get_multi_response=o) with self.assertRaises(exceptions.CTERAException) as error: devices.Devices(self._global_admin).device(o.name) - self.assertEqual('Device not found', error.exception.message) + self.assertEqual(f'Object not found: /portals/None/devices/None', str(error.exception)) def test_filers_no_device_types(self): self._test_filers(None, enum.DeviceType.Gateways) diff --git a/tests/ut/core/admin/test_directory_services.py b/tests/ut/core/admin/test_directory_services.py index a2ec340c..91dba5d5 100644 --- a/tests/ut/core/admin/test_directory_services.py +++ b/tests/ut/core/admin/test_directory_services.py @@ -72,7 +72,7 @@ def test_set_advanced_mapping_disconnected(self): self._init_global_admin() with self.assertRaises(exceptions.CTERAException) as error: directoryservice.DirectoryService(self._global_admin).set_advanced_mapping(None) - self.assertEqual('Failed to apply mapping. Not connected to directory services.', error.exception.message) + self.assertEqual('Failed to apply mapping. Not connected to directory services.', str(error.exception)) def test_set_advanced_mapping(self): put_response = 'Success' @@ -123,16 +123,16 @@ def test_fetch_users_groups_success(self): def test_fetch_invalid_domain(self): self._init_global_admin(get_response=['invalid.domain']) - with self.assertRaises(exceptions.CTERAException) as error: + with self.assertRaises(exceptions.InputError) as error: directoryservice.DirectoryService(self._global_admin).fetch(self._accounts) - self.assertEqual('Invalid domain', error.exception.message) + self.assertEqual(f'Invalid domain: {self._domain}', error.exception.args[1]) def test_fetch_invalid_account_type(self): self._init_global_admin(get_response=[self._domain]) bad_account = munch.Munch({'name': self._account_user_name, 'directory': self._domain, 'account_type': 'bad_type'}) - with self.assertRaises(exceptions.CTERAException) as error: + with self.assertRaises(exceptions.InputError) as error: directoryservice.DirectoryService(self._global_admin).fetch([bad_account]) - self.assertEqual('Invalid account type', error.exception.message) + self.assertEqual(f'Invalid account type: {bad_account["account_type"]}', error.exception.args[1]) def test_connect(self): mock_session = self.patch_call("cterasdk.objects.services.Management.session") @@ -173,7 +173,7 @@ def test_set_access_control_disconnected(self): self._init_global_admin() with self.assertRaises(exceptions.CTERAException) as error: directoryservice.DirectoryService(self._global_admin).set_access_control(self._acl) - self.assertEqual('Failed to apply access control. Not connected to directory services.', error.exception.message) + self.assertEqual('Failed to apply access control. Not connected to directory services.', str(error.exception)) def test_set_access_control_with_default(self): mock_search_users = self.patch_call("cterasdk.core.directoryservice.DirectoryService._search_users") @@ -209,7 +209,7 @@ def test_user_search_not_found(self): directoryservice.DirectoryService(self._global_admin)._search_users(self._domain, # pylint: disable=protected-access self._account_user_name) self._global_admin.api.execute.assert_called_once_with('', 'searchAD', mock.ANY) - self.assertEqual('Could not find results that match your search criteria', error.exception.message) + self.assertEqual(f'Could not find user: {self._account_user_name}@{self._domain}', str(error.exception)) def test_group_search_not_found(self): self._init_global_admin(execute_response=None) @@ -217,7 +217,7 @@ def test_group_search_not_found(self): directoryservice.DirectoryService(self._global_admin)._search_groups(self._domain, # pylint: disable=protected-access self._account_group_name) self._global_admin.api.execute.assert_called_once_with('', 'searchAD', mock.ANY) - self.assertEqual('Could not find results that match your search criteria', error.exception.message) + self.assertEqual(f'Could not find group: {self._account_group_name}@{self._domain}', str(error.exception)) def test_user_search_no_match(self): self._init_global_admin(execute_response=[munch.Munch({'name': 'random'})]) @@ -225,7 +225,7 @@ def test_user_search_no_match(self): directoryservice.DirectoryService(self._global_admin)._search_users(self._domain, # pylint: disable=protected-access self._account_user_name) self._global_admin.api.execute.assert_called_once_with('', 'searchAD', mock.ANY) - self.assertEqual('Search returned multiple results, but none matched your search criteria', error.exception.message) + self.assertEqual('Search returned multiple results, but none matched the specified criteria.', str(error.exception)) def test_user_search_found(self): self._init_global_admin(execute_response=[munch.Munch({'name': self._account_user_name})]) diff --git a/tests/ut/core/admin/test_groups.py b/tests/ut/core/admin/test_groups.py index e11ff42f..d409e986 100644 --- a/tests/ut/core/admin/test_groups.py +++ b/tests/ut/core/admin/test_groups.py @@ -43,13 +43,14 @@ def test_get_group_not_found(self): self._init_global_admin(get_multi_response=get_multi_response) with self.assertRaises(exceptions.CTERAException) as error: groups.Groups(self._global_admin).get(self._local_group) - self._global_admin.api.get_multi.assert_called_once_with(f'/localGroups/{self._groupname}', mock.ANY) + ref = f'/localGroups/{self._groupname}' + self._global_admin.api.get_multi.assert_called_once_with(ref, mock.ANY) expected_include = ['/' + attr for attr in groups.Groups.default] actual_include = self._global_admin.api.get_multi.call_args[0][1] self.assertEqual(len(expected_include), len(actual_include)) for attr in expected_include: self.assertIn(attr, actual_include) - self.assertEqual('Could not find group', error.exception.message) + self.assertEqual(f'Object not found: {ref}', str(error.exception)) def _get_group_object(self, **kwargs): group_object = Object() @@ -102,8 +103,9 @@ def test_modify_group_not_found(self): self._global_admin.api.get = mock.MagicMock(side_effect=exceptions.CTERAException()) with self.assertRaises(exceptions.CTERAException) as error: groups.Groups(self._global_admin).modify(self._groupname) - self._global_admin.api.get.assert_called_once_with(f'/localGroups/{self._groupname}') - self.assertEqual('Failed to retrieve group', error.exception.message) + ref = f'/localGroups/{self._groupname}' + self._global_admin.api.get.assert_called_once_with(ref) + self.assertEqual(f'Group not found: {ref}', str(error.exception)) def test_modify_failure(self): get_response = self._get_group_object(name=self._groupname) @@ -111,11 +113,12 @@ def test_modify_failure(self): self._global_admin.api.execute = mock.MagicMock(side_effect=exceptions.CTERAException()) with self.assertRaises(exceptions.CTERAException) as error: groups.Groups(self._global_admin).modify(self._groupname, self._new_groupname, self._description) - self._global_admin.api.execute.assert_called_once_with(f'/localGroups/{self._groupname}', 'updateGroup', mock.ANY) + ref = f'/localGroups/{self._groupname}' + self._global_admin.api.execute.assert_called_once_with(ref, 'updateGroup', mock.ANY) expected_param = self._get_update_group_object(name=self._new_groupname, description=self._description) actual_param = self._global_admin.api.execute.call_args[0][2] self._assert_equal_objects(actual_param, expected_param) - self.assertEqual('Failed to modify group', error.exception.message) + self.assertEqual(f'Group modification failed: {ref}', str(error.exception)) def _get_add_group_object(self, **kwargs): param = Object() diff --git a/tests/ut/core/admin/test_plans.py b/tests/ut/core/admin/test_plans.py index 8ec31b71..f6c44e12 100644 --- a/tests/ut/core/admin/test_plans.py +++ b/tests/ut/core/admin/test_plans.py @@ -32,13 +32,14 @@ def test_get_plan_not_found(self): self._init_global_admin(get_multi_response=get_multi_response) with self.assertRaises(exceptions.CTERAException) as error: plans.Plans(self._global_admin).get(self._plan_name) - self._global_admin.api.get_multi.assert_called_once_with('/plans/' + self._plan_name, mock.ANY) + ref = '/plans/' + self._plan_name + self._global_admin.api.get_multi.assert_called_once_with(ref, mock.ANY) expected_include = ['/' + attr for attr in plans.Plans.default] actual_include = self._global_admin.api.get_multi.call_args[0][1] self.assertEqual(len(expected_include), len(actual_include)) for attr in expected_include: self.assertIn(attr, actual_include) - self.assertEqual('Could not find subscription plan', error.exception.message) + self.assertEqual(f'Object not found: {ref}', str(error.exception)) def _test__get_storage_amount(self, new, existing, expected): self.assertEqual(plans.Plans._get_storage_amount(new, existing), expected) # pylint: disable=protected-access @@ -72,25 +73,28 @@ def test_delete_failure(self): self._global_admin.api.delete = mock.MagicMock(side_effect=exceptions.CTERAException()) with self.assertRaises(exceptions.CTERAException) as error: plans.Plans(self._global_admin).delete(self._plan_name) - self._global_admin.api.delete.assert_called_once_with(f'/plans/{self._plan_name}') - self.assertEqual('Plan deletion failed', error.exception.message) + ref = f'/plans/{self._plan_name}' + self._global_admin.api.delete.assert_called_once_with(ref) + self.assertEqual(f'Plan deletion failed: {ref}', str(error.exception)) def test_modify_plan_not_found(self): self._init_global_admin() self._global_admin.api.get = mock.MagicMock(side_effect=exceptions.CTERAException()) with self.assertRaises(exceptions.CTERAException) as error: plans.Plans(self._global_admin).modify(self._plan_name) - self._global_admin.api.get.assert_called_once_with(f'/plans/{self._plan_name}') - self.assertEqual('Could not find subscription plan', error.exception.message) + ref = f'/plans/{self._plan_name}' + self._global_admin.api.get.assert_called_once_with(ref) + self.assertEqual(f'Plan not found: {ref}', str(error.exception)) def test_modify_update_failure(self): self._init_global_admin() self._global_admin.api.put = mock.MagicMock(side_effect=exceptions.CTERAException()) with self.assertRaises(exceptions.CTERAException) as error: plans.Plans(self._global_admin).modify(self._plan_name) - self._global_admin.api.get.assert_called_once_with(f'/plans/{self._plan_name}') + ref = f'/plans/{self._plan_name}' + self._global_admin.api.get.assert_called_once_with(ref) self._global_admin.api.put.assert_called_once_with(f'/plans/{self._plan_name}', mock.ANY) - self.assertEqual('Could not modify subscription plan', error.exception.message) + self.assertEqual(f'Plan modification failed: {ref}', str(error.exception)) def test_modify_success_without_apply_changes(self): get_response = self._get_plan_object( diff --git a/tests/ut/core/admin/test_portals.py b/tests/ut/core/admin/test_portals.py index ae3caefc..c8ab60c3 100644 --- a/tests/ut/core/admin/test_portals.py +++ b/tests/ut/core/admin/test_portals.py @@ -42,7 +42,7 @@ def test_get_tenant_not_found(self): self._init_global_admin(get_multi_response=self._get_portal_object(name=None)) with self.assertRaises(exceptions.CTERAException) as error: portals.Portals(self._global_admin).get(self._name) - self.assertEqual('Could not find tenant', error.exception.message) + self.assertEqual(f'Object not found: /portals/{self._name}', str(error.exception)) @staticmethod def _get_query_portals_response(execute_path, execute_name, execute_param): diff --git a/tests/ut/core/admin/test_reports.py b/tests/ut/core/admin/test_reports.py index 132b5715..9d9621b3 100644 --- a/tests/ut/core/admin/test_reports.py +++ b/tests/ut/core/admin/test_reports.py @@ -38,7 +38,7 @@ def test_generate_report_success(self): def test_generate_report_failure(self): with self.assertRaises(exceptions.InputError) as error: reports.Reports(self._global_admin).generate('Expected Failure') - self.assertEqual('Invalid report type', error.exception.message) + self.assertEqual('Invalid report type', error.exception.args[1]) def _assert_called_once_with(self, report_name): self._global_admin.api.get.assert_called_once_with(f'/reports/{report_name}') diff --git a/tests/ut/core/admin/test_servers.py b/tests/ut/core/admin/test_servers.py index b4050d29..fac1b38b 100644 --- a/tests/ut/core/admin/test_servers.py +++ b/tests/ut/core/admin/test_servers.py @@ -75,7 +75,7 @@ def test_get_servers_failure(self): self.assertEqual(len(expected_include), len(actual_include)) for attr in expected_include: self.assertIn(attr, actual_include) - self.assertEqual('Could not find server', error.exception.message) + self.assertEqual(f'Object not found: /servers/{self._server}', str(error.exception)) def test_modify_server_not_found(self): self._init_global_admin() @@ -83,16 +83,17 @@ def test_modify_server_not_found(self): with self.assertRaises(exceptions.CTERAException) as error: servers.Servers(self._global_admin).modify(self._server) self._global_admin.api.get.assert_called_once_with(f'/servers/{self._server}') - self.assertEqual('Failed to retrieve server', error.exception.message) + self.assertEqual(f'Server not found: /servers/{self._server}', str(error.exception)) def test_modify_server_update_failure(self): self._init_global_admin(get_response=self._server) self._global_admin.api.put = mock.MagicMock(side_effect=exceptions.CTERAException()) with self.assertRaises(exceptions.CTERAException) as error: servers.Servers(self._global_admin).modify(self._server) - self._global_admin.api.get.assert_called_once_with(f'/servers/{self._server}') + ref = f'/servers/{self._server}' + self._global_admin.api.get.assert_called_once_with(ref) self._global_admin.api.put.assert_called_once_with(f'/servers/{self._server}', self._server) - self.assertEqual('Could not modify server', error.exception.message) + self.assertEqual(f'Server modification failed: {ref}', str(error.exception)) def test_modify_success(self): new_server_name = 'server1' diff --git a/tests/ut/core/admin/test_startup.py b/tests/ut/core/admin/test_startup.py index 753eac8f..0af16b9d 100644 --- a/tests/ut/core/admin/test_startup.py +++ b/tests/ut/core/admin/test_startup.py @@ -31,4 +31,4 @@ def test_wait_timed_out(self): self._init_setup(get_response=get_response) with self.assertRaises(exceptions.CTERAException) as error: startup.Startup(self._global_admin).wait(retries=2, seconds=1) - self.assertEqual('Timed out. Server did not start in a timely manner', error.exception.message) + self.assertEqual('Timed out. Server did not start in a timely manner', str(error.exception)) diff --git a/tests/ut/core/admin/test_storage_classes.py b/tests/ut/core/admin/test_storage_classes.py index 20a90603..fa739853 100644 --- a/tests/ut/core/admin/test_storage_classes.py +++ b/tests/ut/core/admin/test_storage_classes.py @@ -62,4 +62,4 @@ def test_get_storage_class_in_tenant_context_not_found(self): with self.assertRaises(exceptions.CTERAException) as error: storage_classes.StorageClasses(self._global_admin).get(self._storage_class_name) self._global_admin.api.execute.assert_called_once_with('', 'getStorageClasses') - self.assertEqual('Could not find storage class.', error.exception.message) + self.assertEqual(f'Storage class not found: /storageClasses/{self._storage_class_name}', str(error.exception)) diff --git a/tests/ut/core/admin/test_syslog.py b/tests/ut/core/admin/test_syslog.py index 6fb0cfdb..21bdd6f5 100644 --- a/tests/ut/core/admin/test_syslog.py +++ b/tests/ut/core/admin/test_syslog.py @@ -55,7 +55,7 @@ def test_modify_disabled_syslog(self): with self.assertRaises(exceptions.CTERAException) as error: syslog.Syslog(self._global_admin).modify() self._global_admin.api.get.assert_called_once_with('/settings/logsSettings/syslogConfig') - self.assertEqual('Syslog configuration cannot be modified when disabled', error.exception.message) + self.assertEqual('Syslog configuration cannot be modified when disabled', str(error.exception)) def _default_settings(self): param = Object() diff --git a/tests/ut/core/admin/test_templates.py b/tests/ut/core/admin/test_templates.py index ced9be8e..01b1e62e 100644 --- a/tests/ut/core/admin/test_templates.py +++ b/tests/ut/core/admin/test_templates.py @@ -45,7 +45,7 @@ def test_get_template_not_found(self): self.assertEqual(len(expected_include), len(actual_include)) for attr in expected_include: self.assertIn(attr, actual_include) - self.assertEqual('Could not find template', error.exception.message) + self.assertEqual(f'Object not found: /deviceTemplates/{self._name}', str(error.exception)) def test_list_templates_default_attrs(self): with mock.patch("cterasdk.core.templates.query.iterator") as query_iterator_mock: diff --git a/tests/ut/core/admin/test_users.py b/tests/ut/core/admin/test_users.py index 42a1e1e8..7f7a98e0 100644 --- a/tests/ut/core/admin/test_users.py +++ b/tests/ut/core/admin/test_users.py @@ -109,13 +109,14 @@ def test_get_user_not_found(self): self._init_global_admin(get_multi_response=get_multi_response) with self.assertRaises(exceptions.CTERAException) as error: users.Users(self._global_admin).get(self._local_user_account) - self._global_admin.api.get_multi.assert_called_once_with('/users/' + self._local_user_account.name, mock.ANY) + ref = '/users/' + self._local_user_account.name + self._global_admin.api.get_multi.assert_called_once_with(ref, mock.ANY) expected_include = ['/' + attr for attr in users.Users.default] actual_include = self._global_admin.api.get_multi.call_args[0][1] self.assertEqual(len(expected_include), len(actual_include)) for attr in expected_include: self.assertIn(attr, actual_include) - self.assertEqual('Could not find user', error.exception.message) + self.assertEqual(f'Object not found: {ref}', str(error.exception)) def test_modify_local_user(self): current_user_object = self._get_user_object( @@ -277,14 +278,14 @@ def test_get_user_not_found(self): self._init_global_admin(get_multi_response=get_multi_response) with self.assertRaises(exceptions.CTERAException) as error: admins.Administrators(self._global_admin).get(self._local_user_account.name) - self._global_admin.api.get_multi.assert_called_once_with( - '/administrators/' + self._local_user_account.name, mock.ANY) + ref = '/administrators/' + self._local_user_account.name + self._global_admin.api.get_multi.assert_called_once_with(ref, mock.ANY) expected_include = ['/' + attr for attr in admins.Administrators.default] actual_include = self._global_admin.api.get_multi.call_args[0][1] self.assertEqual(len(expected_include), len(actual_include)) for attr in expected_include: self.assertIn(attr, actual_include) - self.assertEqual('Could not find user', error.exception.message) + self.assertEqual(f'Object not found: {ref}', str(error.exception)) def test_modify_admin_user(self): current_user_object = self._get_admin_object( diff --git a/tests/ut/edge/test_array.py b/tests/ut/edge/test_array.py index f4b7e5ea..4f492cae 100644 --- a/tests/ut/edge/test_array.py +++ b/tests/ut/edge/test_array.py @@ -60,7 +60,7 @@ def test_add_array_with_members_failure(self): self._filer.api.add = mock.MagicMock(side_effect=expected_exception) with self.assertRaises(exceptions.CTERAException) as error: array.Array(self._filer).add(self._array_name, self._array_level, self._array_members) - self.assertEqual('Storage array creation failed.', error.exception.message) + self.assertEqual(f'Storage array creation failed: {self._array_name}', str(error.exception)) def test_delete_array_success(self): delete_response = 'Success' @@ -74,7 +74,7 @@ def test_delete_array_failure(self): self._filer.api.delete = mock.MagicMock(side_effect=expected_exception) with self.assertRaises(exceptions.CTERAException) as error: array.Array(self._filer).delete(self._array_name) - self.assertEqual('Storage array deletion failed.', error.exception.message) + self.assertEqual(f'Storage array deletion failed: /config/storage/arrays/{self._array_name}', str(error.exception)) def test_delete_all(self): self.patch_call("cterasdk.edge.array.Array.delete") diff --git a/tests/ut/edge/test_backup.py b/tests/ut/edge/test_backup.py index 207e89e8..2a3057ef 100644 --- a/tests/ut/edge/test_backup.py +++ b/tests/ut/edge/test_backup.py @@ -57,7 +57,7 @@ def test_attach_incorrect_passphrase(self): self._filer.tasks.wait = mock.MagicMock(return_value=TestEdgeBackup._get_attach_response(backup.AttachRC.CheckCodeInCorrect)) with self.assertRaises(backup.IncorrectPassphrase) as error: backup.Backup(self._filer).configure() - self.assertEqual('Incorrect passphrase', error.exception.message) + self.assertEqual('Incorrect passphrase', str(error.exception)) def test_attach_clocks_out_of_sync(self): self._init_filer() @@ -65,7 +65,7 @@ def test_attach_clocks_out_of_sync(self): self._filer.tasks.wait = mock.MagicMock(return_value=TestEdgeBackup._get_attach_response(backup.AttachRC.ClocksOutOfSync)) with self.assertRaises(backup.ClocksOutOfSync) as error: backup.Backup(self._filer).configure() - self.assertEqual('Clocks are out of sync', error.exception.message) + self.assertEqual('Clocks are out of sync', str(error.exception)) def test_attach_internal_server_error(self): self._init_filer() @@ -73,7 +73,7 @@ def test_attach_internal_server_error(self): self._filer.tasks.wait = mock.MagicMock(return_value=TestEdgeBackup._get_attach_response(backup.AttachRC.InternalServerError)) with self.assertRaises(exceptions.CTERAException) as error: backup.Backup(self._filer).configure() - self.assertEqual('Failed to attach to backup folder', error.exception.message) + self.assertEqual(f'Failed to attach to backup folder (rc={backup.AttachRC.InternalServerError}).', str(error.exception)) def test_attach_permission_denied(self): self._init_filer() @@ -81,15 +81,16 @@ def test_attach_permission_denied(self): self._filer.tasks.wait = mock.MagicMock(return_value=TestEdgeBackup._get_attach_response(backup.AttachRC.PermissionDenied)) with self.assertRaises(exceptions.CTERAException) as error: backup.Backup(self._filer).configure() - self.assertEqual('Failed to attach to backup folder', error.exception.message) + self.assertEqual(f'Failed to attach to backup folder (rc={backup.AttachRC.PermissionDenied}).', str(error.exception)) def test_attach_unknown_error(self): self._init_filer() self._filer.api.execute = mock.MagicMock(side_effect=TestEdgeBackup._mock_execute) - self._filer.tasks.wait = mock.MagicMock(return_value=TestEdgeBackup._get_attach_response('Unknown attach rc')) + attach_response_code = 'Unknown attach rc' + self._filer.tasks.wait = mock.MagicMock(return_value=TestEdgeBackup._get_attach_response(attach_response_code)) with self.assertRaises(exceptions.CTERAException) as error: backup.Backup(self._filer).configure() - self.assertEqual('Failed to attach to backup folder', error.exception.message) + self.assertEqual(f'Failed to attach to backup folder (rc={attach_response_code}).', str(error.exception)) @staticmethod def _mock_get_no_backup_settings(path): diff --git a/tests/ut/edge/test_cache.py b/tests/ut/edge/test_cache.py index fa3e9e34..00af3d6d 100644 --- a/tests/ut/edge/test_cache.py +++ b/tests/ut/edge/test_cache.py @@ -64,7 +64,7 @@ def test_pin_invalid_root_directory(self): with self.assertRaises(exceptions.CTERAException) as error: cache.Cache(self._filer).pin(self._pin_invalid_folder_path) self._filer.api.get.assert_called_once_with('/config/cloudsync/cloudExtender/selectedFolders') - self.assertEqual('Invalid root directory', error.exception.message) + self.assertEqual(f"Invalid root directory: {self._pin_invalid_folder_path.split('/')[0]}.", str(error.exception)) def test_pin_exclude_subfolder(self): get_response = self._create_dir_tree(self._pin_valid_folder_path, True) @@ -135,7 +135,7 @@ def test_pin_recursive_invalid_root_directory(self): with self.assertRaises(exceptions.CTERAException) as error: cache.Cache(self._filer).pin_recursive(self._pin_invalid_folder_path) self._filer.api.get.assert_called_once_with('/config/cloudsync/cloudExtender/selectedFolders') - self.assertEqual('Invalid root directory', error.exception.message) + self.assertEqual(f"Invalid root directory: {self._pin_invalid_folder_path.split('/')[0]}.", str(error.exception)) def test_unpin_recursive(self): get_response = self._create_dir_tree(self._pin_valid_folder_path, True) @@ -155,7 +155,7 @@ def test_unpin_recursive_invalid_root_directory(self): with self.assertRaises(exceptions.CTERAException) as error: cache.Cache(self._filer).unpin_recursive(self._pin_invalid_folder_path) self._filer.api.get.assert_called_once_with('/config/cloudsync/cloudExtender/selectedFolders') - self.assertEqual('Invalid root directory', error.exception.message) + self.assertEqual(f"Invalid root directory: {self._pin_invalid_folder_path.split('/')[0]}.", str(error.exception)) def _get_dir_entry(self, name, include): param = Object() diff --git a/tests/ut/edge/test_directory_service.py b/tests/ut/edge/test_directory_service.py index ac661d2f..d07e8ed9 100644 --- a/tests/ut/edge/test_directory_service.py +++ b/tests/ut/edge/test_directory_service.py @@ -159,7 +159,7 @@ def test_set_advanced_mapping_raise(self): self._init_filer(get_response=1) with self.assertRaises(exceptions.CTERAException) as error: directoryservice.DirectoryService(self._filer).set_advanced_mapping([]) - self.assertEqual('Failed to configure advanced mapping. Not connected to directory services.', error.exception.message) + self.assertEqual('Failed to configure advanced mapping. Not connected to directory services.', str(error.exception)) def test_domains(self): self._init_filer(execute_response=TestEdgeDirectoryService._create_get_domains_response(self._domain_flat_name)) diff --git a/tests/ut/edge/test_firmware.py b/tests/ut/edge/test_firmware.py index d6681c3e..24391413 100644 --- a/tests/ut/edge/test_firmware.py +++ b/tests/ut/edge/test_firmware.py @@ -52,8 +52,7 @@ def test_upgrade_upload_failed(self): ) ) self._filer.api.get.assert_not_called() - self.assertEqual(error.exception.message, 'Failed to upload the new firmware') - self.assertEqual(error.exception.path, TestEdgeFirmware._file_path) + self.assertEqual(str(error.exception), f'Failed to upload firmware: {TestEdgeFirmware._file_path}') def test_upgrade_process_failed(self): self._init_filer( @@ -70,7 +69,8 @@ def test_upgrade_process_failed(self): ) ) self._filer.api.get.assert_called_with(TestEdgeFirmware._task_pointer) - self.assertEqual(error.exception.message, 'Filer failed to receive the new firmware - Failure') + self.assertEqual(str(error.exception), + f'An error occurred during firmware upgrade. Status: {TestEdgeFirmware._status_msg_failure}') @staticmethod def _get_upgrade_cmd_response(rc): diff --git a/tests/ut/edge/test_ftp.py b/tests/ut/edge/test_ftp.py index 132a23bf..6295dbb3 100644 --- a/tests/ut/edge/test_ftp.py +++ b/tests/ut/edge/test_ftp.py @@ -52,7 +52,7 @@ def test_modify_raise(self): self._init_filer(get_response=param) with self.assertRaises(exceptions.CTERAException) as error: ftp.FTP(self._filer).modify() - self.assertEqual('FTP must be enabled in order to modify its configuration', error.exception.message) + self.assertEqual('FTP must be enabled in order to modify its configuration', str(error.exception)) @staticmethod def _get_ftp_configuration_response(allow_anonymous_ftp=None, anonymous_download_limit=None, anonymous_ftp_folder=None, diff --git a/tests/ut/edge/test_licenses.py b/tests/ut/edge/test_licenses.py index d28112cf..2373a6be 100644 --- a/tests/ut/edge/test_licenses.py +++ b/tests/ut/edge/test_licenses.py @@ -35,7 +35,7 @@ def test_apply_license(self): def test_apply_license_raise_input_error(self): with self.assertRaises(exceptions.InputError) as error: licenses.Licenses(self._filer).apply('Expected Failure') - self.assertEqual('Invalid license type', error.exception.message) + self.assertEqual('Invalid license type', error.exception.args[1]) def test_get_local_license(self): get_response = [self._local_license] diff --git a/tests/ut/edge/test_login.py b/tests/ut/edge/test_login.py index ecfb067d..77b46a9b 100644 --- a/tests/ut/edge/test_login.py +++ b/tests/ut/edge/test_login.py @@ -28,12 +28,12 @@ def test_login_failure(self): self._filer.api.form_data = mock.MagicMock(side_effect=expected_exception) with self.assertRaises(exceptions.CTERAException) as error: login.Login(self._filer).login(self._username, self._password) - self.assertEqual(error_message, error.exception.message) + self.assertEqual(error_message, str(error.exception)) def test_login_required(self): - with self.assertRaises(exceptions.NotLoggedIn) as error: + with self.assertRaises(exceptions.session.NotLoggedIn) as error: self._filer.api.get('/config/device') - self.assertEqual('Not logged in.', error.exception.message) + self.assertEqual('Authentication error: Not logged in.', str(error.exception)) def test_logout_success_after_login_success(self): self._init_filer() @@ -62,4 +62,4 @@ def test_logout_failure_after_login_success(self): with self.assertRaises(exceptions.CTERAException) as error: login.Login(self._filer).logout() self._filer.api.form_data.assert_called_once_with('/logout', {'foo': 'bar'}) - self.assertEqual(error_message, error.exception.message) + self.assertEqual(error_message, str(error.exception)) diff --git a/tests/ut/edge/test_network.py b/tests/ut/edge/test_network.py index dcea7b1d..f04f5e0b 100644 --- a/tests/ut/edge/test_network.py +++ b/tests/ut/edge/test_network.py @@ -261,7 +261,7 @@ def test_add_static_routes_raise(self): self._static_routes[0].GwIP, self._static_routes[0].DestIpMask.replace("_", "/") ) - self.assertEqual('Static route creation failed', error.exception.message) + self.assertEqual('Static route creation failed', str(error.exception)) def test_get_all_static_routes(self): get_response = 'Success' @@ -283,7 +283,7 @@ def test_remove_static_route_raise(self): self._filer.api.delete = mock.MagicMock(side_effect=expected_exception) with self.assertRaises(exceptions.CTERAException) as error: network.Network(self._filer).routes.remove(self._static_routes[0].DestIpMask.replace("_", "/")) - self.assertEqual('Static route deletion failed', error.exception.message) + self.assertEqual('Static route deletion failed', str(error.exception)) def test_clean_all_static_routes_success(self): expected_exception = 'Success' @@ -303,7 +303,7 @@ def test_clean_all_static_routes_raise(self): with self.assertRaises(exceptions.CTERAException) as error: network.Network(self._filer).routes.clear() - self.assertEqual('Failed to clear static routes', error.exception.message) + self.assertEqual('Failed to clear static routes', str(error.exception)) def test_get_proxy_config(self): get_response = 'Success' diff --git a/tests/ut/edge/test_nfs.py b/tests/ut/edge/test_nfs.py index 47f23a9c..6b7ff406 100644 --- a/tests/ut/edge/test_nfs.py +++ b/tests/ut/edge/test_nfs.py @@ -57,7 +57,7 @@ def test_modify_raise(self): self._init_filer(get_response=param) with self.assertRaises(exceptions.CTERAException) as error: nfs.NFS(self._filer).modify() - self.assertEqual('NFS must be enabled in order to modify its configuration', error.exception.message) + self.assertEqual('NFS must be enabled in order to modify its configuration', str(error.exception)) def test_modify_all_parameters(self): """Test modifying all NFS parameters""" @@ -88,7 +88,7 @@ def test_modify_krb5_without_nfsv4(self): with self.assertRaises(exceptions.CTERAException) as error: nfs.NFS(self._filer).modify(krb5_enabled=True) - self.assertEqual('NFSv4 must be enabled in order to enable Kerberos', error.exception.message) + self.assertEqual('NFSv4 must be enabled in order to enable Kerberos', str(error.exception)) @staticmethod def _get_nfs_configuration_response( diff --git a/tests/ut/edge/test_ransom_protect.py b/tests/ut/edge/test_ransom_protect.py index 006ab869..2eb84a5b 100644 --- a/tests/ut/edge/test_ransom_protect.py +++ b/tests/ut/edge/test_ransom_protect.py @@ -69,7 +69,7 @@ def test_modify_raise(self): self._init_filer(get_response=get_response) with self.assertRaises(exceptions.CTERAException) as error: ransom_protect.RansomProtect(self._filer).modify() - self.assertEqual('Ransom Protect must be enabled to modify its configuration', error.exception.message) + self.assertEqual('Ransom Protect must be enabled to modify its configuration', str(error.exception)) @staticmethod def _get_ransom_protect_config(block_users=None, detection_threshold=None, detection_interval=None): diff --git a/tests/ut/edge/test_rsync.py b/tests/ut/edge/test_rsync.py index 6464606a..14bdd94e 100644 --- a/tests/ut/edge/test_rsync.py +++ b/tests/ut/edge/test_rsync.py @@ -54,7 +54,7 @@ def test_modify_raise(self): self._init_filer(get_response=param) with self.assertRaises(exceptions.CTERAException) as error: rsync.RSync(self._filer).modify() - self.assertEqual('RSync must be enabled in order to modify its configuration', error.exception.message) + self.assertEqual('RSync must be enabled in order to modify its configuration', str(error.exception)) @staticmethod def _get_rsync_configuration_response(port=None, max_connections=None): diff --git a/tests/ut/edge/test_services.py b/tests/ut/edge/test_services.py index ff5b0263..938427f8 100644 --- a/tests/ut/edge/test_services.py +++ b/tests/ut/edge/test_services.py @@ -115,12 +115,13 @@ def test_connect_require_sso_failure(self): expected_param = self._get_is_web_sso_param(False) actual_param = self._filer.api.execute.call_args[0][2] self._assert_equal_objects(actual_param, expected_param) - self.assertEqual('Connection failed. You must activate this Edge Filer using an activation code.', error.exception.message) + self.assertEqual('Connection failed. You must activate this Edge Filer using an activation code.', str(error.exception)) def test_connect_default_args_task_failure(self): self._init_filer() self._filer.api.execute = mock.MagicMock(side_effect=TestEdgeServices._mock_execute_connect_ok) - self._filer.tasks.wait = mock.MagicMock(side_effect=TestEdgeServices._get_task_error()) + task_error_side_effect = TestEdgeServices._get_task_error() + self._filer.tasks.wait = mock.MagicMock(side_effect=task_error_side_effect) self._filer.network.tcp_connect = mock.MagicMock(return_value=TCPConnectResult(self._server, self._cttp_port, True)) with self.assertRaises(exceptions.CTERAException) as error: @@ -142,7 +143,7 @@ def test_connect_default_args_task_failure(self): expected_param = self._get_attach_and_save_param(False, use_activation_code=False) actual_param = self._filer.api.execute.call_args_list[1][0][2] # Access attachAndSave call param self._assert_equal_objects(actual_param, expected_param) - self.assertEqual('Connection failed', error.exception.message) + self.assertEqual(f'Connection failed. Reason: {task_error_side_effect.task.description}', str(error.exception)) def test_reconnect(self): self._init_filer() diff --git a/tests/ut/edge/test_shares.py b/tests/ut/edge/test_shares.py index 07d5c959..5c5ea6dd 100644 --- a/tests/ut/edge/test_shares.py +++ b/tests/ut/edge/test_shares.py @@ -108,12 +108,12 @@ def test_modify_nfs_v3_share_success(self): def test_add_cifs_share_invalid_principal_type(self): with self.assertRaises(exceptions.InputError) as error: ShareAccessControlEntry(principal_type='Expected Failure', name='Everyone', perm=FileAccessMode.RO) - self.assertEqual('Invalid principal type', error.exception.message) + self.assertEqual('Invalid principal type', error.exception.args[1]) def test_add_cifs_share_invalid_permission(self): with self.assertRaises(exceptions.InputError) as error: ShareAccessControlEntry(principal_type=PrincipalType.LG, name='Everyone', perm='Expected Failure') - self.assertEqual('Invalid permissions', error.exception.message) + self.assertEqual('Invalid permissions', error.exception.args[1]) def test_add_share_failure(self): execute_response = self._get_list_physical_folders_response_object() @@ -132,7 +132,7 @@ def test_add_share_failure(self): actual_param = self._filer.api.add.call_args[0][1] self._assert_equal_objects(actual_param, expected_param) - self.assertEqual('Share creation failed', error.exception.message) + self.assertEqual(f'Share creation failed: {self._share_name}', str(error.exception)) def test_list_physical_folders_input_error(self): execute_response = [] @@ -145,7 +145,7 @@ def test_list_physical_folders_input_error(self): actual_param = self._filer.api.execute.call_args[0][2] self._assert_equal_objects(actual_param, expected_param) - self.assertEqual('Invalid root directory.', error.exception.message) + self.assertEqual('Invalid root directory.', error.exception.args[1]) def test_set_share_winacls(self): put_response = 'Success' @@ -169,7 +169,7 @@ def test_block_files_invalid_share_access_type(self): with self.assertRaises(exceptions.CTERAException) as error: shares.Shares(self._filer).block_files(self._share_name, self._share_block_files) self._filer.api.get.assert_called_once_with('/config/fileservices/share/' + self._share_name) - self.assertEqual('Cannot block file types on non Windows-ACL enabled shares', error.exception.message) + self.assertEqual('Cannot block file types on non Windows-ACL enabled shares.', str(error.exception)) def test_delete_share_success(self): self._init_filer() @@ -180,7 +180,7 @@ def test_delete_share_failure(self): self._filer.api.delete = mock.MagicMock(side_effect=exceptions.CTERAException()) with self.assertRaises(exceptions.CTERAException) as error: shares.Shares(self._filer).delete(self._share_name) - self.assertEqual('Share deletion failed', error.exception.message) + self.assertEqual(f'Share deletion failed: /config/fileservices/share/{self._share_name}', str(error.exception)) def test_modify(self): updated_comment = 'Test Modify' diff --git a/tests/ut/edge/test_shell.py b/tests/ut/edge/test_shell.py index b062abf5..aabd76ae 100644 --- a/tests/ut/edge/test_shell.py +++ b/tests/ut/edge/test_shell.py @@ -30,7 +30,7 @@ def test_run_shell_command_task_error(self): with self.assertRaises(exceptions.CTERAException) as error: shell.Shell(self._filer).run_command(self._shell_command) self._filer.tasks.wait.assert_called_once_with(self._task_id) - self.assertEqual('An error occurred while executing task', error.exception.message) + self.assertEqual('An error occurred while executing task', str(error.exception)) def _get_task_manager_result_object(self): task_param = Object() diff --git a/tests/ut/edge/test_smb.py b/tests/ut/edge/test_smb.py index 2684de99..f0e4dd7b 100644 --- a/tests/ut/edge/test_smb.py +++ b/tests/ut/edge/test_smb.py @@ -55,14 +55,14 @@ def test_required_packet_signing(self): def test_set_packet_signing_raise_input_error(self): with self.assertRaises(exceptions.InputError) as error: smb.SMB(self._filer).set_packet_signing('Invalid argument') - self.assertEqual('Invalid packet signing option', error.exception.message) + self.assertEqual('Invalid packet signing option', error.exception.args[1]) def test_set_packet_signing_raise_error(self): expected_exception = exceptions.CTERAException() self._filer.api.put = mock.MagicMock(side_effect=expected_exception) with self.assertRaises(exceptions.CTERAException) as error: smb.SMB(self._filer).set_packet_signing(CIFSPacketSigning.Disabled) - self.assertEqual('Invalid packet signing co', error.exception.message) + self.assertEqual(f'Invalid packet SMB signing configuration: {CIFSPacketSigning.Disabled}', str(error.exception)) def test_get_configuration(self): self._init_filer(get_response=TestEdgeSMB._get_cifs_configuration_response()) @@ -89,7 +89,7 @@ def test_modify_raise(self): expected_param = TestEdgeSMB._get_cifs_configuration_response(CIFSPacketSigning.Required, 20, True, False, False) actual_param = self._filer.api.put.call_args[0][1] self._assert_equal_objects(actual_param, expected_param) - self.assertEqual('Failed to update SMB configuration.', error.exception.message) + self.assertEqual("An error occurred while trying to modify the SMB server's configuration.", str(error.exception)) def test_modify_smb_disabled_raise(self): param = Object() @@ -97,7 +97,7 @@ def test_modify_smb_disabled_raise(self): self._init_filer(get_response=param) with self.assertRaises(exceptions.CTERAException) as error: smb.SMB(self._filer).modify(CIFSPacketSigning.Required) - self.assertEqual('SMB must be enabled in order to modify its configuration', error.exception.message) + self.assertEqual('SMB must be enabled in order to modify its configuration', str(error.exception)) @staticmethod def _get_cifs_configuration_response(packet_signing=None, idle_disconnect_time=None, compatibility_mode=None, diff --git a/tests/ut/edge/test_support.py b/tests/ut/edge/test_support.py index 9f0d4d0f..78e338d7 100644 --- a/tests/ut/edge/test_support.py +++ b/tests/ut/edge/test_support.py @@ -26,7 +26,7 @@ def test_set_debug_level_input_error(self): self._init_filer() with self.assertRaises(exceptions.InputError) as error: support.Support(self._filer).set_debug_level(*['Expected Failure']) - self.assertEqual('Invalid debug level', error.exception.message) + self.assertEqual('Invalid debug level', error.exception.args[1]) def test_get_support_report(self): cterasdk.settings.io.downloads = '~' diff --git a/tests/ut/edge/test_syslog.py b/tests/ut/edge/test_syslog.py index ff7c25fa..a74d1b0f 100644 --- a/tests/ut/edge/test_syslog.py +++ b/tests/ut/edge/test_syslog.py @@ -83,7 +83,7 @@ def test_modify_raise(self): self._init_filer(get_response=param) with self.assertRaises(exceptions.CTERAException) as error: syslog.Syslog(self._filer).modify() - self.assertEqual('Syslog configuration cannot be modified when disabled', error.exception.message) + self.assertEqual('Syslog configuration cannot be modified when disabled', str(error.exception)) def _get_syslog_object(self, server=None, port=None, protocol=None, min_severity=None): syslog_param = Object() diff --git a/tests/ut/edge/test_telnet.py b/tests/ut/edge/test_telnet.py index 0bd49f14..1cac1761 100644 --- a/tests/ut/edge/test_telnet.py +++ b/tests/ut/edge/test_telnet.py @@ -43,7 +43,7 @@ def test_enable_telnet_raise(self): actual_param = self._filer.api.execute.call_args[0][2] self._assert_equal_objects(actual_param, expected_param) - self.assertEqual('Failed enabling telnet access', error.exception.message) + self.assertEqual(f'Failed to enable telnet. Reason: {execute_response}', str(error.exception)) def test_disable_telnet(self): self._init_filer() diff --git a/tests/ut/edge/test_users.py b/tests/ut/edge/test_users.py index 8de2fc37..cfce6f26 100644 --- a/tests/ut/edge/test_users.py +++ b/tests/ut/edge/test_users.py @@ -87,7 +87,7 @@ def test_add_user_raise(self): self._filer.api.add = mock.MagicMock(side_effect=expected_exception) with self.assertRaises(exceptions.CTERAException) as error: users.Users(self._filer).add(self._username, self._password) - self.assertEqual('User creation failed', error.exception.message) + self.assertEqual(f'User creation failed: {self._username}', str(error.exception)) def test_modify_user(self): get_response = Object() @@ -105,11 +105,12 @@ def test_modify_user(self): self.assertEqual(ret, put_response) def test_modify_user_not_found(self): + ref = f'/config/auth/users/{self._username}' self._filer.api.get = mock.MagicMock(side_effect=exceptions.CTERAException()) with self.assertRaises(exceptions.CTERAException) as error: users.Users(self._filer).modify(self._username) - self._filer.api.get.assert_called_once_with('/config/auth/users/' + self._username) - self.assertEqual('Failed to get the user', error.exception.message) + self._filer.api.get.assert_called_once_with(ref) + self.assertEqual(f'User not found: {ref}', str(error.exception)) def test_modify_user_update_failed(self): get_response = Object() @@ -119,12 +120,13 @@ def test_modify_user_update_failed(self): with self.assertRaises(exceptions.CTERAException) as error: users.Users(self._filer).modify(self._username, self._password, self._full_name, self._email, self._uid) - self._filer.api.get.assert_called_once_with('/config/auth/users/' + self._username) + ref = f'/config/auth/users/{self._username}' + self._filer.api.get.assert_called_once_with(ref) self._filer.api.put.assert_called_once_with('/config/auth/users/' + self._username, mock.ANY) expected_param = self._get_user_object(self._full_name, self._email, self._uid) actual_param = self._filer.api.put.call_args[0][1] self._assert_equal_objects(actual_param, expected_param) - self.assertEqual('Failed to modify user', error.exception.message) + self.assertEqual(f'User modification failed: {ref}', str(error.exception)) def test_delete_user(self): user = self._get_user_object() @@ -140,7 +142,7 @@ def test_delete_user_raise(self): self._filer.api.delete = mock.MagicMock(side_effect=expected_exception) with self.assertRaises(exceptions.CTERAException) as error: users.Users(self._filer).delete(self._username) - self.assertEqual('User deletion failed', error.exception.message) + self.assertEqual(f'User deletion failed: /config/auth/users/{self._username}', str(error.exception)) def _get_user_object(self, full_name=None, email=None, uid=None): o = Object() diff --git a/tests/ut/edge/test_volumes.py b/tests/ut/edge/test_volumes.py index 93e84a98..82e0f24f 100644 --- a/tests/ut/edge/test_volumes.py +++ b/tests/ut/edge/test_volumes.py @@ -98,7 +98,7 @@ def test_add_volume_no_devices(self): mock.call('/status/storage/disks') ] ) - self.assertEqual('Could not find any drives or arrays', error.exception.message) + self.assertEqual('Could not find any drives or arrays', str(error.exception)) def test_add_volume_invalid_device_name(self): self._init_filer() @@ -111,12 +111,12 @@ def test_add_volume_invalid_device_name(self): mock.call('/status/storage/disks') ] ) - self.assertEqual('Invalid device name', error.exception.message) + self.assertEqual('Invalid device name', error.exception.args[1]) def test_add_volume_must_specify_device_name(self): self._init_filer() self._filer.api.get = mock.MagicMock(side_effect=TestEdgeVolumes._mock_no_arrays_multiple_drive) - with self.assertRaises(exceptions.CTERAException) as error: + with self.assertRaises(exceptions.InputError) as error: volumes.Volumes(self._filer).add(self._volume_1_name) self._filer.api.get.assert_has_calls( [ @@ -124,7 +124,7 @@ def test_add_volume_must_specify_device_name(self): mock.call('/status/storage/disks') ] ) - self.assertEqual('You must specify a drive or an array name', error.exception.message) + self.assertEqual('You must specify a drive or an array name', error.exception.args[1]) def test_add_volume_with_device_success(self): add_response = 'Success' @@ -161,7 +161,7 @@ def test_add_volume_exceeding_drive_size(self): mock.call('/status/storage/disks') ] ) - self.assertEqual('You cannot exceed the available storage capacity', error.exception.message) + self.assertEqual('You cannot exceed the available storage capacity', error.exception.args[1]) def test_delete_volume_success(self): delete_response = 'Success' @@ -181,8 +181,9 @@ def test_delete_volume_raise(self): with self.assertRaises(exceptions.CTERAException) as error: volumes.Volumes(self._filer).delete(self._volume_1_name) self._filer.tasks.by_name.assert_called_once_with(' '.join(['Mounting', self._volume_1_name, 'file system'])) - self._filer.api.delete.assert_called_once_with('/config/storage/volumes/' + self._volume_1_name) - self.assertEqual('Volume deletion failed', error.exception.message) + ref = f'/config/storage/volumes/{self._volume_1_name}' + self._filer.api.delete.assert_called_once_with(ref) + self.assertEqual(f'Volume deletion failed: {ref}', str(error.exception)) def test_delete_all_volume_success(self): delete_response = 'Success' @@ -220,7 +221,7 @@ def test_modify_volume_not_found(self): with self.assertRaises(exceptions.CTERAException) as error: volumes.Volumes(self._filer).modify(self._volume_1_name, 9999) self._filer.api.get.assert_called_once_with('/config/storage/volumes/' + self._volume_1_name) - self.assertEqual('Failed to get the volume', error.exception.message) + self.assertEqual(f'Volume not found: {self._volume_1_name}', str(error.exception)) @staticmethod def _get_volume_response(name, size): From 2614e7bdde0dade0568d7b85da48d11c913f92d0 Mon Sep 17 00:00:00 2001 From: Saimon Michelson Date: Fri, 6 Jun 2025 12:08:10 -0400 Subject: [PATCH 03/15] add module for backup related exceptions --- cterasdk/exceptions/__init__.py | 1 + cterasdk/exceptions/backup.py | 29 +++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 cterasdk/exceptions/backup.py diff --git a/cterasdk/exceptions/__init__.py b/cterasdk/exceptions/__init__.py index 604e7847..10e2b1bf 100644 --- a/cterasdk/exceptions/__init__.py +++ b/cterasdk/exceptions/__init__.py @@ -45,6 +45,7 @@ class UserConsentError(CTERAException): from . import ( + backup, direct, io, notifications, diff --git a/cterasdk/exceptions/backup.py b/cterasdk/exceptions/backup.py new file mode 100644 index 00000000..9d52e920 --- /dev/null +++ b/cterasdk/exceptions/backup.py @@ -0,0 +1,29 @@ +from . import CTERAException + + +class NotFound(CTERAException): + """ Not found exception """ + + +class AttachEncrypted(CTERAException): + """ Attach Encrypted exception """ + + def __init__(self, encryptionMode, encryptedFolderKey, passPhraseSalt): + super().__init__() + self.encryptionMode = encryptionMode + self.encryptedFolderKey = encryptedFolderKey + self.passPhraseSalt = passPhraseSalt + + +class IncorrectPassphrase(CTERAException): + """ Incorrect Passphrase exception """ + + def __init__(self): + super().__init__('Incorrect passphrase') + + +class ClocksOutOfSync(CTERAException): + """ Clocks Out of Sync exception """ + + def __init__(self): + super().__init__('Clocks are out of sync') \ No newline at end of file From 6ceb7939d8b5766e157e535b80f8983a519f205f Mon Sep 17 00:00:00 2001 From: Saimon Michelson Date: Fri, 6 Jun 2025 12:28:43 -0400 Subject: [PATCH 04/15] update to pass flake8 --- cterasdk/asynchronous/core/iterator.py | 3 +++ cterasdk/core/buckets.py | 6 ++---- cterasdk/core/cli.py | 4 ++-- cterasdk/core/cloudfs.py | 5 ++--- cterasdk/core/connection.py | 3 +-- cterasdk/core/plans.py | 2 +- cterasdk/core/servers.py | 4 ++-- cterasdk/core/users.py | 2 +- cterasdk/direct/__init__.py | 2 +- cterasdk/edge/array.py | 2 +- cterasdk/edge/cli.py | 4 ++-- cterasdk/edge/logs.py | 6 ++---- cterasdk/edge/network.py | 3 +-- cterasdk/edge/shares.py | 8 +++----- cterasdk/edge/volumes.py | 6 ++---- cterasdk/exceptions/__init__.py | 7 ++----- cterasdk/exceptions/backup.py | 2 +- cterasdk/exceptions/notifications.py | 2 +- cterasdk/exceptions/session.py | 2 +- tests/ut/aio/direct/test_get_object.py | 2 +- tests/ut/core/admin/test_devices.py | 2 +- tests/ut/edge/test_volumes.py | 2 +- 22 files changed, 34 insertions(+), 45 deletions(-) diff --git a/cterasdk/asynchronous/core/iterator.py b/cterasdk/asynchronous/core/iterator.py index c29d2fb3..40fb7d8d 100644 --- a/cterasdk/asynchronous/core/iterator.py +++ b/cterasdk/asynchronous/core/iterator.py @@ -3,6 +3,9 @@ from abc import abstractmethod +logger = logging.getLogger('cterasdk.common') + + class BaseAsyncIterator: """Abstract Asynchronous Iterator""" diff --git a/cterasdk/core/buckets.py b/cterasdk/core/buckets.py index 6d7e4989..ea840ed6 100644 --- a/cterasdk/core/buckets.py +++ b/cterasdk/core/buckets.py @@ -55,11 +55,9 @@ def add(self, name, bucket, read_only=False, dedicated_to=None): param.dedicated = bool(dedicated_to) param.dedicatedPortal = self._get_tenant_base_object_ref(dedicated_to) if dedicated_to else None - logger.info('Adding bucket. %s', - {'name': name, 'bucket': bucket.bucket, 'type': bucket.__class__.__name__}) + logger.info('Adding bucket. %s', {'name': name, 'bucket': bucket.bucket, 'type': bucket.__class__.__name__}) response = self._core.api.add('/locations', param) - logger.info('Bucket added. %s', - {'name': name, 'bucket': bucket.bucket, 'type': bucket.__class__.__name__}) + logger.info('Bucket added. %s', {'name': name, 'bucket': bucket.bucket, 'type': bucket.__class__.__name__}) return response def modify(self, current_name, new_name=None, read_only=None, dedicated_to=None, verify_ssl=None): diff --git a/cterasdk/core/cli.py b/cterasdk/core/cli.py index 4f461b0a..6cc8f492 100644 --- a/cterasdk/core/cli.py +++ b/cterasdk/core/cli.py @@ -17,8 +17,8 @@ def run_command(self, cli_command): :return str: The response of the Portal """ logger.warning('Usage of the CLI module is discouraged. ' - 'Review available modules to determine if there are existing ones that ' - 'support this action.') + 'Review available modules to determine if there are existing ones that ' + 'support this action.') logger.info("Executing CLI command. %s", {'cli_command': cli_command}) response = self._core.api.execute('', 'debugCmd', cli_command) logger.info("CLI command executed. %s", {'cli_command': cli_command}) diff --git a/cterasdk/core/cloudfs.py b/cterasdk/core/cloudfs.py index 7084d634..b0b2424a 100644 --- a/cterasdk/core/cloudfs.py +++ b/cterasdk/core/cloudfs.py @@ -120,7 +120,7 @@ def modify(self, current_name, new_name): return response except CTERAException as error: logger.error('Folder group modification failed: %s', current_name) - raise CTERAException(f'Folder group modification failed: {ref}') + raise CTERAException(f'Folder group modification failed: {ref}') from error def delete(self, name): """ @@ -291,8 +291,7 @@ def delete(self, name, owner, *, permanently=False): :param bool,optional permanently: Delete permanently """ cloudfolder = self.find(name, owner, include=['uid', 'isDeleted']) - logger.info('Deleting cloud drive folder. %s', - {'name': name, 'owner': str(owner), 'permanently': permanently}) + logger.info('Deleting cloud drive folder. %s', {'name': name, 'owner': str(owner), 'permanently': permanently}) if permanently: return self._core.api.execute(f'/objs/{cloudfolder.uid}', 'deleteFolderPermanently') if not cloudfolder.isDeleted: diff --git a/cterasdk/core/connection.py b/cterasdk/core/connection.py index 85ff3d3d..f00e5106 100644 --- a/cterasdk/core/connection.py +++ b/cterasdk/core/connection.py @@ -9,6 +9,5 @@ def test(Portal): tcp_connect(Portal.host(), Portal.port()) logger.debug('Trying to obtain Portal public info.') response = Portal.public_info() - logger.debug('Successfully obtained Portal public info. %s', - {'version': response.version, 'name': response.name}) + logger.debug('Successfully obtained Portal public info. %s', {'version': response.version, 'name': response.name}) return response diff --git a/cterasdk/core/plans.py b/cterasdk/core/plans.py index 1b4a6bf0..d21cf35e 100644 --- a/cterasdk/core/plans.py +++ b/cterasdk/core/plans.py @@ -129,7 +129,7 @@ def modify(self, name, services=None, retention=None, quotas=None, apply_changes return response except CTERAException as error: logger.error("Plan modification failed: %s", ref) - raise CTERAException(f'Plan modification failed: {ref}') + raise CTERAException(f'Plan modification failed: {ref}') from error @staticmethod def _assign_services(plan, services): diff --git a/cterasdk/core/servers.py b/cterasdk/core/servers.py index 8dd41801..ddebbd4b 100644 --- a/cterasdk/core/servers.py +++ b/cterasdk/core/servers.py @@ -26,7 +26,7 @@ def _get_entire_object(self, server): try: return self._core.api.get(ref) except CTERAException as error: - raise CTERAException(f'Server not found: {ref}') + raise CTERAException(f'Server not found: {ref}') from error def get(self, name, include=None): """ @@ -98,7 +98,7 @@ def modify(self, name, server_name=None, app=None, preview=None, enable_public_i return response except CTERAException as error: logger.error("Server modification failed: %s", ref) - raise CTERAException(f'Server modification failed: {ref}') + raise CTERAException(f'Server modification failed: {ref}') from error class Tasks(BaseCommand): diff --git a/cterasdk/core/users.py b/cterasdk/core/users.py index 407b8bb1..fa6e0f7a 100644 --- a/cterasdk/core/users.py +++ b/cterasdk/core/users.py @@ -151,7 +151,7 @@ def modify(self, current_username, new_username=None, email=None, first_name=Non return response except CTERAException as error: logger.error('User modification failed: %s', ref) - raise CTERAException(f'User modification failed: {ref}') + raise CTERAException(f'User modification failed: {ref}') from error def apply_changes(self, wait=False): """ diff --git a/cterasdk/direct/__init__.py b/cterasdk/direct/__init__.py index 0f98ea58..52a5ed32 100644 --- a/cterasdk/direct/__init__.py +++ b/cterasdk/direct/__init__.py @@ -1 +1 @@ -from . import client # noqa: E402, F401 \ No newline at end of file +from . import client # noqa: E402, F401 diff --git a/cterasdk/edge/array.py b/cterasdk/edge/array.py index 78823450..bc29429f 100644 --- a/cterasdk/edge/array.py +++ b/cterasdk/edge/array.py @@ -39,7 +39,7 @@ def add(self, array_name, level, members=None): logger.info("Storage array created: %s/%s", ref, array_name) return response except CTERAException as error: - logger.error(f"Storage array creation failed: %s", array_name) + logger.error("Storage array creation failed: %s", array_name) raise CTERAException(f"Storage array creation failed: {array_name}") from error def delete(self, array_name): diff --git a/cterasdk/edge/cli.py b/cterasdk/edge/cli.py index 63c19c8b..86129567 100644 --- a/cterasdk/edge/cli.py +++ b/cterasdk/edge/cli.py @@ -17,8 +17,8 @@ def run_command(self, cli_command): :return str: Response """ logger.warning('Usage of the CLI module is discouraged. ' - 'Review available modules to determine if there are existing ones that ' - 'support this action.') + 'Review available modules to determine if there are existing ones that ' + 'support this action.') logger.info("Executing CLI command. %s", {'cli_command': cli_command}) response = self._edge.api.execute('/config/device', 'debugCmd', cli_command) logger.info("CLI command executed. %s", {'cli_command': cli_command}) diff --git a/cterasdk/edge/logs.py b/cterasdk/edge/logs.py index 2032d9ff..5d112ef9 100644 --- a/cterasdk/edge/logs.py +++ b/cterasdk/edge/logs.py @@ -30,11 +30,9 @@ def settings(self, retention, min_severity=None): log_config.LogKeepPeriod = retention if min_severity: log_config.minSeverity = min_severity - logger.info('Updating log settings. %s', - {'retention': retention, 'min_severity': log_config.minSeverity}) + logger.info('Updating log settings. %s', {'retention': retention, 'min_severity': log_config.minSeverity}) self._edge.api.put('/config/logging/general', log_config) - logger.info('Log settings updated. %s', - {'retention': retention, 'min_severity': log_config.minSeverity}) + logger.info('Log settings updated. %s', {'retention': retention, 'min_severity': log_config.minSeverity}) def logs(self, topic, include=None, minSeverity=enum.Severity.INFO): """ diff --git a/cterasdk/edge/network.py b/cterasdk/edge/network.py index ef801ca4..9ffe84e2 100644 --- a/cterasdk/edge/network.py +++ b/cterasdk/edge/network.py @@ -86,8 +86,7 @@ def set_static_nameserver(self, primary_dns_server, secondary_dns_server=None): self._edge.api.put('/config/network/ports/0/ip', ip) - logger.info('Nameserver settings updated. %s', - {'DNS1': primary_dns_server, 'DNS2': secondary_dns_server}) + logger.info('Nameserver settings updated. %s', {'DNS1': primary_dns_server, 'DNS2': secondary_dns_server}) def enable_dhcp(self): """ diff --git a/cterasdk/edge/shares.py b/cterasdk/edge/shares.py index 456105e7..e66b8cac 100644 --- a/cterasdk/edge/shares.py +++ b/cterasdk/edge/shares.py @@ -97,8 +97,7 @@ def set_share_winacls(self, name): :param str name: The share name """ - logger.error("Updating Windows file sharing access mode. %s", - {'share': name, 'access': enum.Acl.WindowsNT}) + logger.error("Updating Windows file sharing access mode. %s", {'share': name, 'access': enum.Acl.WindowsNT}) self._edge.api.put('/config/fileservices/share/' + name + '/access', enum.Acl.WindowsNT) def get_access_type(self, name): @@ -129,7 +128,7 @@ def block_files(self, name, extensions): share = self.get(name) Shares._validate_share_access(share) logger.error("Updating the list of blocked file extensions. %s", - {'share': name, 'extensions': extensions, 'access': enum.Acl.WindowsNT}) + {'share': name, 'extensions': extensions, 'access': enum.Acl.WindowsNT}) self._edge.api.put('/config/fileservices/share/' + share.name + '/screenedFileTypes', extensions) def set_acl(self, name, acl): @@ -426,8 +425,7 @@ def _validate_root_directory(self, name): response = self._edge.api.execute('/status/fileManager', 'listPhysicalFolders', param) for root in response: if root.fullpath == f'/{name}': - logger.debug("Found root directory. %s", - {'name': root.name, 'type': root.type, 'fullpath': root.fullpath}) + logger.debug("Found root directory. %s", {'name': root.name, 'type': root.type, 'fullpath': root.fullpath}) return name logger.error("Could not find root directory. %s", {'name': name}) diff --git a/cterasdk/edge/volumes.py b/cterasdk/edge/volumes.py index 4c036923..6742dc5e 100644 --- a/cterasdk/edge/volumes.py +++ b/cterasdk/edge/volumes.py @@ -166,15 +166,13 @@ def _device_volume(device_name, ctera_devices): def _volume_size(size, device_name, device_size): if size is not None: if size > device_size: - logger.error('You cannot exceed the available storage capacity. %s', - {'size': size, 'free_size': device_size}) + logger.error('You cannot exceed the available storage capacity. %s', {'size': size, 'free_size': device_size}) raise InputError("You cannot exceed the available storage capacity", size, device_size) return size if device_size > 0: logger.info('You did not specify a volume size.') - logger.info('Allocating available storage capacity. %s', - {'name': device_name, 'free_size': device_size}) + logger.info('Allocating available storage capacity. %s', {'name': device_name, 'free_size': device_size}) return device_size logger.error('Insufficient storage space. %s', {'name': device_name}) diff --git a/cterasdk/exceptions/__init__.py b/cterasdk/exceptions/__init__.py index 10e2b1bf..eddb2087 100644 --- a/cterasdk/exceptions/__init__.py +++ b/cterasdk/exceptions/__init__.py @@ -1,6 +1,3 @@ -from ..convert import tojsonstr - - class CTERAException(Exception): """ Base Exception. @@ -44,11 +41,11 @@ class UserConsentError(CTERAException): """Console""" -from . import ( +from . import ( # noqa: E402, F401 backup, direct, io, notifications, session, transport -) \ No newline at end of file +) diff --git a/cterasdk/exceptions/backup.py b/cterasdk/exceptions/backup.py index 9d52e920..b571960d 100644 --- a/cterasdk/exceptions/backup.py +++ b/cterasdk/exceptions/backup.py @@ -26,4 +26,4 @@ class ClocksOutOfSync(CTERAException): """ Clocks Out of Sync exception """ def __init__(self): - super().__init__('Clocks are out of sync') \ No newline at end of file + super().__init__('Clocks are out of sync') diff --git a/cterasdk/exceptions/notifications.py b/cterasdk/exceptions/notifications.py index 7d8734da..1480b3a0 100644 --- a/cterasdk/exceptions/notifications.py +++ b/cterasdk/exceptions/notifications.py @@ -12,4 +12,4 @@ class NotificationsError(CTERAException): def __init__(self, cloudfolders, cursor): super().__init__('An error occurred while trying to retrieve notifications.') self.cloudfolders = cloudfolders - self.cursor = cursor \ No newline at end of file + self.cursor = cursor diff --git a/cterasdk/exceptions/session.py b/cterasdk/exceptions/session.py index bfffaa12..f35a2800 100644 --- a/cterasdk/exceptions/session.py +++ b/cterasdk/exceptions/session.py @@ -19,4 +19,4 @@ class ContextError(CTERAException): """API invocation context rrror""" def __init__(self, message): - super().__init__(f'Context error: {message}.') \ No newline at end of file + super().__init__(f'Context error: {message}.') diff --git a/tests/ut/aio/direct/test_get_object.py b/tests/ut/aio/direct/test_get_object.py index 75b52ffc..257fbbfd 100644 --- a/tests/ut/aio/direct/test_get_object.py +++ b/tests/ut/aio/direct/test_get_object.py @@ -4,7 +4,7 @@ from unittest import mock import munch from cterasdk.direct.lib import get_object -from cterasdk import exceptions, ctera_direct +from cterasdk import exceptions from . import base diff --git a/tests/ut/core/admin/test_devices.py b/tests/ut/core/admin/test_devices.py index 95fb1764..06d6de29 100644 --- a/tests/ut/core/admin/test_devices.py +++ b/tests/ut/core/admin/test_devices.py @@ -44,7 +44,7 @@ def test_device_notfound(self): self._init_global_admin(get_multi_response=o) with self.assertRaises(exceptions.CTERAException) as error: devices.Devices(self._global_admin).device(o.name) - self.assertEqual(f'Object not found: /portals/None/devices/None', str(error.exception)) + self.assertEqual('Object not found: /portals/None/devices/None', str(error.exception)) def test_filers_no_device_types(self): self._test_filers(None, enum.DeviceType.Gateways) diff --git a/tests/ut/edge/test_volumes.py b/tests/ut/edge/test_volumes.py index 82e0f24f..7e95882a 100644 --- a/tests/ut/edge/test_volumes.py +++ b/tests/ut/edge/test_volumes.py @@ -181,7 +181,7 @@ def test_delete_volume_raise(self): with self.assertRaises(exceptions.CTERAException) as error: volumes.Volumes(self._filer).delete(self._volume_1_name) self._filer.tasks.by_name.assert_called_once_with(' '.join(['Mounting', self._volume_1_name, 'file system'])) - ref = f'/config/storage/volumes/{self._volume_1_name}' + ref = f'/config/storage/volumes/{self._volume_1_name}' self._filer.api.delete.assert_called_once_with(ref) self.assertEqual(f'Volume deletion failed: {ref}', str(error.exception)) From f05059aa322ffeb3485d191349cf728d0581d56e Mon Sep 17 00:00:00 2001 From: Saimon Michelson Date: Fri, 6 Jun 2025 12:49:33 -0400 Subject: [PATCH 05/15] update code to pass pylint --- cterasdk/edge/backup.py | 29 +--------------------------- cterasdk/edge/shares.py | 2 +- cterasdk/edge/smb.py | 2 +- cterasdk/exceptions/__init__.py | 29 +++++++++------------------- cterasdk/exceptions/backup.py | 2 +- cterasdk/exceptions/io.py | 2 +- cterasdk/exceptions/notifications.py | 2 +- cterasdk/exceptions/session.py | 2 +- cterasdk/exceptions/transport.py | 2 +- tests/ut/edge/test_cache.py | 6 +++--- 10 files changed, 20 insertions(+), 58 deletions(-) diff --git a/cterasdk/edge/backup.py b/cterasdk/edge/backup.py index 047a0879..9e23b60f 100644 --- a/cterasdk/edge/backup.py +++ b/cterasdk/edge/backup.py @@ -2,6 +2,7 @@ from ..common import Object from ..exceptions import CTERAException +from ..exceptions.backup import NotFound, AttachEncrypted, IncorrectPassphrase, ClocksOutOfSync from .enum import BackupConfStatusID from .base_command import BaseCommand from .directorytree import DirectoryTree @@ -38,34 +39,6 @@ class EncryptionMode: Secret = 'SecretKeyEncryption' -class NotFound(CTERAException): - """ Not found exception """ - - -class AttachEncrypted(CTERAException): - """ Attach Encrypted exception """ - - def __init__(self, encryptionMode, encryptedFolderKey, passPhraseSalt): - super().__init__() - self.encryptionMode = encryptionMode - self.encryptedFolderKey = encryptedFolderKey - self.passPhraseSalt = passPhraseSalt - - -class IncorrectPassphrase(CTERAException): - """ Incorrect Passphrase exception """ - - def __init__(self): - super().__init__('Incorrect passphrase') - - -class ClocksOutOfSync(CTERAException): - """ Clocks Out of Sync exception """ - - def __init__(self): - super().__init__('Clocks are out of sync') - - class Backup(BaseCommand): """ Edge Filer backup configuration APIs """ diff --git a/cterasdk/edge/shares.py b/cterasdk/edge/shares.py index e66b8cac..78bee712 100644 --- a/cterasdk/edge/shares.py +++ b/cterasdk/edge/shares.py @@ -290,7 +290,7 @@ def delete(self, name): self._edge.api.delete(ref) logger.info("Share deleted: %s", ref) except Exception as error: - logger.error(f"Share deletion failed: {ref}") + logger.error("Share deletion failed: %s", ref) raise CTERAException(f'Share deletion failed: {ref}') from error def get_trusted_nfs_clients(self, name): diff --git a/cterasdk/edge/smb.py b/cterasdk/edge/smb.py index 16b671e8..f4618851 100644 --- a/cterasdk/edge/smb.py +++ b/cterasdk/edge/smb.py @@ -42,7 +42,7 @@ def set_packet_signing(self, packet_signing): self._edge.api.put('/config/fileservices/cifs/packetSigning', packet_signing) logger.info('SMB packet signing configuration updated: %s', packet_signing) except CTERAException as error: - logger.error(f'Invalid SMB packet signing configuration: {packet_signing}') + logger.error('Invalid SMB packet signing configuration: %s', packet_signing) raise CTERAException(f'Invalid packet SMB signing configuration: {packet_signing}') from error def disable(self): diff --git a/cterasdk/exceptions/__init__.py b/cterasdk/exceptions/__init__.py index eddb2087..8fab2996 100644 --- a/cterasdk/exceptions/__init__.py +++ b/cterasdk/exceptions/__init__.py @@ -1,14 +1,13 @@ -class CTERAException(Exception): - """ - Base Exception. - - :parm str message: Error message - """ - def __init__(self, message=None): - super().__init__(message) +from . import ( # noqa: E402, F401 + backup, + direct, + io, + notifications, + session, + transport +) - def __repr__(self): - return str(self) +from .base import CTERAException class ObjectNotFoundException(CTERAException): @@ -39,13 +38,3 @@ def __init__(self, message, expression=None, options=None): class UserConsentError(CTERAException): """Console""" - - -from . import ( # noqa: E402, F401 - backup, - direct, - io, - notifications, - session, - transport -) diff --git a/cterasdk/exceptions/backup.py b/cterasdk/exceptions/backup.py index b571960d..45538925 100644 --- a/cterasdk/exceptions/backup.py +++ b/cterasdk/exceptions/backup.py @@ -1,4 +1,4 @@ -from . import CTERAException +from .base import CTERAException class NotFound(CTERAException): diff --git a/cterasdk/exceptions/io.py b/cterasdk/exceptions/io.py index 523a468e..31b7136c 100644 --- a/cterasdk/exceptions/io.py +++ b/cterasdk/exceptions/io.py @@ -1,4 +1,4 @@ -from . import CTERAException +from .base import CTERAException class RemoteStorageException(CTERAException): diff --git a/cterasdk/exceptions/notifications.py b/cterasdk/exceptions/notifications.py index 1480b3a0..c2e99ce1 100644 --- a/cterasdk/exceptions/notifications.py +++ b/cterasdk/exceptions/notifications.py @@ -1,4 +1,4 @@ -from . import CTERAException +from .base import CTERAException class NotificationsError(CTERAException): diff --git a/cterasdk/exceptions/session.py b/cterasdk/exceptions/session.py index f35a2800..933f5346 100644 --- a/cterasdk/exceptions/session.py +++ b/cterasdk/exceptions/session.py @@ -1,4 +1,4 @@ -from . import CTERAException +from .base import CTERAException class SessionExpired(CTERAException): diff --git a/cterasdk/exceptions/transport.py b/cterasdk/exceptions/transport.py index 9d938609..cc7c6e2a 100644 --- a/cterasdk/exceptions/transport.py +++ b/cterasdk/exceptions/transport.py @@ -1,5 +1,5 @@ from http import HTTPStatus -from . import CTERAException +from .base import CTERAException class HTTPError(CTERAException): diff --git a/tests/ut/edge/test_cache.py b/tests/ut/edge/test_cache.py index 00af3d6d..d5b2ddf0 100644 --- a/tests/ut/edge/test_cache.py +++ b/tests/ut/edge/test_cache.py @@ -64,7 +64,7 @@ def test_pin_invalid_root_directory(self): with self.assertRaises(exceptions.CTERAException) as error: cache.Cache(self._filer).pin(self._pin_invalid_folder_path) self._filer.api.get.assert_called_once_with('/config/cloudsync/cloudExtender/selectedFolders') - self.assertEqual(f"Invalid root directory: {self._pin_invalid_folder_path.split('/')[0]}.", str(error.exception)) + self.assertEqual(f"Invalid root directory: {self._pin_invalid_folder_path.split('/', maxsplit=1)[0]}.", str(error.exception)) def test_pin_exclude_subfolder(self): get_response = self._create_dir_tree(self._pin_valid_folder_path, True) @@ -135,7 +135,7 @@ def test_pin_recursive_invalid_root_directory(self): with self.assertRaises(exceptions.CTERAException) as error: cache.Cache(self._filer).pin_recursive(self._pin_invalid_folder_path) self._filer.api.get.assert_called_once_with('/config/cloudsync/cloudExtender/selectedFolders') - self.assertEqual(f"Invalid root directory: {self._pin_invalid_folder_path.split('/')[0]}.", str(error.exception)) + self.assertEqual(f"Invalid root directory: {self._pin_invalid_folder_path.split('/', maxsplit=1)[0]}.", str(error.exception)) def test_unpin_recursive(self): get_response = self._create_dir_tree(self._pin_valid_folder_path, True) @@ -155,7 +155,7 @@ def test_unpin_recursive_invalid_root_directory(self): with self.assertRaises(exceptions.CTERAException) as error: cache.Cache(self._filer).unpin_recursive(self._pin_invalid_folder_path) self._filer.api.get.assert_called_once_with('/config/cloudsync/cloudExtender/selectedFolders') - self.assertEqual(f"Invalid root directory: {self._pin_invalid_folder_path.split('/')[0]}.", str(error.exception)) + self.assertEqual(f"Invalid root directory: {self._pin_invalid_folder_path.split('/', maxsplit=1)[0]}.", str(error.exception)) def _get_dir_entry(self, name, include): param = Object() From 5a6eb424179d67a98d883d4218710c765569851c Mon Sep 17 00:00:00 2001 From: Saimon Michelson Date: Fri, 6 Jun 2025 12:53:22 -0400 Subject: [PATCH 06/15] commit missing module updates --- cterasdk/asynchronous/core/files/io.py | 2 +- cterasdk/audit/postman.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cterasdk/asynchronous/core/files/io.py b/cterasdk/asynchronous/core/files/io.py index fba9eeae..3632b651 100644 --- a/cterasdk/asynchronous/core/files/io.py +++ b/cterasdk/asynchronous/core/files/io.py @@ -91,7 +91,7 @@ async def move(core, *paths, destination=None): async def retrieve_remote_dir(core, directory): resource = await metadata(core, directory) if not resource.isFolder: - raise RemoteStorageException('The destination path is not a directory', None, path=directory.absolute) + raise RemoteStorageException('The destination path is not a directory', path=directory.absolute) return str(resource.cloudFolderInfo.uid) diff --git a/cterasdk/audit/postman.py b/cterasdk/audit/postman.py index 0675eded..5cb43c81 100644 --- a/cterasdk/audit/postman.py +++ b/cterasdk/audit/postman.py @@ -63,7 +63,7 @@ def request_headers(self, d): self.header = [Header(k, v) for k, v in d.items()] def request_body(self, data): - self.body = data + self.body = data # pylint: disable=attribute-defined-outside-init class Header(Object): From d0a050a5199c33981beb72a13798ae963ed0326f Mon Sep 17 00:00:00 2001 From: Saimon Michelson Date: Fri, 6 Jun 2025 12:54:15 -0400 Subject: [PATCH 07/15] add base exception --- cterasdk/exceptions/base.py | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 cterasdk/exceptions/base.py diff --git a/cterasdk/exceptions/base.py b/cterasdk/exceptions/base.py new file mode 100644 index 00000000..d7789c92 --- /dev/null +++ b/cterasdk/exceptions/base.py @@ -0,0 +1,11 @@ +class CTERAException(Exception): + """ + Base Exception. + + :parm str message: Error message + """ + def __init__(self, message=None): + super().__init__(message) + + def __repr__(self): + return str(self) \ No newline at end of file From fc45f9d18ce8a31c5785c2db280a5e54cb3f503a Mon Sep 17 00:00:00 2001 From: Saimon Michelson Date: Fri, 6 Jun 2025 13:24:43 -0400 Subject: [PATCH 08/15] update base exception class --- cterasdk/exceptions/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cterasdk/exceptions/base.py b/cterasdk/exceptions/base.py index d7789c92..f6e0d78b 100644 --- a/cterasdk/exceptions/base.py +++ b/cterasdk/exceptions/base.py @@ -8,4 +8,4 @@ def __init__(self, message=None): super().__init__(message) def __repr__(self): - return str(self) \ No newline at end of file + return str(self) From d4f4d9fb99272197b99b60ecf1a99a9db84e9de5 Mon Sep 17 00:00:00 2001 From: Saimon Michelson Date: Fri, 6 Jun 2025 13:26:13 -0400 Subject: [PATCH 09/15] update docs for direct-io exception --- docs/source/UserGuides/DataServices/DirectIO.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/source/UserGuides/DataServices/DirectIO.rst b/docs/source/UserGuides/DataServices/DirectIO.rst index 478768fb..c938551d 100644 --- a/docs/source/UserGuides/DataServices/DirectIO.rst +++ b/docs/source/UserGuides/DataServices/DirectIO.rst @@ -186,22 +186,22 @@ Streamer API Exceptions ========== -.. autoclass:: cterasdk.direct.exceptions.DirectIOError +.. autoclass:: cterasdk.direct.exceptions.direct.DirectIOError :noindex: :members: :show-inheritance: -.. autoclass:: cterasdk.direct.exceptions.DirectIOAPIError +.. autoclass:: cterasdk.direct.exceptions.direct.DirectIOAPIError :noindex: :members: :show-inheritance: -.. autoclass:: cterasdk.direct.exceptions.BlockError +.. autoclass:: cterasdk.direct.exceptions.direct.BlockError :noindex: :members: :show-inheritance: -.. autoclass:: cterasdk.direct.exceptions.StreamError +.. autoclass:: cterasdk.direct.exceptions.direct.StreamError :noindex: :members: :show-inheritance: From af660bf143c37ccf89650c065ae08188745487e7 Mon Sep 17 00:00:00 2001 From: Saimon Michelson Date: Fri, 6 Jun 2025 14:36:08 -0400 Subject: [PATCH 10/15] support uploading files from string or bytes --- cterasdk/asynchronous/core/files/browser.py | 6 +++--- cterasdk/cio/common.py | 8 ++++++++ cterasdk/cio/core.py | 1 + cterasdk/cio/edge.py | 1 + cterasdk/core/files/browser.py | 8 ++++---- cterasdk/core/files/io.py | 2 +- docs/source/UserGuides/DataServices/DirectIO.rst | 8 ++++---- 7 files changed, 22 insertions(+), 12 deletions(-) diff --git a/cterasdk/asynchronous/core/files/browser.py b/cterasdk/asynchronous/core/files/browser.py index 4d87a6d0..07a61a38 100644 --- a/cterasdk/asynchronous/core/files/browser.py +++ b/cterasdk/asynchronous/core/files/browser.py @@ -134,14 +134,14 @@ def normalize(self, entries): class CloudDrive(FileBrowser): - async def upload(self, name, size, destination, handle): + async def upload(self, name, destination, handle, size=None): """ Upload from file handle. :param str name: File name. - :param str size: File size. :param str destination: Path to remote directory. :param object handle: Handle. + :param str,optional size: File size, defaults to content length """ upload_function = io.upload(name, size, self.normalize(destination), handle) return await upload_function(self._core) @@ -155,7 +155,7 @@ async def upload_file(self, path, destination): """ with open(path, 'rb') as handle: metadata = commonfs.properties(path) - response = await self.upload(metadata['name'], metadata['size'], destination, handle) + response = await self.upload(metadata['name'], destination, handle, metadata['size']) return response async def mkdir(self, path): diff --git a/cterasdk/cio/common.py b/cterasdk/cio/common.py index 1ef98771..0b517e74 100644 --- a/cterasdk/cio/common.py +++ b/cterasdk/cio/common.py @@ -65,6 +65,14 @@ def __str__(self): return self.absolute +def encode_stream(fd, size): + if isinstance(fd, str): + fd = fd.encode('utf-8') + if isinstance(fd, bytes): + size = str(len(fd)) + return fd, size + + def encode_request_parameter(param): return dict( inputXML=utf8_decode(toxmlstr(param)) diff --git a/cterasdk/cio/core.py b/cterasdk/cio/core.py index 0aadb90a..e8669fe9 100644 --- a/cterasdk/cio/core.py +++ b/cterasdk/cio/core.py @@ -272,6 +272,7 @@ def handle(path): @contextmanager def upload(core, name, destination, size, fd): + fd, size = common.encode_stream(fd, size) param = dict( name=name, Filename=name, diff --git a/cterasdk/cio/edge.py b/cterasdk/cio/edge.py index 61711834..b5ede1e4 100644 --- a/cterasdk/cio/edge.py +++ b/cterasdk/cio/edge.py @@ -116,6 +116,7 @@ def handle_many(directory, objects): @contextmanager def upload(name, destination, fd): + fd, *_ = common.encode_stream(fd, 0) param = dict( name=name, fullpath=f'{destination.absolute}/{name}', diff --git a/cterasdk/core/files/browser.py b/cterasdk/core/files/browser.py index e353c629..d043c3cc 100644 --- a/cterasdk/core/files/browser.py +++ b/cterasdk/core/files/browser.py @@ -140,14 +140,14 @@ def normalize(self, entries): class CloudDrive(FileBrowser): - def upload(self, name, size, destination, handle): + def upload(self, name, destination, handle, size=None): """ Upload from file handle. :param str name: File name. - :param str size: File size. :param str destination: Path to remote directory. - :param object handle: Handle. + :param object handle: File handle, String, or Bytes. + :param str,optional size: File size, defaults to content length """ upload_function = io.upload(name, size, self.normalize(destination), handle) return upload_function(self._core) @@ -161,7 +161,7 @@ def upload_file(self, path, destination): """ with open(path, 'rb') as handle: metadata = commonfs.properties(path) - response = self.upload(metadata['name'], metadata['size'], destination, handle) + response = self.upload(metadata['name'], destination, handle, metadata['size']) return response def mkdir(self, path): diff --git a/cterasdk/core/files/io.py b/cterasdk/core/files/io.py index 054baee1..77745095 100644 --- a/cterasdk/core/files/io.py +++ b/cterasdk/core/files/io.py @@ -151,7 +151,7 @@ def wrapper(core): """ Upload file from metadata and file handle. - :param cterasdk.objects.synchronous.core.Portal core: POrtal object. + :param cterasdk.objects.synchronous.core.Portal core: Portal object. """ target = retrieve_remote_dir(core, destination) with fs.upload(core, name, destination, size, fd) as param: diff --git a/docs/source/UserGuides/DataServices/DirectIO.rst b/docs/source/UserGuides/DataServices/DirectIO.rst index c938551d..8117b9a3 100644 --- a/docs/source/UserGuides/DataServices/DirectIO.rst +++ b/docs/source/UserGuides/DataServices/DirectIO.rst @@ -186,22 +186,22 @@ Streamer API Exceptions ========== -.. autoclass:: cterasdk.direct.exceptions.direct.DirectIOError +.. autoclass:: cterasdk.exceptions.direct.DirectIOError :noindex: :members: :show-inheritance: -.. autoclass:: cterasdk.direct.exceptions.direct.DirectIOAPIError +.. autoclass:: cterasdk.exceptions.direct.DirectIOAPIError :noindex: :members: :show-inheritance: -.. autoclass:: cterasdk.direct.exceptions.direct.BlockError +.. autoclass:: cterasdk.exceptions.direct.BlockError :noindex: :members: :show-inheritance: -.. autoclass:: cterasdk.direct.exceptions.direct.StreamError +.. autoclass:: cterasdk.exceptions.direct.StreamError :noindex: :members: :show-inheritance: From b07ea599bd004196ed89ba53e448d7dbefce8096 Mon Sep 17 00:00:00 2001 From: Saimon Michelson Date: Sat, 7 Jun 2025 00:12:20 -0400 Subject: [PATCH 11/15] support uploading with filename in destination, auto rename if destination file if needed --- cterasdk/asynchronous/core/files/io.py | 46 ++++++++++++++---------- cterasdk/asynchronous/edge/files/io.py | 36 +++++++++++++++---- cterasdk/core/files/io.py | 48 +++++++++++++++----------- cterasdk/edge/files/io.py | 36 +++++++++++++++---- cterasdk/exceptions/io.py | 6 ++++ 5 files changed, 119 insertions(+), 53 deletions(-) diff --git a/cterasdk/asynchronous/core/files/io.py b/cterasdk/asynchronous/core/files/io.py index 3632b651..18d45298 100644 --- a/cterasdk/asynchronous/core/files/io.py +++ b/cterasdk/asynchronous/core/files/io.py @@ -1,7 +1,7 @@ import logging from ....cio.common import encode_request_parameter from ....cio import core as fs -from ....exceptions.io import ResourceNotFoundError, RemoteStorageException, ResourceExistsError +from ....exceptions.io import ResourceNotFoundError, NotADirectory, ResourceExistsError from .. import query from ....lib import FetchResourcesResponse @@ -17,18 +17,17 @@ async def listdir(core, path, depth=None, include_deleted=False, search_criteria async def exists(core, path): - try: - await metadata(core, path) - return True - except ResourceNotFoundError: - return False + exists, *_ = await metadata(core, path, suppress_error=True) + return exists -async def metadata(core, path): +async def metadata(core, path, suppress_error=False): response = await listdir(core, path, 0) if response.root is None: - raise ResourceNotFoundError(path.absolute) - return response.root + if not suppress_error: + raise ResourceNotFoundError(path.absolute) + return False, None + return True, response.root async def versions(core, path): @@ -88,11 +87,11 @@ async def move(core, *paths, destination=None): return await core.v1.api.execute('', 'moveResources', param) -async def retrieve_remote_dir(core, directory): - resource = await metadata(core, directory) - if not resource.isFolder: - raise RemoteStorageException('The destination path is not a directory', path=directory.absolute) - return str(resource.cloudFolderInfo.uid) +async def ensure_directory(core, directory, suppress_error=False): + exists, resource = await metadata(core, directory, suppress_error=True) + if (not exists or not resource.isFolder) and not suppress_error: + raise NotADirectory(directory.absolute) + return resource.isFolder if exists else False, resource def handle(path): @@ -132,10 +131,19 @@ async def wrapper(core): :param object handle: File handle. """ with fs.handle_many(directory, objects) as param: - return await core.io.download_zip(await retrieve_remote_dir(core, directory), encode_request_parameter(param)) + _, resource = await ensure_directory(core, directory) + return await core.io.download_zip(str(resource.cloudFolderInfo.uid), encode_request_parameter(param)) return wrapper +async def _validate_destination(core, name, destination): + is_dir, resource = await ensure_directory(core, destination, suppress_error=True) + if not is_dir: + is_dir, resource = await ensure_directory(core, destination.parent) + return resource.cloudFolderInfo.uid, destination.name, destination.parent + return resource.cloudFolderInfo.uid, name, destination + + def upload(name, size, destination, fd): """ Create upload function @@ -150,11 +158,11 @@ async def wrapper(core): """ Upload file from metadata and file handle. - :param cterasdk.objects.synchronous.core.Portal core: POrtal object. + :param cterasdk.objects.synchronous.core.Portal core: Portal object. """ - target = await retrieve_remote_dir(core, destination) - with fs.upload(core, name, destination, size, fd) as param: - return await core.io.upload(target, param) + uid, filename, directory = await _validate_destination(core, name, destination) + with fs.upload(core, filename, directory, size, fd) as param: + return await core.io.upload(str(uid), param) return wrapper diff --git a/cterasdk/asynchronous/edge/files/io.py b/cterasdk/asynchronous/edge/files/io.py index 5842377e..ddf0bb2d 100644 --- a/cterasdk/asynchronous/edge/files/io.py +++ b/cterasdk/asynchronous/edge/files/io.py @@ -1,8 +1,8 @@ import logging from ....cio.common import encode_request_parameter from ....cio import edge as fs -from ....exceptions.transport import HTTPError -from ....exceptions.io import RestrictedPathError +from ....exceptions.transport import NotFound +from ....exceptions.io import RestrictedPathError, ResourceNotFoundError, NotADirectory logger = logging.getLogger('cterasdk.edge') @@ -24,11 +24,24 @@ async def walk(edge, path): async def exists(edge, path): + exists, *_ = await metadata(edge, path, suppress_error=True) + return exists + + +async def metadata(edge, path, suppress_error=False): try: - await edge.io.propfind(path.absolute, 0) - return True - except HTTPError: - return False + return True, fs.format_listdir_response(None, await edge.io.propfind(path.absolute, 0))[0] + except NotFound as error: + if not suppress_error: + raise ResourceNotFoundError(path.absolute) from error + return False, None + + +async def ensure_directory(edge, directory, suppress_error=False): + exists, resource = await metadata(edge, directory, suppress_error=True) + if (not exists or not resource.is_dir) and not suppress_error: + raise NotADirectory(directory.absolute) + return resource.is_dir if exists else False, resource async def mkdir(edge, path): @@ -103,6 +116,14 @@ async def wrapper(edge): return wrapper +async def _validate_destination(edge, name, destination): + is_dir, *_ = await ensure_directory(edge, destination, suppress_error=True) + if not is_dir: + is_dir, *_ = await ensure_directory(edge, destination.parent) + return destination.name, destination.parent + return name, destination + + def upload(name, destination, fd): """ Create upload function @@ -119,6 +140,7 @@ async def wrapper(edge): :param cterasdk.objects.synchronous.edge.Edge edge: Edge Filer object. """ - with fs.upload(name, destination, fd) as param: + filename, directory = await _validate_destination(edge, name, destination) + with fs.upload(filename, directory, fd) as param: return await edge.io.upload('/actions/upload', param) return wrapper diff --git a/cterasdk/core/files/io.py b/cterasdk/core/files/io.py index 77745095..71ecb5f4 100644 --- a/cterasdk/core/files/io.py +++ b/cterasdk/core/files/io.py @@ -1,7 +1,7 @@ import logging from ...cio.common import encode_request_parameter from ...cio import core as fs -from ...exceptions.io import ResourceNotFoundError, RemoteStorageException, ResourceExistsError +from ...exceptions.io import ResourceNotFoundError, ResourceExistsError, NotADirectory from ...core import query from ..enum import CollaboratorType from ...lib import FetchResourcesResponse @@ -18,18 +18,17 @@ def listdir(core, path, depth=None, include_deleted=False, search_criteria=None, def exists(core, path): - try: - metadata(core, path) - return True - except ResourceNotFoundError: - return False + exists, *_ = metadata(core, path, suppress_error=True) + return exists -def metadata(core, path): +def metadata(core, path, suppress_error=False): response = listdir(core, path, 0) if response.root is None: - raise ResourceNotFoundError(path.absolute) - return response.root + if not suppress_error: + raise ResourceNotFoundError(path.absolute) + return False, None + return True, response.root def versions(core, path): @@ -89,11 +88,11 @@ def move(core, *paths, destination=None): return core.api.execute('', 'moveResources', param) -def retrieve_remote_dir(core, directory): - resource = metadata(core, directory) - if not resource.isFolder: - raise RemoteStorageException('The destination path is not a directory', path=directory.absolute) - return str(resource.cloudFolderInfo.uid) +def ensure_directory(core, directory, suppress_error=False): + exists, resource = metadata(core, directory, suppress_error=True) + if (not exists or not resource.isFolder) and not suppress_error: + raise NotADirectory(directory.absolute) + return resource.isFolder if exists else False, resource def handle(path): @@ -133,10 +132,19 @@ def wrapper(core): :param object handle: File handle. """ with fs.handle_many(directory, objects) as param: - return core.io.download_zip(retrieve_remote_dir(core, directory), encode_request_parameter(param)) + _, resource = ensure_directory(core, directory) + return core.io.download_zip(str(resource.cloudFolderInfo.uid), encode_request_parameter(param)) return wrapper +def _validate_destination(core, name, destination): + is_dir, resource = ensure_directory(core, destination, suppress_error=True) + if not is_dir: + is_dir, resource = ensure_directory(core, destination.parent) + return resource.cloudFolderInfo.uid, destination.name, destination.parent + return resource.cloudFolderInfo.uid, name, destination + + def upload(name, size, destination, fd): """ Create upload function @@ -153,9 +161,9 @@ def wrapper(core): :param cterasdk.objects.synchronous.core.Portal core: Portal object. """ - target = retrieve_remote_dir(core, destination) - with fs.upload(core, name, destination, size, fd) as param: - return core.io.upload(target, param) + uid, filename, directory = _validate_destination(core, name, destination) + with fs.upload(core, filename, directory, size, fd) as param: + return core.io.upload(str(uid), param) return wrapper @@ -207,7 +215,7 @@ def add_share_recipients(core, path, recipients): def _obtain_valid_recipients(core, path, recipients): - resource_info = metadata(core, path) + _, resource_info = metadata(core, path) valid_recipients = [] for recipient in filter(fs.valid_recipient, recipients): if not recipient.type == CollaboratorType.EXT: @@ -221,7 +229,7 @@ def _obtain_valid_recipients(core, path, recipients): def unshare(core, path): - resource_info = metadata(core, path) + _, resource_info = metadata(core, path) with fs.unshare(resource_info, path) as param: return core.api.execute('', 'shareResource', param) diff --git a/cterasdk/edge/files/io.py b/cterasdk/edge/files/io.py index 6bdbcc7b..6a86691d 100644 --- a/cterasdk/edge/files/io.py +++ b/cterasdk/edge/files/io.py @@ -1,8 +1,8 @@ import logging from ...cio.common import encode_request_parameter from ...cio import edge as fs -from ...exceptions.transport import HTTPError -from ...exceptions.io import RestrictedPathError +from ...exceptions.transport import NotFound +from ...exceptions.io import RestrictedPathError, ResourceNotFoundError, NotADirectory logger = logging.getLogger('cterasdk.edge') @@ -24,11 +24,24 @@ def walk(edge, path): def exists(edge, path): + exists, *_ = metadata(edge, path, suppress_error=True) + return exists + + +def metadata(edge, path, suppress_error=False): try: - edge.io.propfind(path.absolute, 0) - return True - except HTTPError: - return False + return True, fs.format_listdir_response(None, edge.io.propfind(path.absolute, 0))[0] + except NotFound as error: + if not suppress_error: + raise ResourceNotFoundError(path.absolute) from error + return False, None + + +def ensure_directory(edge, directory, suppress_error=False): + exists, resource = metadata(edge, directory, suppress_error=True) + if (not exists or not resource.is_dir) and not suppress_error: + raise NotADirectory(directory.absolute) + return resource.is_dir if exists else False, resource def mkdir(edge, path): @@ -103,6 +116,14 @@ def wrapper(edge): return wrapper +def _validate_destination(edge, name, destination): + is_dir, *_ = ensure_directory(edge, destination, suppress_error=True) + if not is_dir: + is_dir, *_ = ensure_directory(edge, destination.parent) + return destination.name, destination.parent + return name, destination + + def upload(name, destination, fd): """ Create upload function @@ -119,6 +140,7 @@ def wrapper(edge): :param cterasdk.objects.synchronous.edge.Edge edge: Edge Filer object. """ - with fs.upload(name, destination, fd) as param: + filename, directory = _validate_destination(edge, name, destination) + with fs.upload(filename, directory, fd) as param: return edge.io.upload('/actions/upload', param) return wrapper diff --git a/cterasdk/exceptions/io.py b/cterasdk/exceptions/io.py index 31b7136c..b47c6e02 100644 --- a/cterasdk/exceptions/io.py +++ b/cterasdk/exceptions/io.py @@ -18,6 +18,12 @@ def __init__(self, path): super().__init__('Remote directory not found. Please verify the path and try again.', path) +class NotADirectory(RemoteStorageException): + + def __init__(self, path): + super().__init__('Target validation error: Resource exists but it is not a directory.', path) + + class ResourceExistsError(CTERAException): def __init__(self): From 5b4fc096a5222d7c93332b5f7dd4213c220de22b Mon Sep 17 00:00:00 2001 From: Saimon Michelson Date: Sat, 7 Jun 2025 00:17:05 -0400 Subject: [PATCH 12/15] avoid name redefinition --- cterasdk/asynchronous/core/files/io.py | 9 +++++++-- cterasdk/asynchronous/edge/files/io.py | 9 +++++++-- cterasdk/core/files/io.py | 9 +++++++-- cterasdk/edge/files/io.py | 9 +++++++-- 4 files changed, 28 insertions(+), 8 deletions(-) diff --git a/cterasdk/asynchronous/core/files/io.py b/cterasdk/asynchronous/core/files/io.py index 18d45298..cf3f9124 100644 --- a/cterasdk/asynchronous/core/files/io.py +++ b/cterasdk/asynchronous/core/files/io.py @@ -17,11 +17,16 @@ async def listdir(core, path, depth=None, include_deleted=False, search_criteria async def exists(core, path): - exists, *_ = await metadata(core, path, suppress_error=True) - return exists + e, *_ = await metadata(core, path, suppress_error=True) + return e async def metadata(core, path, suppress_error=False): + """ + Get item metadata. + + :returns: A tuple indicating if a file exists, and its metadata + """ response = await listdir(core, path, 0) if response.root is None: if not suppress_error: diff --git a/cterasdk/asynchronous/edge/files/io.py b/cterasdk/asynchronous/edge/files/io.py index ddf0bb2d..eb23d2b2 100644 --- a/cterasdk/asynchronous/edge/files/io.py +++ b/cterasdk/asynchronous/edge/files/io.py @@ -24,11 +24,16 @@ async def walk(edge, path): async def exists(edge, path): - exists, *_ = await metadata(edge, path, suppress_error=True) - return exists + e, *_ = await metadata(edge, path, suppress_error=True) + return e async def metadata(edge, path, suppress_error=False): + """ + Get item metadata. + + :returns: A tuple indicating if a file exists, and its metadata + """ try: return True, fs.format_listdir_response(None, await edge.io.propfind(path.absolute, 0))[0] except NotFound as error: diff --git a/cterasdk/core/files/io.py b/cterasdk/core/files/io.py index 71ecb5f4..9412f855 100644 --- a/cterasdk/core/files/io.py +++ b/cterasdk/core/files/io.py @@ -18,11 +18,16 @@ def listdir(core, path, depth=None, include_deleted=False, search_criteria=None, def exists(core, path): - exists, *_ = metadata(core, path, suppress_error=True) - return exists + e, *_ = metadata(core, path, suppress_error=True) + return e def metadata(core, path, suppress_error=False): + """ + Get item metadata. + + :returns: A tuple indicating if a file exists, and its metadata + """ response = listdir(core, path, 0) if response.root is None: if not suppress_error: diff --git a/cterasdk/edge/files/io.py b/cterasdk/edge/files/io.py index 6a86691d..7b1813c8 100644 --- a/cterasdk/edge/files/io.py +++ b/cterasdk/edge/files/io.py @@ -24,11 +24,16 @@ def walk(edge, path): def exists(edge, path): - exists, *_ = metadata(edge, path, suppress_error=True) - return exists + e, *_ = metadata(edge, path, suppress_error=True) + return e def metadata(edge, path, suppress_error=False): + """ + Get item metadata. + + :returns: A tuple indicating if a file exists, and its metadata + """ try: return True, fs.format_listdir_response(None, edge.io.propfind(path.absolute, 0))[0] except NotFound as error: From 3f5cdafb6a592899e9531058a35fb9354ad43607 Mon Sep 17 00:00:00 2001 From: Saimon Michelson Date: Sat, 7 Jun 2025 00:24:17 -0400 Subject: [PATCH 13/15] add exceptions to docs --- .../source/UserGuides/Miscellaneous/Index.rst | 142 ++++++++++++++++++ 1 file changed, 142 insertions(+) diff --git a/docs/source/UserGuides/Miscellaneous/Index.rst b/docs/source/UserGuides/Miscellaneous/Index.rst index 654b82e5..581ffe42 100644 --- a/docs/source/UserGuides/Miscellaneous/Index.rst +++ b/docs/source/UserGuides/Miscellaneous/Index.rst @@ -2,6 +2,148 @@ Miscellaneous ============= + +Exceptions +========== + +.. autoclass:: cterasdk.exceptions.CTERAException + :noindex: + :members: + :show-inheritance: + +.. autoclass:: cterasdk.exceptions.ObjectNotFoundException + :noindex: + :members: + :show-inheritance: + +.. autoclass:: cterasdk.exceptions.InputError + :noindex: + :members: + :show-inheritance: + +Session +------- + +.. autoclass:: cterasdk.exceptions.session.NotLoggedIn + :noindex: + :members: + :show-inheritance: + +.. autoclass:: cterasdk.exceptions.session.SessionExpired + :noindex: + :members: + :show-inheritance: + +.. autoclass:: cterasdk.exceptions.session.ContextError + :noindex: + :members: + :show-inheritance: + +I/O +--- + +.. autoclass:: cterasdk.exceptions.io.RemoteStorageException + :noindex: + :members: + :show-inheritance: + +.. autoclass:: cterasdk.exceptions.io.ResourceNotFoundError + :noindex: + :members: + :show-inheritance: + +.. autoclass:: cterasdk.exceptions.io.NotADirectory + :noindex: + :members: + :show-inheritance: + +.. autoclass:: cterasdk.exceptions.io.ResourceExistsError + :noindex: + :members: + :show-inheritance: + +.. autoclass:: cterasdk.exceptions.io.PathValidationError + :noindex: + :members: + :show-inheritance: + +.. autoclass:: cterasdk.exceptions.io.NameSyntaxError + :noindex: + :members: + :show-inheritance: + +.. autoclass:: cterasdk.exceptions.io.ReservedNameError + :noindex: + :members: + :show-inheritance: + +.. autoclass:: cterasdk.exceptions.io.RestrictedPathError + :noindex: + :members: + :show-inheritance: + +Notification Service +-------------------- + +.. autoclass:: cterasdk.exceptions.notifications.NotificationError + :noindex: + :members: + :show-inheritance: + + +HTTP Transport +-------------- + +.. autoclass:: cterasdk.exceptions.transport.HTTPError + :noindex: + :members: + :show-inheritance: + +.. autoclass:: cterasdk.exceptions.transport.BadRequest + :noindex: + :members: + :show-inheritance: + +.. autoclass:: cterasdk.exceptions.transport.Unauthorized + :noindex: + :members: + :show-inheritance: + +.. autoclass:: cterasdk.exceptions.transport.Forbidden + :noindex: + :members: + :show-inheritance: + +.. autoclass:: cterasdk.exceptions.transport.NotFound + :noindex: + :members: + :show-inheritance: + +.. autoclass:: cterasdk.exceptions.transport.Unprocessable + :noindex: + :members: + :show-inheritance: + +.. autoclass:: cterasdk.exceptions.transport.InternalServerError + :noindex: + :members: + :show-inheritance: + +.. autoclass:: cterasdk.exceptions.transport.BadGateway + :noindex: + :members: + :show-inheritance: + +.. autoclass:: cterasdk.exceptions.transport.ServiceUnavailable + :noindex: + :members: + :show-inheritance: + +.. autoclass:: cterasdk.exceptions.transport.GatewayTimeout + :noindex: + :members: + :show-inheritance: + Auditing ======== From 21affe7fa40c26e9aae6fb11a319196c68a40444 Mon Sep 17 00:00:00 2001 From: Saimon Michelson Date: Sat, 7 Jun 2025 00:28:24 -0400 Subject: [PATCH 14/15] name redefinition updates --- cterasdk/asynchronous/core/files/io.py | 10 +++++----- cterasdk/asynchronous/edge/files/io.py | 10 +++++----- cterasdk/core/files/io.py | 10 +++++----- cterasdk/edge/files/io.py | 10 +++++----- 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/cterasdk/asynchronous/core/files/io.py b/cterasdk/asynchronous/core/files/io.py index cf3f9124..a7920794 100644 --- a/cterasdk/asynchronous/core/files/io.py +++ b/cterasdk/asynchronous/core/files/io.py @@ -17,8 +17,8 @@ async def listdir(core, path, depth=None, include_deleted=False, search_criteria async def exists(core, path): - e, *_ = await metadata(core, path, suppress_error=True) - return e + present, *_ = await metadata(core, path, suppress_error=True) + return present async def metadata(core, path, suppress_error=False): @@ -93,10 +93,10 @@ async def move(core, *paths, destination=None): async def ensure_directory(core, directory, suppress_error=False): - exists, resource = await metadata(core, directory, suppress_error=True) - if (not exists or not resource.isFolder) and not suppress_error: + present, resource = await metadata(core, directory, suppress_error=True) + if (not present or not resource.isFolder) and not suppress_error: raise NotADirectory(directory.absolute) - return resource.isFolder if exists else False, resource + return resource.isFolder if present else False, resource def handle(path): diff --git a/cterasdk/asynchronous/edge/files/io.py b/cterasdk/asynchronous/edge/files/io.py index eb23d2b2..538d3f2d 100644 --- a/cterasdk/asynchronous/edge/files/io.py +++ b/cterasdk/asynchronous/edge/files/io.py @@ -24,8 +24,8 @@ async def walk(edge, path): async def exists(edge, path): - e, *_ = await metadata(edge, path, suppress_error=True) - return e + present, *_ = await metadata(edge, path, suppress_error=True) + return present async def metadata(edge, path, suppress_error=False): @@ -43,10 +43,10 @@ async def metadata(edge, path, suppress_error=False): async def ensure_directory(edge, directory, suppress_error=False): - exists, resource = await metadata(edge, directory, suppress_error=True) - if (not exists or not resource.is_dir) and not suppress_error: + present, resource = await metadata(edge, directory, suppress_error=True) + if (not present or not resource.is_dir) and not suppress_error: raise NotADirectory(directory.absolute) - return resource.is_dir if exists else False, resource + return resource.is_dir if present else False, resource async def mkdir(edge, path): diff --git a/cterasdk/core/files/io.py b/cterasdk/core/files/io.py index 9412f855..805eaa1c 100644 --- a/cterasdk/core/files/io.py +++ b/cterasdk/core/files/io.py @@ -18,8 +18,8 @@ def listdir(core, path, depth=None, include_deleted=False, search_criteria=None, def exists(core, path): - e, *_ = metadata(core, path, suppress_error=True) - return e + present, *_ = metadata(core, path, suppress_error=True) + return present def metadata(core, path, suppress_error=False): @@ -94,10 +94,10 @@ def move(core, *paths, destination=None): def ensure_directory(core, directory, suppress_error=False): - exists, resource = metadata(core, directory, suppress_error=True) - if (not exists or not resource.isFolder) and not suppress_error: + present, resource = metadata(core, directory, suppress_error=True) + if (not present or not resource.isFolder) and not suppress_error: raise NotADirectory(directory.absolute) - return resource.isFolder if exists else False, resource + return resource.isFolder if present else False, resource def handle(path): diff --git a/cterasdk/edge/files/io.py b/cterasdk/edge/files/io.py index 7b1813c8..0b52a807 100644 --- a/cterasdk/edge/files/io.py +++ b/cterasdk/edge/files/io.py @@ -24,8 +24,8 @@ def walk(edge, path): def exists(edge, path): - e, *_ = metadata(edge, path, suppress_error=True) - return e + present, *_ = metadata(edge, path, suppress_error=True) + return present def metadata(edge, path, suppress_error=False): @@ -43,10 +43,10 @@ def metadata(edge, path, suppress_error=False): def ensure_directory(edge, directory, suppress_error=False): - exists, resource = metadata(edge, directory, suppress_error=True) - if (not exists or not resource.is_dir) and not suppress_error: + present, resource = metadata(edge, directory, suppress_error=True) + if (not present or not resource.is_dir) and not suppress_error: raise NotADirectory(directory.absolute) - return resource.is_dir if exists else False, resource + return resource.is_dir if present else False, resource def mkdir(edge, path): From a18a62e5ba4f2fe8e48907f84efa7cf27a1520da Mon Sep 17 00:00:00 2001 From: Saimon Michelson Date: Sat, 7 Jun 2025 00:33:46 -0400 Subject: [PATCH 15/15] update to pass docs --- docs/source/UserGuides/Miscellaneous/Index.rst | 2 +- docs/source/api/cterasdk.direct.exceptions.rst | 7 ------- docs/source/api/cterasdk.direct.rst | 1 - 3 files changed, 1 insertion(+), 9 deletions(-) delete mode 100644 docs/source/api/cterasdk.direct.exceptions.rst diff --git a/docs/source/UserGuides/Miscellaneous/Index.rst b/docs/source/UserGuides/Miscellaneous/Index.rst index 581ffe42..daf0d709 100644 --- a/docs/source/UserGuides/Miscellaneous/Index.rst +++ b/docs/source/UserGuides/Miscellaneous/Index.rst @@ -85,7 +85,7 @@ I/O Notification Service -------------------- -.. autoclass:: cterasdk.exceptions.notifications.NotificationError +.. autoclass:: cterasdk.exceptions.notifications.NotificationsError :noindex: :members: :show-inheritance: diff --git a/docs/source/api/cterasdk.direct.exceptions.rst b/docs/source/api/cterasdk.direct.exceptions.rst deleted file mode 100644 index 796224d0..00000000 --- a/docs/source/api/cterasdk.direct.exceptions.rst +++ /dev/null @@ -1,7 +0,0 @@ -cterasdk.direct.exceptions module -================================= - -.. automodule:: cterasdk.direct.exceptions - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/source/api/cterasdk.direct.rst b/docs/source/api/cterasdk.direct.rst index 7425b3f5..64f9f747 100644 --- a/docs/source/api/cterasdk.direct.rst +++ b/docs/source/api/cterasdk.direct.rst @@ -12,7 +12,6 @@ Submodules .. toctree:: cterasdk.direct.client - cterasdk.direct.exceptions cterasdk.direct.stream cterasdk.direct.types