Skip to content

Commit b6d9820

Browse files
authored
Saimon/refactor filesystem api (#293)
1 parent f22f18a commit b6d9820

File tree

15 files changed

+655
-195
lines changed

15 files changed

+655
-195
lines changed

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: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,15 @@
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):
910
"""
1011
Portal SSL Certificate APIs
1112
"""
1213

13-
def __init__(self, portal):
14-
super().__init__(portal)
15-
self._filesystem = FileSystem.instance()
16-
1714
def get(self):
1815
"""
1916
Retrieve details of the current installed SSL certificate
@@ -39,14 +36,15 @@ def export(self, destination=None):
3936
:param str,optional destination:
4037
File destination, defaults to the default directory
4138
"""
42-
directory, filename = self._filesystem.generate_file_location(destination, 'certificate.zip')
39+
directory, filename = commonfs.generate_file_destination(destination, 'certificate.zip')
4340
logging.getLogger('cterasdk.core').info('Exporting SSL certificate.')
4441
handle = self._core.ctera.handle('/preview/exportCertificate')
45-
filepath = self._filesystem.save(directory, filename, handle)
42+
filepath = synfs.write(directory, filename, handle)
4643
logging.getLogger('cterasdk.core').info('Exported SSL certificate. %s', {'filepath': filepath})
4744
return filepath
4845

49-
def create_zip_archive(self, private_key, *certificates):
46+
@staticmethod
47+
def create_zip_archive(private_key, *certificates):
5048
"""
5149
Create a ZIP archive that can be imported to CTERA Portal
5250
@@ -57,22 +55,22 @@ def create_zip_archive(self, private_key, *certificates):
5755

5856
key_basename = 'private.key'
5957
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)
58+
key_filepath = commonfs.join(tempdir, key_basename)
59+
synfs.overwrite(key_filepath, key_object.pem_data)
6260

6361
cert_basename = 'certificate'
6462
certificates = [X509Certificate.load_certificate(certificate) for certificate in certificates]
6563
certificate_chain = create_certificate_chain(*certificates)
6664

6765
certificate_chain_zip_archive = None
6866
if certificate_chain:
69-
certificate_chain_zip_archive = FileSystem.join(tempdir, f'{cert_basename}.zip')
67+
certificate_chain_zip_archive = commonfs.join(tempdir, f'{cert_basename}.zip')
7068
with ZipFile(certificate_chain_zip_archive, 'w') as zip_archive:
7169
zip_archive.write(key_filepath, key_basename)
7270
for idx, certificate in enumerate(certificate_chain):
7371
filename = f'{cert_basename}{idx if idx > 0 else ""}.crt'
74-
filepath = FileSystem.join(tempdir, filename)
75-
self._filesystem.write(filepath, certificate.pem_data)
72+
filepath = commonfs.join(tempdir, filename)
73+
synfs.overwrite(filepath, certificate.pem_data)
7674
zip_archive.write(filepath, filename)
7775

7876
return certificate_chain_zip_archive
@@ -92,11 +90,11 @@ def import_from_chain(self, private_key, *certificates):
9290
:param str private_key: The PEM-encoded private key, or a path to the PEM-encoded private key file
9391
:param list[str] certificates: The PEM-encoded certificates, or a list of paths of the PEM-encoded certificate files
9492
"""
95-
zipflie = self.create_zip_archive(private_key, *certificates)
93+
zipflie = SSL.create_zip_archive(private_key, *certificates)
9694
return self.import_from_zip(zipflie)
9795

9896
def _import_certificate(self, zipfile):
99-
self._filesystem.properties(zipfile)
97+
commonfs.properties(zipfile)
10098
logging.getLogger('cterasdk.core').info('Uploading SSL certificate.')
10199
with open(zipfile, 'rb') as fd:
102100
response = self._core.api.form_data(

cterasdk/edge/config.py

Lines changed: 25 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,17 @@
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

15-
def __init__(self, edge):
16-
super().__init__(edge)
17-
self._filesystem = FileSystem.instance()
18-
1919
def get_location(self):
2020
"""
2121
Get the location of the Edge Filer
@@ -31,7 +31,7 @@ def set_location(self, location):
3131
:param str location: New location to set
3232
:return str: The new location
3333
"""
34-
logging.getLogger('cterasdk.edge').info('Configuring device location. %s', {'location': location})
34+
logger.info('Configuring device location. %s', {'location': location})
3535
return self._edge.api.put('/config/device/location', location)
3636

3737
def get_hostname(self):
@@ -49,7 +49,7 @@ def set_hostname(self, hostname):
4949
:param str hostname: New hostname to set
5050
:return str: The new hostname
5151
"""
52-
logging.getLogger('cterasdk.edge').info('Configuring device hostname. %s', {'hostname': hostname})
52+
logger.info('Configuring device hostname. %s', {'hostname': hostname})
5353
return self._edge.api.put('/config/device/hostname', hostname)
5454

5555
def import_config(self, config, exclude=None):
@@ -64,19 +64,19 @@ def import_config(self, config, exclude=None):
6464
if isinstance(config, Device):
6565
database = copy.deepcopy(config)
6666
elif isinstance(config, str):
67-
database = self.load_config(config)
67+
database = Config.load_config(config)
6868

6969
if exclude:
7070
delete_attrs(database, exclude)
7171

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

7575
return self._import_configuration(path)
7676

7777
def _import_configuration(self, path):
78-
self._filesystem.properties(path)
79-
logging.getLogger('cterasdk.edge').info('Importing Edge Filer configuration.')
78+
commonfs.properties(path)
79+
logger.info('Importing Edge Filer configuration.')
8080
with open(path, 'rb') as fd:
8181
response = self._edge.api.form_data(
8282
'/config',
@@ -86,28 +86,29 @@ def _import_configuration(self, path):
8686
config=fd
8787
)
8888
)
89-
logging.getLogger('cterasdk.edge').info('Imported Edge Filer configuration.')
89+
logger.info('Imported Edge Filer configuration.')
9090
return response
9191

92-
def load_config(self, config):
92+
@staticmethod
93+
def load_config(config):
9394
"""
9495
Load the Edge Filer configuration
9596
9697
:param str config: A string or a path to the Edge Filer configuration file
9798
"""
9899
data = None
99-
if self._filesystem.exists(config):
100-
logging.getLogger('cterasdk.edge').info('Reading the Edge Filer configuration from file. %s', {'path': config})
100+
if commonfs.exists(config):
101+
logger.info('Reading the Edge Filer configuration from file. %s', {'path': config})
101102
with open(config, 'r', encoding='utf-8') as f:
102103
data = f.read()
103104
else:
104105
data = config
105106

106107
database = fromxmlstr(data)
107108
if database:
108-
logging.getLogger('cterasdk.edge').info('Completed parsing the Edge Filer configuration. %s', {'firmware': database.firmware})
109+
logger.info('Completed parsing the Edge Filer configuration. %s', {'firmware': database.firmware})
109110
return database
110-
logging.getLogger('cterasdk.edge').error("Failed parsing the Edge Filer's configuration.")
111+
logger.error("Failed parsing the Edge Filer's configuration.")
111112
raise CTERAException("Failed parsing the Edge Filer's configuration")
112113

113114
def export(self, destination=None):
@@ -118,11 +119,12 @@ def export(self, destination=None):
118119
File destination, defaults to the default directory
119120
"""
120121
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()})
122+
directory, filename = commonfs.generate_file_destination(destination, default_filename)
123+
logger.info('Exporting configuration. %s', {'host': self._edge.host()})
123124
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})
125+
filepath = synfs.write(directory, filename, handle)
126+
logger.info('Exported configuration. %s', {'filepath': filepath})
127+
return filepath
126128

127129
def is_wizard_enabled(self):
128130
"""
@@ -145,5 +147,5 @@ def disable_wizard(self):
145147
return self._set_wizard(False)
146148

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

0 commit comments

Comments
 (0)