Skip to content

Commit c2ff6c8

Browse files
committed
support synchronous and async writes to local file-system, support downloads via asyncio-WebDAV, update docs, update modules that require filesystem apis
1 parent f22f18a commit c2ff6c8

13 files changed

Lines changed: 438 additions & 123 deletions

File tree

cterasdk/asynchronous/core/files/browser.py

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from ....cio.core import CorePath
2-
from ....lib import FileSystem
2+
from ....lib.storage import asynfs, commonfs
33
from ..base_command import BaseCommand
44
from . import io
55

@@ -9,7 +9,6 @@ class FileBrowser(BaseCommand):
99
def __init__(self, core):
1010
super().__init__(core)
1111
self._scope = f'/{self._core.context}/webdav'
12-
self._filesystem = FileSystem.instance()
1312

1413
async def handle(self, path):
1514
"""
@@ -30,6 +29,38 @@ async def handle_many(self, directory, *objects):
3029
handle_many_function = await io.handle_many(self.normalize(directory), *objects)
3130
return await handle_many_function(self._core)
3231

32+
async def download(self, path, destination=None):
33+
"""
34+
Download a file
35+
36+
:param str path: Path
37+
:param str,optional destination:
38+
File destination, if it is a directory, the original filename will be kept, defaults to the default directory
39+
"""
40+
directory, name = commonfs.determine_directory_and_filename(path, destination=destination)
41+
handle = await self.handle(path)
42+
return await asynfs.write(directory, name, handle)
43+
44+
async def download_many(self, target, objects, destination=None):
45+
"""
46+
Download selected files and/or directories as a ZIP archive.
47+
48+
.. warning::
49+
The provided list of objects is not validated. Only existing files and directories
50+
will be included in the resulting ZIP file.
51+
52+
:param str target:
53+
Path to the cloud folder containing the files and directories to download.
54+
:param list[str] objects:
55+
List of file and/or directory names to include in the download.
56+
:param str destination:
57+
Optional. Path to the destination file or directory. If a directory is provided,
58+
the original filename will be preserved. Defaults to the default download directory.
59+
"""
60+
directory, name = commonfs.determine_directory_and_filename(target, objects, destination=destination, archive=True)
61+
handle = await self.handle_many(target, *objects)
62+
return await asynfs.write(directory, name, handle)
63+
3364
async def listdir(self, path, depth=None, include_deleted=False):
3465
"""
3566
List Directory
@@ -116,7 +147,7 @@ async def upload_file(self, path, destination):
116147
:param str destination: Remote path
117148
"""
118149
with open(path, 'rb') as handle:
119-
metadata = self._filesystem.properties(path)
150+
metadata = commonfs.properties(path)
120151
response = await self.upload(metadata['name'], metadata['size'], destination, handle)
121152
return response
122153

cterasdk/core/files/browser.py

Lines changed: 18 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import cterasdk.settings
44
from ...cio.core import CorePath
55
from ...exceptions import CTERAException
6-
from ...lib import FileSystem
6+
from ...lib.storage import synfs, commonfs
77
from ..base_command import BaseCommand
88
from . import io
99

@@ -13,7 +13,6 @@ class FileBrowser(BaseCommand):
1313
def __init__(self, core):
1414
super().__init__(core)
1515
self._scope = f'/{self._core.context}/webdav'
16-
self._filesystem = FileSystem.instance()
1716

1817
def handle(self, path):
1918
"""
@@ -42,24 +41,29 @@ def download(self, path, destination=None):
4241
:param str,optional destination:
4342
File destination, if it is a directory, the original filename will be kept, defaults to the default directory
4443
"""
45-
directory, name = self.determine_directory_and_filename(path, destination=destination)
44+
directory, name = commonfs.determine_directory_and_filename(path, destination=destination)
4645
handle = self.handle(path)
47-
return self._filesystem.save(directory, name, handle)
46+
return synfs.write(directory, name, handle)
4847

49-
def download_as_zip(self, target, objects, destination=None):
48+
def download_many(self, target, objects, destination=None):
5049
"""
51-
Download a list of files and/or directories from a cloud folder as a ZIP file
50+
Download selected files and/or directories as a ZIP archive.
5251
53-
.. warning:: The list of files is not validated. The ZIP file will include only the existing files and directories
52+
.. warning::
53+
The provided list of objects is not validated. Only existing files and directories
54+
will be included in the resulting ZIP file.
5455
55-
:param str target: Path to the cloud directory
56-
:param list[str] objects: List of files and/or directories in the cloud folder to download
57-
:param str,optional destination:
58-
File destination, if it is a directory, the original filename will be kept, defaults to the default directory
56+
:param str target:
57+
Path to the cloud folder containing the files and directories to download.
58+
:param list[str] objects:
59+
List of file and/or directory names to include in the download.
60+
:param str destination:
61+
Optional. Path to the destination file or directory. If a directory is provided,
62+
the original filename will be preserved. Defaults to the default download directory.
5963
"""
60-
directory, name = self.determine_directory_and_filename(target, objects, destination=destination, archive=True)
64+
directory, name = commonfs.determine_directory_and_filename(target, objects, destination=destination, archive=True)
6165
handle = self.handle_many(target, *objects)
62-
return self._filesystem.save(directory, name, handle)
66+
return synfs.write(directory, name, handle)
6367

6468
def listdir(self, path, depth=None, include_deleted=False):
6569
"""
@@ -120,31 +124,6 @@ def permalink(self, path):
120124
return contents[0].permalink
121125
raise FileNotFoundError('File not found.', path)
122126

123-
def determine_directory_and_filename(self, p, objects=None, destination=None, archive=False):
124-
"""
125-
Determine location to save file.
126-
127-
:param str p: Path.
128-
:param list[str],optional objects: List of files or folders
129-
:param str,optional destination: Destination
130-
:param bool,optional archive: Compressed archive
131-
:returns: Directory and file name
132-
:rtype: tuple[str]
133-
"""
134-
directory, name = None, None
135-
if destination:
136-
directory, name = self._filesystem.split_file_directory(destination)
137-
else:
138-
directory = self._filesystem.downloads_directory()
139-
140-
if not name:
141-
normalized = self.normalize(p)
142-
if archive:
143-
name = self._filesystem.compute_zip_file_name(normalized.absolute, objects)
144-
else:
145-
name = normalized.name
146-
return directory, name
147-
148127
def normalize(self, entries):
149128
return CorePath.instance(self._scope, entries)
150129

@@ -171,7 +150,7 @@ def upload_file(self, path, destination):
171150
:param str destination: Remote path
172151
"""
173152
with open(path, 'rb') as handle:
174-
metadata = self._filesystem.properties(path)
153+
metadata = commonfs.properties(path)
175154
response = self.upload(metadata['name'], metadata['size'], destination, handle)
176155
return response
177156

cterasdk/core/ssl.py

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
from zipfile import ZipFile
33

44
from .base_command import BaseCommand
5-
from ..lib import FileSystem, X509Certificate, PrivateKey, TempfileServices, create_certificate_chain
5+
from ..lib import X509Certificate, PrivateKey, TempfileServices, create_certificate_chain
6+
from ..lib.storage import commonfs, synfs
67

78

89
class SSL(BaseCommand):
@@ -12,7 +13,6 @@ class SSL(BaseCommand):
1213

1314
def __init__(self, portal):
1415
super().__init__(portal)
15-
self._filesystem = FileSystem.instance()
1616

1717
def get(self):
1818
"""
@@ -39,10 +39,10 @@ def export(self, destination=None):
3939
:param str,optional destination:
4040
File destination, defaults to the default directory
4141
"""
42-
directory, filename = self._filesystem.generate_file_location(destination, 'certificate.zip')
42+
directory, filename = commonfs.generate_file_destination(destination, 'certificate.zip')
4343
logging.getLogger('cterasdk.core').info('Exporting SSL certificate.')
4444
handle = self._core.ctera.handle('/preview/exportCertificate')
45-
filepath = self._filesystem.save(directory, filename, handle)
45+
filepath = synfs.write(directory, filename, handle)
4646
logging.getLogger('cterasdk.core').info('Exported SSL certificate. %s', {'filepath': filepath})
4747
return filepath
4848

@@ -57,22 +57,22 @@ def create_zip_archive(self, private_key, *certificates):
5757

5858
key_basename = 'private.key'
5959
key_object = PrivateKey.load_private_key(private_key)
60-
key_filepath = FileSystem.join(tempdir, key_basename)
61-
self._filesystem.write(key_filepath, key_object.pem_data)
60+
key_filepath = commonfs.join(tempdir, key_basename)
61+
synfs.overwrite(key_filepath, key_object.pem_data)
6262

6363
cert_basename = 'certificate'
6464
certificates = [X509Certificate.load_certificate(certificate) for certificate in certificates]
6565
certificate_chain = create_certificate_chain(*certificates)
6666

6767
certificate_chain_zip_archive = None
6868
if certificate_chain:
69-
certificate_chain_zip_archive = FileSystem.join(tempdir, f'{cert_basename}.zip')
69+
certificate_chain_zip_archive = commonfs.join(tempdir, f'{cert_basename}.zip')
7070
with ZipFile(certificate_chain_zip_archive, 'w') as zip_archive:
7171
zip_archive.write(key_filepath, key_basename)
7272
for idx, certificate in enumerate(certificate_chain):
7373
filename = f'{cert_basename}{idx if idx > 0 else ""}.crt'
74-
filepath = FileSystem.join(tempdir, filename)
75-
self._filesystem.write(filepath, certificate.pem_data)
74+
filepath = commonfs.join(tempdir, filename)
75+
synfs.overwrite(filepath, certificate.pem_data)
7676
zip_archive.write(filepath, filename)
7777

7878
return certificate_chain_zip_archive
@@ -96,7 +96,7 @@ def import_from_chain(self, private_key, *certificates):
9696
return self.import_from_zip(zipflie)
9797

9898
def _import_certificate(self, zipfile):
99-
self._filesystem.properties(zipfile)
99+
commonfs.properties(zipfile)
100100
logging.getLogger('cterasdk.core').info('Uploading SSL certificate.')
101101
with open(zipfile, 'rb') as fd:
102102
response = self._core.api.form_data(

cterasdk/edge/config.py

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,19 @@
55
from ..exceptions import CTERAException
66
from ..convert import fromxmlstr, toxmlstr
77
from ..common import Device, delete_attrs
8-
from ..lib import FileSystem, TempfileServices
8+
from ..lib import TempfileServices
9+
from ..lib.storage import commonfs, synfs
910
from .base_command import BaseCommand
1011

1112

13+
logger = logging.getLogger('cterasdk.edge')
14+
15+
1216
class Config(BaseCommand):
1317
""" Edge Filer General Configuration APIs """
1418

1519
def __init__(self, edge):
1620
super().__init__(edge)
17-
self._filesystem = FileSystem.instance()
1821

1922
def get_location(self):
2023
"""
@@ -31,7 +34,7 @@ def set_location(self, location):
3134
:param str location: New location to set
3235
:return str: The new location
3336
"""
34-
logging.getLogger('cterasdk.edge').info('Configuring device location. %s', {'location': location})
37+
logger.info('Configuring device location. %s', {'location': location})
3538
return self._edge.api.put('/config/device/location', location)
3639

3740
def get_hostname(self):
@@ -49,7 +52,7 @@ def set_hostname(self, hostname):
4952
:param str hostname: New hostname to set
5053
:return str: The new hostname
5154
"""
52-
logging.getLogger('cterasdk.edge').info('Configuring device hostname. %s', {'hostname': hostname})
55+
logger.info('Configuring device hostname. %s', {'hostname': hostname})
5356
return self._edge.api.put('/config/device/hostname', hostname)
5457

5558
def import_config(self, config, exclude=None):
@@ -69,14 +72,14 @@ def import_config(self, config, exclude=None):
6972
if exclude:
7073
delete_attrs(database, exclude)
7174

72-
path = self._filesystem.join(TempfileServices.mkdir(), f'{self._edge.session().address}.xml')
73-
self._filesystem.write(path, toxmlstr(database, True).encode('utf-8'))
75+
path = commonfs.join(TempfileServices.mkdir(), f'{self._edge.session().address}.xml')
76+
synfs.overwrite(path, toxmlstr(database, True).encode('utf-8'))
7477

7578
return self._import_configuration(path)
7679

7780
def _import_configuration(self, path):
78-
self._filesystem.properties(path)
79-
logging.getLogger('cterasdk.edge').info('Importing Edge Filer configuration.')
81+
commonfs.properties(path)
82+
logger.info('Importing Edge Filer configuration.')
8083
with open(path, 'rb') as fd:
8184
response = self._edge.api.form_data(
8285
'/config',
@@ -86,7 +89,7 @@ def _import_configuration(self, path):
8689
config=fd
8790
)
8891
)
89-
logging.getLogger('cterasdk.edge').info('Imported Edge Filer configuration.')
92+
logger.info('Imported Edge Filer configuration.')
9093
return response
9194

9295
def load_config(self, config):
@@ -96,18 +99,18 @@ def load_config(self, config):
9699
:param str config: A string or a path to the Edge Filer configuration file
97100
"""
98101
data = None
99-
if self._filesystem.exists(config):
100-
logging.getLogger('cterasdk.edge').info('Reading the Edge Filer configuration from file. %s', {'path': config})
102+
if commonfs.exists(config):
103+
logger.info('Reading the Edge Filer configuration from file. %s', {'path': config})
101104
with open(config, 'r', encoding='utf-8') as f:
102105
data = f.read()
103106
else:
104107
data = config
105108

106109
database = fromxmlstr(data)
107110
if database:
108-
logging.getLogger('cterasdk.edge').info('Completed parsing the Edge Filer configuration. %s', {'firmware': database.firmware})
111+
logger.info('Completed parsing the Edge Filer configuration. %s', {'firmware': database.firmware})
109112
return database
110-
logging.getLogger('cterasdk.edge').error("Failed parsing the Edge Filer's configuration.")
113+
logger.error("Failed parsing the Edge Filer's configuration.")
111114
raise CTERAException("Failed parsing the Edge Filer's configuration")
112115

113116
def export(self, destination=None):
@@ -118,11 +121,11 @@ def export(self, destination=None):
118121
File destination, defaults to the default directory
119122
"""
120123
default_filename = self._edge.host() + datetime.now().strftime('_%Y-%m-%dT%H_%M_%S') + '.xml'
121-
directory, filename = self._filesystem.generate_file_location(destination, default_filename)
122-
logging.getLogger('cterasdk.edge').info('Exporting configuration. %s', {'host': self._edge.host()})
124+
directory, filename = commonfs.generate_file_destination(destination, default_filename)
125+
logger.info('Exporting configuration. %s', {'host': self._edge.host()})
123126
handle = self._edge.api.handle('/export')
124-
filepath = FileSystem.instance().save(directory, filename, handle)
125-
logging.getLogger('cterasdk.edge').info('Exported configuration. %s', {'filepath': filepath})
127+
filepath = synfs.write(directory, filename, handle)
128+
logger.info('Exported configuration. %s', {'filepath': filepath})
126129

127130
def is_wizard_enabled(self):
128131
"""
@@ -145,5 +148,5 @@ def disable_wizard(self):
145148
return self._set_wizard(False)
146149

147150
def _set_wizard(self, state):
148-
logging.getLogger('cterasdk.edge').info('Disabling first time wizard')
151+
logger.info('Disabling first time wizard')
149152
return self._edge.api.put('/config/gui/openFirstTimeWizard', state)

0 commit comments

Comments
 (0)