diff --git a/cterasdk/asynchronous/core/cloudfs.py b/cterasdk/asynchronous/core/cloudfs.py index 5df03293..802878a8 100644 --- a/cterasdk/asynchronous/core/cloudfs.py +++ b/cterasdk/asynchronous/core/cloudfs.py @@ -1,6 +1,7 @@ from .base_command import BaseCommand from . import query from ...core.query import QueryParamBuilder, FilterBuilder +from ...core.cloudfs import ZoneQueryParams, DevicesDelta, FoldersDelta from ...common.utils import union @@ -14,6 +15,7 @@ class CloudFS(BaseCommand): def __init__(self, core): super().__init__(core) self.drives = CloudDrives(self._core) + self.zones = Zones(self._core) class CloudDrives(BaseCommand): @@ -31,10 +33,59 @@ async def find(self, name, owner, include=None): :returns: A Cloud Drive Folder """ - user = await self._core.users.get(owner, ['uid']) include = union(include or [], CloudDrives.default) builder = QueryParamBuilder().include(include).ownedBy(user.uid) builder.addFilter(FilterBuilder('name').eq(name)) param = builder.build() return query.iterator(self._core, '/cloudDrives', param) + + +class Zones(BaseCommand): + """ + Portal Zones APIs + """ + + async def all(self, filters=None): + """ + List Zones + :param list[],optional filters: List of additional filters, defaults to None + + :return: Iterator for all Zones + :rtype: cterasdk.lib.iterator.QueryIterator + """ + builder = QueryParamBuilder().include_classname().startFrom(0).countLimit(25) + filters = filters or [] + for query_filter in filters: + builder.addFilter(query_filter) + builder.orFilter((len(filters) > 1)) + param = builder.build() + async for zone in query.iterator(self._core, '', param, 'getZonesDisplayInfo'): + yield zone + + async def list_zones(self, filters=None, expand_zone=False): + """ + List Zones + :param list[],optional filters: List of additional filters, defaults to None + :param bool,optional expand_zone: Include Cloud Drive folders and devices + + :return: Iterator for all Zones + :rtype: cterasdk.lib.iterator.QueryIterator + """ + async for zone in self.all(filters): + if expand_zone: + info = await self._core.v1.api.execute('', 'getZoneBasicInfo', zone.zoneId) + zone.devices = [device async for device in query.iterator(self._core, '', + ZoneQueryParams(info.zoneId, DevicesDelta()), 'getZoneDevices')] + if info.policyType == 'selectedFolders': + zone.cloudfolders = [ + volume + async for volume in query.iterator( + self._core, + '', + ZoneQueryParams(info.zoneId, FoldersDelta()), + 'getZoneFolders', + ) + ] + yield zone + yield zone diff --git a/cterasdk/asynchronous/core/devices.py b/cterasdk/asynchronous/core/devices.py new file mode 100644 index 00000000..7ebee8e2 --- /dev/null +++ b/cterasdk/asynchronous/core/devices.py @@ -0,0 +1,36 @@ +from .base_command import BaseCommand +from . import remote +from ...core.query import QueryParamBuilder +from . import query +from ...common import union + + +class Devices(BaseCommand): + """ Portal Devices APIs """ + + async def devices(self, include=None, allPortals=False, filters=None, user=None): + """ + Get Devices + + :param list[str],optional include: List of fields to retrieve, defaults to ['name', 'portal', 'deviceType'] + :param bool,optional allPortals: Search in all portals, defaults to False + :param list[],optional filters: List of additional filters, defaults to None + :param cterasdk.core.types.UserAccount user: User account of the device owner + + :return: Iterator for all matching Devices + :rtype: cterasdk.lib.iterator.QueryIterator + """ + include = union(include or [], ['name', 'portal', 'deviceType', 'version', 'remoteAccessUrl']) + builder = QueryParamBuilder().include(include).allPortals(allPortals) + filters = filters or [] + for query_filter in filters: + builder.addFilter(query_filter) + if user: + uid = self._core.users.get(user, ['uid']).uid + builder.ownedBy(uid) + builder.orFilter((len(filters) > 1)) + param = builder.build() + + iterator = query.iterator(self._core, '/devices', param) + async for dev in iterator: + yield remote.remote_command(self._core, dev) diff --git a/cterasdk/asynchronous/core/remote.py b/cterasdk/asynchronous/core/remote.py new file mode 100644 index 00000000..35f28329 --- /dev/null +++ b/cterasdk/asynchronous/core/remote.py @@ -0,0 +1,22 @@ +from ...core.enum import DeviceType +from ...objects.asynchronous import edge, drive +from ...common import parse_base_object_ref + + +def remote_command(core, device): + tenant = parse_base_object_ref(device.portal).name + base = f'{core.v1.ctera.baseurl}/devicecmdnew/{tenant}/{device.name}' + + ManagedDevice = None + if device.deviceType in DeviceType.Gateways: + ManagedDevice = edge.AsyncEdge(core=core, base=base) + elif device.deviceType in DeviceType.Agents: + ManagedDevice = drive.AsyncDrive(core=core, base=base) + elif device.deviceType == "Mobile": + return device + else: + return device + + ManagedDevice.__dict__.update(device.__dict__.copy()) + + return ManagedDevice diff --git a/cterasdk/asynchronous/edge/remote.py b/cterasdk/asynchronous/edge/remote.py new file mode 100644 index 00000000..53d1bd43 --- /dev/null +++ b/cterasdk/asynchronous/edge/remote.py @@ -0,0 +1,34 @@ +import re +import logging + +from ...common import parse_base_object_ref +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 + 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, {Portal._session_id_key: Portal.get_session_id()}) # pylint: disable=protected-access + logger.info("Enabled remote access. %s", {'tenant': device_tenant, 'device': device_name}) + return device_object + + +def create_device_object(device): + device_object = device.__class__(base=re.sub(r'^http(?=:)', 'https', device.remoteAccessUrl)) + return device_object + + +def authn_token(Portal, device_tenant, 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: + logger.error('Failed to Retrieve SSO Ticket. %s', {'tenant': device_tenant, 'device': device_name}) + raise CTERAException('Failed to Retrieve SSO Ticket.') + logger.debug("Retrieved SSO Ticket. %s", {'tenant': device_tenant, 'device': device_name}) + return token diff --git a/cterasdk/clients/clients.py b/cterasdk/clients/clients.py index 3df1e8ce..4f2957fe 100644 --- a/cterasdk/clients/clients.py +++ b/cterasdk/clients/clients.py @@ -117,6 +117,15 @@ async def delete(self, path, **kwargs): return await response.json() +class AsyncMigrate(AsyncJSON): + """CTERA Migrate Service""" + + async def login(self): + response = await Client.get(self, '/auth/user', on_error=JSONHandler()) + self.headers.persist_response_header(response, 'x-mt-x') + return response.json() + + class AsyncXML(AsyncClient): async def get(self, path, **kwargs): diff --git a/cterasdk/core/cloudfs.py b/cterasdk/core/cloudfs.py index 7e47905c..6dc91b83 100644 --- a/cterasdk/core/cloudfs.py +++ b/cterasdk/core/cloudfs.py @@ -5,7 +5,7 @@ from .base_command import BaseCommand from . import query, devices from .enum import ListFilter, PolicyType -from .types import ArchiveSettingsBuilder, ComplianceSettingsBuilder, ExtendedAttributesBuilder +from .types import ArchiveSettingsBuilder, ComplianceSettingsBuilder, ExtendedAttributesBuilder, DevicesDelta, FoldersDelta from ..common import union, Object from ..exceptions import CTERAException, ObjectNotFoundException @@ -519,6 +519,19 @@ def delete(self, name): return response +class ZoneQueryParams(Object): + + def __init__(self, zone_id, delta): + super().__init__() + self._classname = 'ZoneQuery' + self.zoneId = zone_id + self.query = query.QueryParamBuilder().include_classname().orFilter(True).build() + self.delta = delta + + def increment(self): + return self.query.increment() + + class Zones(BaseCommand): """ Portal Zones APIs @@ -565,6 +578,25 @@ def all(self, filters=None): param = builder.build() return query.iterator(self._core, '', param, 'getZonesDisplayInfo') + def list_zones(self, filters=None, expand_zone=False): + """ + List Zones + :param list[],optional filters: List of additional filters, defaults to None + :param bool,optional expand_zone: Include Cloud Drive folders and devices + + :return: Iterator for all Zones + :rtype: cterasdk.lib.iterator.QueryIterator + """ + for zone in self.all(filters): + if expand_zone: + info = self._core.api.execute('', 'getZoneBasicInfo', zone.zoneId) + zone.devices = list(query.iterator(self._core, '', ZoneQueryParams(info.zoneId, DevicesDelta()), 'getZoneDevices')) + if info.policyType == 'selectedFolders': + zone.cloudfolders = list(query.iterator(self._core, '', + ZoneQueryParams(info.zoneId, FoldersDelta()), 'getZoneFolders')) + yield zone + yield zone + def search(self, name): """ Search for Zones by name diff --git a/cterasdk/core/remote.py b/cterasdk/core/remote.py index 432f7c35..91f4c17e 100644 --- a/cterasdk/core/remote.py +++ b/cterasdk/core/remote.py @@ -3,15 +3,15 @@ from ..common import parse_base_object_ref -def remote_command(Portal, device): +def remote_command(core, device): tenant = parse_base_object_ref(device.portal).name - base = f'{Portal.ctera.baseurl}/devicecmdnew/{tenant}/{device.name}' + base = f'{core.ctera.baseurl}/devicecmdnew/{tenant}/{device.name}' ManagedDevice = None if device.deviceType in DeviceType.Gateways: - ManagedDevice = edge.Edge(Portal=Portal, base=base) + ManagedDevice = edge.Edge(core=core, base=base) elif device.deviceType in DeviceType.Agents: - ManagedDevice = drive.Drive(Portal=Portal, base=base) + ManagedDevice = drive.Drive(core=core, base=base) elif device.deviceType == "Mobile": return device else: diff --git a/cterasdk/core/types.py b/cterasdk/core/types.py index 4f7385dc..9094450d 100644 --- a/cterasdk/core/types.py +++ b/cterasdk/core/types.py @@ -1270,3 +1270,28 @@ def __init__(self, operator, value): @staticmethod def contains(value): return ContentFilter(SearchOperator.CONTAINS, value) + + +class ZoneDelta(Object): + + def __init__(self): + super().__init__() + self._classname = 'ZoneDelta' + + +class FoldersDelta(ZoneDelta): + + def __init__(self): + super().__init__() + self.policyDelta = [] + + +class DevicesDelta(ZoneDelta): + + def __init__(self): + super().__init__() + self.devicesDelta = Object( + _classname='ZoneDeviceDelta', + added=[], + removed=[] + ) diff --git a/cterasdk/edge/shares.py b/cterasdk/edge/shares.py index 453488a7..ff4053bc 100644 --- a/cterasdk/edge/shares.py +++ b/cterasdk/edge/shares.py @@ -2,7 +2,7 @@ from . import enum from ..cio.edge.types import automatic_resolution -from ..common import Object +from ..common import Object, BaseModule from ..exceptions import CTERAException, InputError from .base_command import BaseCommand from .types import NFSv3AccessControlEntry, RemoveNFSv3AccessControlEntry, ShareAccessControlEntry, RemoveShareAccessControlEntry @@ -11,6 +11,12 @@ logger = logging.getLogger('cterasdk.edge') +class SharesModule(BaseModule): + + def initialize_version(self, software_version): + return SharesV7 if software_version >= '7.12.5400' else SharesV1 + + class Shares(BaseCommand): def get(self, name=None): @@ -20,77 +26,6 @@ def get(self, name=None): """ return self._edge.api.get('/config/fileservices/share' + ('' if name is None else ('/' + name))) - def add(self, - name, - directory, - acl=None, - access=enum.Acl.WindowsNT, - csc=enum.ClientSideCaching.Manual, - dir_permissions=777, - comment=None, - export_to_afp=False, - export_to_ftp=False, - export_to_nfs=False, - export_to_pc_agent=False, - export_to_rsync=False, - indexed=False, - trusted_nfs_clients=None, - uuid=None - ): # pylint: disable=too-many-arguments,too-many-locals,unused-argument - """ - Add a network share. - - :param str name: The share name - :param str directory: Full directory path - :param list[cterasdk.edge.types.ShareAccessControlEntry] acl: List of access control entries - :param cterasdk.edge.enum.Acl access: The Windows File Sharing authentication mode, defaults to ``winAclMode`` - :param cterasdk.edge.enum.ClientSideCaching csc: The client side caching (offline files) configuration, defaults to ``manual`` - :param int dir_permissions: Directory Permission, defaults to 777 - :param str comment: Comment - :param bool export_to_afp: Whether to enable AFP access, defaults to ``False`` - :param bool export_to_ftp: Whether to enable FTP access, defaults to ``False`` - :param bool export_to_nfs: Whether to enable NFS access, defaults to ``False`` - :param bool export_to_pc_agent: Whether to allow as a destination share for CTERA Backup Agents, defaults to ``False`` - :param bool export_to_rsync: Whether to enable access over rsync, defaults to ``False`` - :param bool indexed: Whether to enable indexing for search, defaults to ``False`` - :param list[cterasdk.edge.types.NFSv3AccessControlEntry] trusted_nfs_clients: Trusted NFS v3 clients, defaults to ``None`` - """ - acl = acl or [] - - param = Object() - param.name = name - - parts = automatic_resolution(directory).parts - volume = parts[0] - self._validate_root_directory(volume) - param.volume = volume - - directory = '/'.join(parts[1:]) - param.directory = directory - - param.access = access - param.clientSideCaching = csc - param.dirPermissions = dir_permissions - param.exportToAFP = export_to_afp - param.exportToFTP = export_to_ftp - param.exportToNFS = export_to_nfs - param.exportToPCAgent = export_to_pc_agent - param.exportToRSync = export_to_rsync - param.indexed = indexed - param.comment = comment - Shares._validate_acl(acl) - param.acl = [acl_entry.to_server_object() for acl_entry in acl] - param.trustedNFSClients = [client.to_server_object() for client in (trusted_nfs_clients or [])] - if uuid: - param._uuid = uuid # pylint: disable=protected-access - - try: - self._edge.api.add('/config/fileservices/share', param) - 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): """ Set a network share to use Windows ACL Emulation Mode @@ -202,83 +137,6 @@ def get_acl(self, name): """ return self._edge.api.get('/config/fileservices/share/' + name + '/acl') - def modify( - self, - name, - directory=None, - acl=None, - access=None, - csc=None, - dir_permissions=None, - comment=None, - export_to_afp=None, - export_to_ftp=None, - export_to_nfs=None, - export_to_pc_agent=None, - export_to_rsync=None, - indexed=None, - trusted_nfs_clients=None - ): # pylint: disable=too-many-arguments,too-many-locals,too-many-branches,unused-argument - """ - Modify an existing network share. All parameters but name are optional and default to None - - :param str name: The share name - :param str,optional directory: Full directory path - :param list[cterasdk.edge.types.ShareAccessControlEntry],optional acl: List of access control entries - :param cterasdk.edge.enum.Acl,optional access: The Windows File Sharing authentication mode - :param cterasdk.edge.enum.ClientSideCaching,optional csc: The client side caching (offline files) configuration - :param int,optional dir_permissions: Directory Permission - :param str,optional comment: Comment - :param bool,optional export_to_afp: Whether to enable AFP access - :param bool,optional export_to_ftp: Whether to enable FTP access - :param bool,optional export_to_nfs: Whether to enable NFS access - :param bool,optional export_to_pc_agent: Whether to allow as a destination share for CTERA Backup Agents - :param bool,optional export_to_rsync: Whether to enable access over rsync - :param bool,optional indexed: Whether to enable indexing for search - :param list[cterasdk.edge.types.NFSv3AccessControlEntry] trusted_nfs_clients: Trusted NFS v3 clients, defaults to ``None`` - """ - share = self.get(name=name) - if directory is not None: - parts = automatic_resolution(directory).parts - volume = parts[0] - self._validate_root_directory(volume) - share.volume = volume - directory = '/'.join(parts[1:]) - share.directory = directory - if access is not None: - share.access = access - if csc is not None: - share.clientSideCaching = csc - if dir_permissions is not None: - share.dirPermissions = dir_permissions - if export_to_afp is not None: - share.exportToAFP = export_to_afp - if export_to_ftp is not None: - share.exportToFTP = export_to_ftp - if export_to_nfs is not None: - share.exportToNFS = export_to_nfs - if export_to_pc_agent is not None: - share.exportToPCAgent = export_to_pc_agent - if export_to_rsync is not None: - share.exportToRSync = export_to_rsync - if indexed is not None: - share.indexed = indexed - if comment is not None: - share.comment = comment - if acl is not None: - Shares._validate_acl(acl) - share.acl = [acl_entry.to_server_object() for acl_entry in acl] - if trusted_nfs_clients is not None: - share.trustedNFSClients = [client.to_server_object() for client in trusted_nfs_clients] - - try: - self._edge.api.put('/config/fileservices/share/' + name, share) - logger.info("Share modified. %s", {'name': name}) - except CTERAException as error: - message = f'Share modification failed: {name}' - logger.error(message) - raise CTERAException(message) from error - def delete(self, name): """ Delete a share. @@ -485,3 +343,196 @@ def _validate_remove_trusted_nfs_clients(trusted_nfs_clients): repr(entry), 'cterasdk.edge.types.RemoveNFSv3AccessControlEntry' ) + + def _add_share_param(self, name, directory, acl=None, # pylint: disable=too-many-arguments,too-many-locals,unused-argument + access=enum.Acl.WindowsNT, csc=enum.ClientSideCaching.Manual, + comment=None, export_to_afp=False, export_to_ftp=False, export_to_nfs=False, indexed=False, + trusted_nfs_clients=None, uuid=None): + acl = acl or [] + + param = Object() + param.name = name + + parts = automatic_resolution(directory).parts + volume = parts[0] + self._validate_root_directory(volume) + param.volume = volume + + directory = '/'.join(parts[1:]) + param.directory = directory + + param.access = access + param.clientSideCaching = csc + param.exportToAFP = export_to_afp + param.exportToFTP = export_to_ftp + param.exportToNFS = export_to_nfs + param.indexed = indexed + param.comment = comment + Shares._validate_acl(acl) + param.acl = [acl_entry.to_server_object() for acl_entry in acl] + param.trustedNFSClients = [client.to_server_object() for client in (trusted_nfs_clients or [])] + if uuid: + param._uuid = uuid # pylint: disable=protected-access + return param + + def _add(self, param): + try: + self._edge.api.add('/config/fileservices/share', param) + 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 _modify_share_param( + self, + name, + directory=None, + acl=None, + access=None, + csc=None, + comment=None, + export_to_afp=None, + export_to_ftp=None, + export_to_nfs=None, + indexed=None, + trusted_nfs_clients=None, + ): # pylint: disable=too-many-arguments,too-many-locals,too-many-branches,unused-argument + share = self.get(name=name) + if directory is not None: + parts = automatic_resolution(directory).parts + volume = parts[0] + self._validate_root_directory(volume) + share.volume = volume + directory = '/'.join(parts[1:]) + share.directory = directory + if access is not None: + share.access = access + if csc is not None: + share.clientSideCaching = csc + if export_to_afp is not None: + share.exportToAFP = export_to_afp + if export_to_ftp is not None: + share.exportToFTP = export_to_ftp + if export_to_nfs is not None: + share.exportToNFS = export_to_nfs + if indexed is not None: + share.indexed = indexed + if comment is not None: + share.comment = comment + if acl is not None: + Shares._validate_acl(acl) + share.acl = [acl_entry.to_server_object() for acl_entry in acl] + if trusted_nfs_clients is not None: + share.trustedNFSClients = [client.to_server_object() for client in trusted_nfs_clients] + return share + + def _modify(self, name, param): # pylint: disable=too-many-arguments,too-many-locals,too-many-branches,unused-argument + try: + self._edge.api.put('/config/fileservices/share/' + name, param) + logger.info("Share modified. %s", {'name': name}) + except CTERAException as error: + message = f'Share modification failed: {name}' + logger.error(message) + raise CTERAException(message) from error + + +class SharesV7(Shares): + + def add(self, name, directory, acl=None, # pylint: disable=too-many-arguments,too-many-locals,unused-argument + access=enum.Acl.WindowsNT, csc=enum.ClientSideCaching.Manual, + comment=None, export_to_afp=False, export_to_ftp=False, export_to_nfs=False, indexed=False, + trusted_nfs_clients=None, uuid=None): + """ + Add a network share. + + :param str name: The share name + :param str directory: Full directory path + :param list[cterasdk.edge.types.ShareAccessControlEntry] acl: List of access control entries + :param cterasdk.edge.enum.Acl access: The Windows File Sharing authentication mode, defaults to ``winAclMode`` + :param cterasdk.edge.enum.ClientSideCaching csc: The client side caching (offline files) configuration, defaults to ``manual`` + :param str comment: Comment + :param bool export_to_afp: Whether to enable AFP access, defaults to ``False`` + :param bool export_to_ftp: Whether to enable FTP access, defaults to ``False`` + :param bool export_to_nfs: Whether to enable NFS access, defaults to ``False`` + :param bool indexed: Whether to enable indexing for search, defaults to ``False`` + :param list[cterasdk.edge.types.NFSv3AccessControlEntry] trusted_nfs_clients: Trusted NFS v3 clients, defaults to ``None`` + """ + param = self._add_share_param(name, directory, acl, access, csc, comment, export_to_afp, export_to_ftp, export_to_nfs, + indexed, trusted_nfs_clients, uuid) + return self._add(param) + + def modify(self, name, directory=None, # pylint: disable=too-many-arguments,too-many-locals,too-many-branches,unused-argument + acl=None, access=None, csc=None, comment=None, + export_to_afp=None, export_to_ftp=None, export_to_nfs=None, indexed=None, + trusted_nfs_clients=None): + param = self._modify_share_param(name, directory, acl, access, csc, comment, export_to_afp, export_to_ftp, + export_to_nfs, indexed, trusted_nfs_clients) + return self._modify(name, param) + + +class SharesV1(Shares): + + def add(self, name, directory, acl=None, # pylint: disable=too-many-arguments,too-many-locals,unused-argument + access=enum.Acl.WindowsNT, csc=enum.ClientSideCaching.Manual, + dir_permissions=777, comment=None, export_to_afp=False, export_to_ftp=False, export_to_nfs=False, + export_to_pc_agent=False, export_to_rsync=False, indexed=False, + trusted_nfs_clients=None, uuid=None): + """ + Add a network share. + + :param str name: The share name + :param str directory: Full directory path + :param list[cterasdk.edge.types.ShareAccessControlEntry] acl: List of access control entries + :param cterasdk.edge.enum.Acl access: The Windows File Sharing authentication mode, defaults to ``winAclMode`` + :param cterasdk.edge.enum.ClientSideCaching csc: The client side caching (offline files) configuration, defaults to ``manual`` + :param int dir_permissions: Directory Permission, defaults to 777 + :param str comment: Comment + :param bool export_to_afp: Whether to enable AFP access, defaults to ``False`` + :param bool export_to_ftp: Whether to enable FTP access, defaults to ``False`` + :param bool export_to_nfs: Whether to enable NFS access, defaults to ``False`` + :param bool export_to_pc_agent: Whether to allow as a destination share for CTERA Backup Agents, defaults to ``False`` + :param bool export_to_rsync: Whether to enable access over rsync, defaults to ``False`` + :param bool indexed: Whether to enable indexing for search, defaults to ``False`` + :param list[cterasdk.edge.types.NFSv3AccessControlEntry] trusted_nfs_clients: Trusted NFS v3 clients, defaults to ``None`` + """ + param = self._add_share_param(name, directory, acl, access, csc, comment, export_to_afp, export_to_ftp, export_to_nfs, + indexed, trusted_nfs_clients, uuid) + param.dirPermissions = dir_permissions + param.exportToPCAgent = export_to_pc_agent + param.exportToRSync = export_to_rsync + return self._add(param) + + def modify(self, name, directory=None, # pylint: disable=too-many-arguments,too-many-locals,too-many-branches,unused-argument + acl=None, access=None, csc=None, dir_permissions=None, + comment=None, export_to_afp=None, export_to_ftp=None, export_to_nfs=None, + export_to_pc_agent=None, export_to_rsync=None, indexed=None, + trusted_nfs_clients=None): + """ + Modify an existing network share. All parameters but name are optional and default to None + + :param str name: The share name + :param str,optional directory: Full directory path + :param list[cterasdk.edge.types.ShareAccessControlEntry],optional acl: List of access control entries + :param cterasdk.edge.enum.Acl,optional access: The Windows File Sharing authentication mode + :param cterasdk.edge.enum.ClientSideCaching,optional csc: The client side caching (offline files) configuration + :param int,optional dir_permissions: Directory Permission + :param str,optional comment: Comment + :param bool,optional export_to_afp: Whether to enable AFP access + :param bool,optional export_to_ftp: Whether to enable FTP access + :param bool,optional export_to_nfs: Whether to enable NFS access + :param bool,optional export_to_pc_agent: Whether to allow as a destination share for CTERA Backup Agents + :param bool,optional export_to_rsync: Whether to enable access over rsync + :param bool,optional indexed: Whether to enable indexing for search + :param list[cterasdk.edge.types.NFSv3AccessControlEntry] trusted_nfs_clients: Trusted NFS v3 clients, defaults to ``None`` + """ + param = self._modify_share_param(name, directory, acl, access, csc, comment, export_to_afp, export_to_ftp, + export_to_nfs, indexed, trusted_nfs_clients) + if dir_permissions is not None: + param.dirPermissions = dir_permissions + if export_to_nfs is not None: + param.exportToNFS = export_to_nfs + if export_to_pc_agent is not None: + param.exportToPCAgent = export_to_pc_agent + if export_to_rsync is not None: + param.exportToRSync = export_to_rsync + return self._modify(name, param) diff --git a/cterasdk/objects/asynchronous/core.py b/cterasdk/objects/asynchronous/core.py index 723d7d96..8a1564a1 100644 --- a/cterasdk/objects/asynchronous/core.py +++ b/cterasdk/objects/asynchronous/core.py @@ -4,7 +4,7 @@ from ...clients import clients from .. import authenticators from ...lib.session.core import Session -from ...asynchronous.core import files, login, cloudfs, notifications, portals, roles, settings, tasks, users +from ...asynchronous.core import files, login, cloudfs, devices, notifications, portals, roles, settings, tasks, users class Clients: @@ -91,6 +91,7 @@ class AsyncGlobalAdmin(AsyncPortal): def __init__(self, host, port=None, https=True): super().__init__(host, port, https) self.portals = portals.Portals(self) + self.devices = devices.Devices(self) @property def context(self): diff --git a/cterasdk/objects/asynchronous/drive.py b/cterasdk/objects/asynchronous/drive.py new file mode 100644 index 00000000..80fed858 --- /dev/null +++ b/cterasdk/objects/asynchronous/drive.py @@ -0,0 +1,31 @@ +import cterasdk.settings +from ..services import AsyncManagement +from ..endpoints import EndpointBuilder +from ...clients import clients +from ...lib.session.edge import Session + + +class Clients: + + def __init__(self, drive, core): + if core: + drive.session().start_remote_session(core.session()) + self.api = core.default.clone(clients.AsyncAPI, EndpointBuilder.new(drive.base), authenticator=lambda *_: True) + else: + self.api = drive.default.clone(clients.AsyncAPI, EndpointBuilder.new(drive.base, '/admingui/api')) + + +class AsyncDrive(AsyncManagement): + + def __init__(self, host=None, port=None, https=True, core=None, *, base=None): + super().__init__(host, port, https, base, cterasdk.settings.edge.asyn.settings, core=core) + self._ctera_session = Session(self.host()) + self._ctera_clients = Clients(self, core) + + @property + def api(self): + return self.clients.api + + @property + def _login_object(self): + raise NotImplementedError("Logins to the 'Drive App' are not enabled.") diff --git a/cterasdk/objects/asynchronous/edge.py b/cterasdk/objects/asynchronous/edge.py index dd4f36ce..f8cc6950 100644 --- a/cterasdk/objects/asynchronous/edge.py +++ b/cterasdk/objects/asynchronous/edge.py @@ -9,9 +9,18 @@ class Clients: - def __init__(self, edge): - self.api = edge.default.clone(clients.AsyncAPI, EndpointBuilder.new(edge.base, '/admingui/api')) - self.io = IO(edge) + def __init__(self, edge, core): + if core: + edge.session().start_remote_session(core.session()) + self.migrate = clients.RestrictedAPI('migrate') + self.api = edge.default.clone(clients.AsyncAPI, EndpointBuilder.new(edge.base), authenticator=lambda *_: True) + self.stats = clients.RestrictedAPI('stats') + self.io = clients.RestrictedAPI('io') + else: + self.migrate = edge.default.clone(clients.AsyncMigrate, EndpointBuilder.new(edge.base, '/migration/rest/v1')) + self.api = edge.default.clone(clients.AsyncAPI, EndpointBuilder.new(edge.base, '/admingui/api')) + self.stats = edge.default.clone(clients.AsyncJSON, EndpointBuilder.new(edge.base, '/stats')) + self.io = IO(edge) class IO: @@ -55,16 +64,12 @@ def delete(self): class AsyncEdge(AsyncManagement): - def __init__(self, host=None, port=None, https=True, *, base=None): - super().__init__(host, port, https, base, cterasdk.settings.edge.asyn.settings) + def __init__(self, host=None, port=None, https=True, core=None, *, base=None): + super().__init__(host, port, https, base, cterasdk.settings.edge.asyn.settings, core=core) self._ctera_session = Session(self.host()) - self._ctera_clients = Clients(self) + self._ctera_clients = Clients(self, core) self.files = files.FileBrowser(self) - @property - def v1(self): - return self.clients.v1 - @property def api(self): return self.clients.api @@ -79,3 +84,7 @@ def _login_object(self): def _authenticator(self, url): return authenticators.edge(self.session(), url) + + @property + def _omit_fields(self): + return super()._omit_fields + ['files'] diff --git a/cterasdk/objects/services.py b/cterasdk/objects/services.py index 75c5a700..b1312bc5 100644 --- a/cterasdk/objects/services.py +++ b/cterasdk/objects/services.py @@ -86,9 +86,11 @@ class AsyncManagement(CTERA): # pylint: disable=abstract-method async def __aenter__(self): return self - def __init__(self, host, port, https, base, settings): + def __init__(self, host, port, https, base, settings, *, core=None): super().__init__(host, port, https, base) - self._default = clients.AsyncClient(endpoints.EndpointBuilder.new(self.base), settings=settings, authenticator=self._authenticator) + self._core = core + self._default = core.default if core else clients.AsyncClient(endpoints.EndpointBuilder.new(self.base), + settings=settings, authenticator=self._authenticator) async def login(self, username, password): self._before_login() @@ -111,9 +113,11 @@ class Management(CTERA): def __enter__(self): return self - def __init__(self, host, port, https, base, settings): + def __init__(self, host, port, https, base, settings, *, core=None): super().__init__(host, port, https, base) - self._default = clients.Client(endpoints.EndpointBuilder.new(self.base), settings=settings, authenticator=self._authenticator) + self._core = core + self._default = core.default if core else clients.Client(endpoints.EndpointBuilder.new(self.base), + settings=settings, authenticator=self._authenticator) def login(self, username, password): """ diff --git a/cterasdk/objects/synchronous/drive.py b/cterasdk/objects/synchronous/drive.py index 2ab59a78..0d989d7c 100644 --- a/cterasdk/objects/synchronous/drive.py +++ b/cterasdk/objects/synchronous/drive.py @@ -8,22 +8,20 @@ class Clients: - def __init__(self, drive, Portal): - if Portal: - drive._Portal = Portal - drive.default.close() - drive._ctera_session.start_remote_session(Portal.session()) - self.api = Portal.default.clone(clients.API, EndpointBuilder.new(drive.base), authenticator=lambda *_: True) + def __init__(self, drive, core): + if core: + drive.session().start_remote_session(core.session()) + self.api = drive.default.clone(clients.API, EndpointBuilder.new(drive.base), authenticator=lambda *_: True) else: self.api = drive.default.clone(clients.API, EndpointBuilder.new(drive.base, '/admingui/api')) class Drive(Management): - def __init__(self, host=None, port=None, https=True, Portal=None, *, base=None): - super().__init__(host, port, https, base, cterasdk.settings.drive.syn.settings) + def __init__(self, host=None, port=None, https=True, core=None, *, base=None): + super().__init__(host, port, https, base, cterasdk.settings.drive.syn.settings, core=core) self._ctera_session = Session(self.host()) - self._ctera_clients = Clients(self, Portal) + self._ctera_clients = Clients(self, core) self.backup = backup.Backup(self) self.cli = cli.CLI(self) self.logs = logs.Logs(self) diff --git a/cterasdk/objects/synchronous/edge.py b/cterasdk/objects/synchronous/edge.py index 54d032a4..dcecda24 100644 --- a/cterasdk/objects/synchronous/edge.py +++ b/cterasdk/objects/synchronous/edge.py @@ -18,13 +18,11 @@ class Clients: - def __init__(self, edge, Portal): - if Portal: - edge._Portal = Portal - edge.default.close() - edge._ctera_session.start_remote_session(Portal.session()) + def __init__(self, edge, core): + if core: + edge.session().start_remote_session(core.session()) self.migrate = clients.RestrictedAPI('migrate') - self.api = Portal.default.clone(clients.API, EndpointBuilder.new(edge.base), authenticator=lambda *_: True) + self.api = edge.default.clone(clients.API, EndpointBuilder.new(edge.base), authenticator=lambda *_: True) self.stats = clients.RestrictedAPI('stats') self.io = clients.RestrictedAPI('io') else: @@ -75,10 +73,10 @@ def delete(self): class Edge(Management): # pylint: disable=too-many-instance-attributes - def __init__(self, host=None, port=None, https=True, Portal=None, *, base=None): - super().__init__(host, port, https, base, cterasdk.settings.edge.syn.settings) + def __init__(self, host=None, port=None, https=True, core=None, *, base=None): + super().__init__(host, port, https, base, cterasdk.settings.edge.syn.settings, core=core) self._ctera_session = Session(self.host()) - self._ctera_clients = Clients(self, Portal) + self._ctera_clients = Clients(self, core) self.afp = afp.AFP(self) self.aio = aio.AIO(self) self.antivirus = antivirus.Antivirus(self) @@ -106,7 +104,7 @@ def __init__(self, host=None, port=None, https=True, Portal=None, *, base=None): self.ransom_protect = ransom_protect.RansomProtect(self) self.rsync = rsync.RSync(self) self.services = services.Services(self) - self.shares = shares.Shares(self) + self.shares = modules.initialize(shares.SharesModule, self) self.shell = shell.Shell(self) self.smb = smb.SMB(self) self.snmp = snmp.SNMP(self) @@ -123,8 +121,9 @@ def __init__(self, host=None, port=None, https=True, Portal=None, *, base=None): self.volumes = volumes.Volumes(self) def _after_login(self): - self.ssl = modules.initialize(ssl.SSLModule, self) self.network = modules.initialize(network.NetworkModule, self) + self.shares = modules.initialize(shares.SharesModule, self) + self.ssl = modules.initialize(ssl.SSLModule, self) @property def migrate(self): @@ -168,7 +167,7 @@ def sso(self, ticket, session): self.session().start_session(self) def remote_access(self): - return remote.remote_access(self, self._Portal) + return remote.remote_access(self, self._core) @property def _omit_fields(self): diff --git a/docs/source/UserGuides/Miscellaneous/Changelog.rst b/docs/source/UserGuides/Miscellaneous/Changelog.rst index 347daa7b..8c82b036 100644 --- a/docs/source/UserGuides/Miscellaneous/Changelog.rst +++ b/docs/source/UserGuides/Miscellaneous/Changelog.rst @@ -1,6 +1,18 @@ Changelog ========= +2.20.44 +======= + +Bug Fixes +^^^^^^^^^ + +- Support listing zone cloud folders and devices +- Support remote access via CTTP through asynchronous I/O (``asyncio``) +- Updated the Edge Filer shares module to accommodate the removal of the ``dirPermissions`` attribute. + +Related issues and pull requests on GitHub: `#365 `_ + 2.20.43 ------- diff --git a/tests/ut/edge/test_shares.py b/tests/ut/edge/test_shares.py index 5c5ea6dd..74ac5b3e 100644 --- a/tests/ut/edge/test_shares.py +++ b/tests/ut/edge/test_shares.py @@ -33,14 +33,14 @@ def setUp(self): def test_get_all_shares(self): get_response = 'Success' self._init_filer(get_response=get_response) - ret = shares.Shares(self._filer).get() + ret = shares.SharesV1(self._filer).get() self._filer.api.get.assert_called_once_with('/config/fileservices/share') self.assertEqual(ret, get_response) def test_get_share(self): get_response = 'Success' self._init_filer(get_response=get_response) - ret = shares.Shares(self._filer).get(self._share_name) + ret = shares.SharesV1(self._filer).get(self._share_name) self._filer.api.get.assert_called_once_with('/config/fileservices/share/' + self._share_name) self.assertEqual(ret, get_response) @@ -48,7 +48,7 @@ def test_add_cifs_share_default_config_without_acls(self): execute_response = self._get_list_physical_folders_response_object() self._init_filer(execute_response=execute_response) - shares.Shares(self._filer).add(self._share_name, self._share_fullpath, []) + shares.SharesV1(self._filer).add(self._share_name, self._share_fullpath, []) self._filer.api.execute.assert_called_once_with('/status/fileManager', 'listPhysicalFolders', mock.ANY) expected_param = self._get_list_physical_folders_param() @@ -64,7 +64,7 @@ def test_add_cifs_share_default_config_with_acls(self): execute_response = self._get_list_physical_folders_response_object() self._init_filer(execute_response=execute_response) - shares.Shares(self._filer).add(self._share_name, self._share_fullpath, self._share_acl) + shares.SharesV1(self._filer).add(self._share_name, self._share_fullpath, self._share_acl) self._filer.api.execute.assert_called_once_with('/status/fileManager', 'listPhysicalFolders', mock.ANY) expected_param = self._get_list_physical_folders_param() @@ -77,8 +77,8 @@ def test_add_nfs_v3_share_success(self): execute_response = self._get_list_physical_folders_response_object() self._init_filer(execute_response=execute_response) - shares.Shares(self._filer).add(self._share_name, self._share_fullpath, export_to_nfs=True, - trusted_nfs_clients=self._trusted_nfs_clients) + shares.SharesV1(self._filer).add(self._share_name, self._share_fullpath, export_to_nfs=True, + trusted_nfs_clients=self._trusted_nfs_clients) self._filer.api.execute.assert_called_once_with('/status/fileManager', 'listPhysicalFolders', mock.ANY) expected_param = self._get_list_physical_folders_param() @@ -95,7 +95,7 @@ def test_modify_nfs_v3_share_success(self): get_response = self._get_share_object(export_to_nfs=False) self._init_filer(get_response=get_response) - shares.Shares(self._filer).modify(self._share_name, export_to_nfs=True, trusted_nfs_clients=self._trusted_nfs_clients) + shares.SharesV1(self._filer).modify(self._share_name, export_to_nfs=True, trusted_nfs_clients=self._trusted_nfs_clients) self._filer.api.get.assert_called_once_with('/config/fileservices/share/' + self._share_name) self._filer.api.put.assert_called_once_with('/config/fileservices/share/' + self._share_name, mock.ANY) @@ -120,7 +120,7 @@ def test_add_share_failure(self): self._init_filer(execute_response=execute_response) self._filer.api.add = mock.MagicMock(side_effect=exceptions.CTERAException()) with self.assertRaises(exceptions.CTERAException) as error: - shares.Shares(self._filer).add(self._share_name, self._share_fullpath, []) + shares.SharesV1(self._filer).add(self._share_name, self._share_fullpath, []) self._filer.api.execute.assert_called_once_with('/status/fileManager', 'listPhysicalFolders', mock.ANY) expected_param = self._get_list_physical_folders_param() @@ -138,7 +138,7 @@ def test_list_physical_folders_input_error(self): execute_response = [] self._init_filer(execute_response=execute_response) with self.assertRaises(exceptions.InputError) as error: - shares.Shares(self._filer).add(self._share_name, self._share_fullpath, []) + shares.SharesV1(self._filer).add(self._share_name, self._share_fullpath, []) self._filer.api.execute.assert_called_once_with('/status/fileManager', 'listPhysicalFolders', mock.ANY) expected_param = self._get_list_physical_folders_param() @@ -150,13 +150,13 @@ def test_list_physical_folders_input_error(self): def test_set_share_winacls(self): put_response = 'Success' self._init_filer(put_response=put_response) - shares.Shares(self._filer).set_share_winacls(self._share_name) + shares.SharesV1(self._filer).set_share_winacls(self._share_name) self._filer.api.put.assert_called_once_with('/config/fileservices/share/' + self._share_name + '/access', Acl.WindowsNT) def test_block_files_success(self): get_response = self._get_share_object() self._init_filer(get_response=get_response) - shares.Shares(self._filer).block_files(self._share_name, self._share_block_files) + shares.SharesV1(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._filer.api.put.assert_called_once_with( '/config/fileservices/share/' + self._share_name + '/screenedFileTypes', @@ -167,19 +167,19 @@ def test_block_files_invalid_share_access_type(self): get_response = self._get_share_object(access='Expected Failure') self._init_filer(get_response=get_response) with self.assertRaises(exceptions.CTERAException) as error: - shares.Shares(self._filer).block_files(self._share_name, self._share_block_files) + shares.SharesV1(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.', str(error.exception)) def test_delete_share_success(self): self._init_filer() - shares.Shares(self._filer).delete(self._share_name) + shares.SharesV1(self._filer).delete(self._share_name) self._filer.api.delete.assert_called_once_with('/config/fileservices/share/' + self._share_name) 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) + shares.SharesV1(self._filer).delete(self._share_name) self.assertEqual(f'Share deletion failed: /config/fileservices/share/{self._share_name}', str(error.exception)) def test_modify(self): @@ -199,7 +199,7 @@ def test_modify(self): csc=ClientSideCaching.Disabled ) expected_param = self._get_share_object(**modify_command_dict) - shares.Shares(self._filer).modify(self._share_name, **modify_command_dict) + shares.SharesV1(self._filer).modify(self._share_name, **modify_command_dict) self._filer.api.get.assert_called_once_with('/config/fileservices/share/' + self._share_name) self._filer.api.put.assert_called_once_with('/config/fileservices/share/' + self._share_name, mock.ANY) actual_param = self._filer.api.put.call_args[0][1] @@ -244,7 +244,7 @@ def test_get_acl(self): share_name = 'share' get_response = self._get_acl_object() self._init_filer(get_response=[get_response.to_server_object()]) - acl = shares.Shares(self._filer).get_acl(share_name) + acl = shares.SharesV1(self._filer).get_acl(share_name) self._filer.api.get.assert_called_once_with('/config/fileservices/share/' + share_name + '/acl') self._assert_equal_objects(ShareAccessControlEntry.from_server_object(acl[0]), get_response) @@ -256,7 +256,7 @@ def test_get_trusted_nfs_clients(self): share_name = 'share' get_response = self._get_get_trusted_nfs_client_object() self._init_filer(get_response=[get_response.to_server_object()]) - trusted_nfs_clients = shares.Shares(self._filer).get_trusted_nfs_clients(share_name) + trusted_nfs_clients = shares.SharesV1(self._filer).get_trusted_nfs_clients(share_name) self._filer.api.get.assert_called_once_with('/config/fileservices/share/' + share_name + '/trustedNFSClients') self._assert_equal_objects(NFSv3AccessControlEntry.from_server_object(trusted_nfs_clients[0]), get_response) @@ -264,7 +264,7 @@ def test_set_trusted_nfs_clients(self): share_name = 'share' new_trusted_nfs_clients = self._get_get_trusted_nfs_client_object() self._init_filer() - shares.Shares(self._filer).set_trusted_nfs_clients(share_name, [new_trusted_nfs_clients]) + shares.SharesV1(self._filer).set_trusted_nfs_clients(share_name, [new_trusted_nfs_clients]) self._filer.api.put.assert_called_once_with('/config/fileservices/share/' + share_name + '/trustedNFSClients', mock.ANY) expected_param = new_trusted_nfs_clients.to_server_object() actual_param = self._filer.api.put.call_args[0][1][0] @@ -276,7 +276,7 @@ def test_add_trusted_nfs_clients(self): self._init_filer(get_response=[current_trusted_nfs_clients.to_server_object()]) new_trusted_nfs_clients = self._get_get_trusted_nfs_client_object(address="192.168.0.0") - shares.Shares(self._filer).add_trusted_nfs_clients(share_name, [new_trusted_nfs_clients]) + shares.SharesV1(self._filer).add_trusted_nfs_clients(share_name, [new_trusted_nfs_clients]) self._filer.api.put.assert_called_once_with('/config/fileservices/share/' + share_name + '/trustedNFSClients', mock.ANY) def get_address(elem): @@ -299,7 +299,7 @@ def test_remove_trusted_nfs_clients(self): trusted_nfs_client_to_remove = self._get_get_trusted_nfs_client_object(address="192.168.1.0") self._init_filer(get_response=[trusted_nfs_client_to_keep.to_server_object(), trusted_nfs_client_to_remove.to_server_object()]) - shares.Shares(self._filer).remove_trusted_nfs_clients( + shares.SharesV1(self._filer).remove_trusted_nfs_clients( share_name, [ RemoveNFSv3AccessControlEntry(trusted_nfs_client_to_remove.address, trusted_nfs_client_to_remove.netmask) @@ -318,7 +318,7 @@ def _get_get_trusted_nfs_client_object(address=None): def _test_get_access_type(self, expected_access): share_name = 'share' self._init_filer(get_response=expected_access) - actual_access = shares.Shares(self._filer).get_access_type(share_name) + actual_access = shares.SharesV1(self._filer).get_access_type(share_name) self._filer.api.get.assert_called_once_with('/config/fileservices/share/' + share_name + '/access') self.assertEqual(actual_access, expected_access) @@ -329,7 +329,7 @@ def test_get_access_type(self): def _test_set_access_type(self, access): share_name = 'share' self._init_filer() - shares.Shares(self._filer).set_access_type(share_name, access) + shares.SharesV1(self._filer).set_access_type(share_name, access) self._filer.api.put.assert_called_once_with('/config/fileservices/share/' + share_name + '/access', access) def test_set_access_type(self): @@ -340,7 +340,7 @@ def test_get_screened_file_types(self): share_name = 'share' get_response = ['exe', 'sh'] self._init_filer(get_response=get_response) - screened_file_types = shares.Shares(self._filer).get_screened_file_types(share_name) + screened_file_types = shares.SharesV1(self._filer).get_screened_file_types(share_name) self._filer.api.get.assert_called_once_with('/config/fileservices/share/' + share_name + '/screenedFileTypes') self.assertListEqual(get_response, screened_file_types) @@ -350,7 +350,7 @@ def test_set_screened_file_types(self): current_share = self._test_screened_file_types_get_current_share(share_name, Acl.WindowsNT, [current_screened_file_type]) self._init_filer(get_response=current_share) new_screened_file_type = ['new'] - shares.Shares(self._filer).set_screened_file_types(share_name, new_screened_file_type) + shares.SharesV1(self._filer).set_screened_file_types(share_name, new_screened_file_type) self._filer.api.get.assert_called_once_with('/config/fileservices/share/' + share_name) self._filer.api.put.assert_called_once_with('/config/fileservices/share/' + share_name + '/screenedFileTypes', mock.ANY) self.assertListEqual(new_screened_file_type, self._filer.api.put.call_args[0][1]) @@ -364,7 +364,7 @@ def test_set_screened_file_types_invalid_access(self): ) self._init_filer(get_response=current_share) with self.assertRaises(exceptions.CTERAException): - shares.Shares(self._filer).set_screened_file_types(share_name, ['new']) + shares.SharesV1(self._filer).set_screened_file_types(share_name, ['new']) def test_add_screened_file_types(self): share_name = 'share' @@ -372,7 +372,7 @@ def test_add_screened_file_types(self): current_share = self._test_screened_file_types_get_current_share(share_name, Acl.WindowsNT, current_screened_file_type) self._init_filer(get_response=current_share) new_screened_file_type = ['new'] - shares.Shares(self._filer).add_screened_file_types(share_name, new_screened_file_type) + shares.SharesV1(self._filer).add_screened_file_types(share_name, new_screened_file_type) self._filer.api.get.assert_called_once_with('/config/fileservices/share/' + share_name) self._filer.api.put.assert_called_once_with('/config/fileservices/share/' + share_name + '/screenedFileTypes', mock.ANY) self.assertListEqual(sorted(current_screened_file_type + new_screened_file_type), sorted(self._filer.api.put.call_args[0][1])) @@ -386,7 +386,7 @@ def test_add_screened_file_types_invalid_access(self): ) self._init_filer(get_response=current_share) with self.assertRaises(exceptions.CTERAException): - shares.Shares(self._filer).add_screened_file_types(share_name, ['new']) + shares.SharesV1(self._filer).add_screened_file_types(share_name, ['new']) def test_remove_screened_file_types(self): share_name = 'share' @@ -394,7 +394,7 @@ def test_remove_screened_file_types(self): current_share = self._test_screened_file_types_get_current_share(share_name, Acl.WindowsNT, current_screened_file_types) self._init_filer(get_response=current_share) removed_screened_file_type = 'new' - shares.Shares(self._filer).remove_screened_file_types(share_name, [removed_screened_file_type]) + shares.SharesV1(self._filer).remove_screened_file_types(share_name, [removed_screened_file_type]) self._filer.api.get.assert_called_once_with('/config/fileservices/share/' + share_name) self._filer.api.put.assert_called_once_with('/config/fileservices/share/' + share_name + '/screenedFileTypes', mock.ANY) self.assertListEqual(['old'], sorted(self._filer.api.put.call_args[0][1])) @@ -408,7 +408,7 @@ def test_remove_screened_file_types_invalid_access(self): ) self._init_filer(get_response=current_share) with self.assertRaises(exceptions.CTERAException): - shares.Shares(self._filer).remove_screened_file_types(share_name, ['new']) + shares.SharesV1(self._filer).remove_screened_file_types(share_name, ['new']) @staticmethod def _test_screened_file_types_get_current_share(name, access, current_list):