Skip to content

Commit 9167618

Browse files
authored
Add partial support for Portal configuration templates: (#113)
1 parent 48574fe commit 9167618

File tree

16 files changed

+425
-115
lines changed

16 files changed

+425
-115
lines changed

cterasdk/common/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@
33
from .datetime_utils import DateTimeUtils # noqa: E402, F401
44
from .utils import merge, union, parse_base_object_ref, convert_size, df_military_time, DataUnit # noqa: E402, F401
55
from .types import PolicyRule, PolicyRuleConverter, StringCriteriaBuilder, IntegerCriteriaBuilder, DateTimeCriteriaBuilder, \
6-
ListCriteriaBuilder, ThrottlingRuleBuilder, ThrottlingRule # noqa: E402, F401
6+
ListCriteriaBuilder, ThrottlingRuleBuilder, ThrottlingRule, FilterBackupSet, FileFilterBuilder # noqa: E402, F401

cterasdk/common/enum.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,31 @@ class DayOfWeek:
2020
Weekdays = [Monday, Tuesday, Wednesday, Thursday, Friday]
2121
Weekend = [Saturday, Sunday]
2222
All = [Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday]
23+
24+
25+
class FileCriteria:
26+
"""
27+
File Criteria
28+
29+
:ivar str Name: File name
30+
:ivar str Path: File path
31+
:ivar str Type: File type
32+
:ivar str Size: File size
33+
:ivar str Modified: Last modified
34+
"""
35+
Name = 'FileName'
36+
Path = 'PathName'
37+
Type = 'FileType'
38+
Size = 'FileSize'
39+
Modified = 'LastModified'
40+
41+
42+
class BooleanFunction:
43+
"""
44+
Boolean function
45+
46+
:ivar str AND: AND
47+
:ivar str AND: OR
48+
"""
49+
AND = 'AND'
50+
OR = 'OR'

cterasdk/common/types.py

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
from .object import Object
55
from .utils import df_military_time, day_of_week
6+
from .enum import FileCriteria, BooleanFunction
67

78

89
class PolicyRule:
@@ -256,3 +257,77 @@ def build(self):
256257
if errors:
257258
raise ValueError('No value for required field: %s' % errors)
258259
return self.param
260+
261+
262+
class FileFilterBuilder:
263+
264+
Type = 'File'
265+
266+
@staticmethod
267+
def extensions():
268+
return ListCriteriaBuilder(FileFilterBuilder.Type, FileCriteria.Type)
269+
270+
@staticmethod
271+
def names():
272+
return ListCriteriaBuilder(FileFilterBuilder.Type, FileCriteria.Name)
273+
274+
@staticmethod
275+
def name():
276+
return StringCriteriaBuilder(FileFilterBuilder.Type, FileCriteria.Name)
277+
278+
@staticmethod
279+
def paths():
280+
return ListCriteriaBuilder(FileFilterBuilder.Type, FileCriteria.Path)
281+
282+
@staticmethod
283+
def path():
284+
return StringCriteriaBuilder(FileFilterBuilder.Type, FileCriteria.Path)
285+
286+
@staticmethod
287+
def size():
288+
return IntegerCriteriaBuilder(FileFilterBuilder.Type, FileCriteria.Size)
289+
290+
@staticmethod
291+
def last_modified():
292+
return DateTimeCriteriaBuilder(FileFilterBuilder.Type, FileCriteria.Modified)
293+
294+
295+
class DirectoryEntryFactory:
296+
297+
@staticmethod
298+
def root(included):
299+
return DirEntry('root', included=included)
300+
301+
302+
class FileEntry(Object):
303+
304+
def __init__(self, name, display_name=None, included=None):
305+
self.name = name
306+
self.displayName = display_name
307+
self.isIncluded = included
308+
309+
310+
class DirEntry(FileEntry):
311+
312+
def __init__(self, name, display_name=None, included=None, children=None):
313+
super().__init__(name, display_name, included)
314+
self.children = children
315+
316+
317+
class BackupSet(Object):
318+
319+
def __init__(self, name, directory_tree=None, filter_rules=None, defaults_dirs=None,
320+
template_dirs=None, enabled=True, boolean_function=None, comment=None):
321+
self._classname = self.__class__.__name__ # pylint: disable=protected-access
322+
self.name = name
323+
self.isEnabled = enabled
324+
self.directoryTree = directory_tree if directory_tree else DirectoryEntryFactory.root(True)
325+
self.booleanFunction = boolean_function if boolean_function else BooleanFunction.AND
326+
self.templateDirectories = template_dirs
327+
self.defaultDirs = defaults_dirs
328+
self.comment = comment
329+
self.filterRules = filter_rules
330+
331+
332+
class FilterBackupSet(BackupSet):
333+
pass

cterasdk/core/buckets.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ def get(self, name, include=None):
3838

3939
def add(self, name, bucket, read_only=False, dedicated_to=None):
4040
"""
41-
Get a Bucket
41+
Add a Bucket
4242
4343
:param str name: Name of the bucket
4444
:param cterasdk.core.types.Bucket bucket: Storage bucket to add

cterasdk/core/enum.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -398,3 +398,35 @@ class BucketType:
398398
Nutanix = 'Nutanix'
399399
Wasabi = 'WasabiS3'
400400
Google = 'GoogleS3'
401+
402+
403+
class EnvironmentVariables:
404+
"""
405+
Environment Variables.\n
406+
Some environment variables are applicable across platforms (i.e. Windows, Linux), while others are limited to a designated platform
407+
408+
:ivar str ALLUSERSPROFILE: All users profile
409+
:ivar str WINDIR: Windows directory
410+
:ivar str TEMP: Temp directory
411+
:ivar str SYSTEMDRIVE: System drive
412+
:ivar str PROGRAMFILES: Program files
413+
:ivar str APPDATA: Application data
414+
:ivar str USERPROFILE: Current user profile
415+
:ivar str PRIMARYUSER: Primary user
416+
:ivar str USERS: Users directory (CTERA Edge Filer)
417+
:ivar str AGENTS: Agents directory (CTERA Edge Filer)
418+
:ivar str SYNCS: Syncs directory (CTERA Edge Filer)
419+
:ivar str PROJECTS: Projects directory (CTERA Edge Filer)
420+
"""
421+
ALLUSERSPROFILE = '$ALLUSERSPROFILE'
422+
WINDIR = '$WINDIR'
423+
TEMP = '$TEMP'
424+
SYSTEMDRIVE = '$SYSTEMDRIVE'
425+
PROGRAMFILES = '$PROGRAMFILES'
426+
APPDATA = '$APPDATA'
427+
USERPROFILE = '$USERPROFILE'
428+
USERS = '$USERS'
429+
AGENTS = '$AGENTS'
430+
SYNCS = '$SYNCS'
431+
PROJECTS = '$PROJECTS'
432+
PRIMARYUSER = '$PRIMARYUSER'

cterasdk/core/templates.py

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
import logging
2+
3+
from ..common import union, Object
4+
from ..exception import CTERAException
5+
from .base_command import BaseCommand
6+
from . import query
7+
8+
9+
class Templates(BaseCommand):
10+
"""
11+
Portal Configuration Template APIs
12+
"""
13+
14+
default = ['name']
15+
16+
def __init__(self, portal):
17+
super().__init__(portal)
18+
self.auto_assign = TemplateAutoAssignPolicy(self._portal)
19+
20+
def _get_entire_object(self, name):
21+
try:
22+
return self._portal.get('/deviceTemplates/%s' % name)
23+
except CTERAException as error:
24+
raise CTERAException('Failed to get template', error)
25+
26+
def get(self, name, include=None):
27+
"""
28+
Get a Configuration Template
29+
30+
:param str name: Name of the template
31+
:param list[str] include: List of fields to retrieve, defaults to ``['name']``
32+
"""
33+
include = union(include or [], Templates.default)
34+
include = ['/' + attr for attr in include]
35+
template = self._portal.get_multi('/deviceTemplates/' + name, include)
36+
if template.name is None:
37+
raise CTERAException('Could not find template', None, name=name)
38+
return template
39+
40+
def add(self, name, description=None, include_sets=None, exclude_sets=None):
41+
"""
42+
Add a Configuration Template
43+
44+
:param str name: Name of the template
45+
"""
46+
param = Object()
47+
param._classname = 'DeviceTemplate' # pylint: disable=protected-access
48+
param.name = name
49+
param.description = description
50+
51+
param.deviceSettings = Object()
52+
param.deviceSettings._classname = 'DeviceTemplateSettings' # pylint: disable=protected-access
53+
54+
if include_sets or exclude_sets:
55+
param.deviceSettings.backup = Object()
56+
param.deviceSettings.backup._classname = 'BackupTemplate' # pylint: disable=protected-access
57+
param.deviceSettings.backup.backupPolicy = Object()
58+
param.deviceSettings.backup.backupPolicy._classname = 'BackupPolicyTemplate' # pylint: disable=protected-access
59+
if include_sets:
60+
param.deviceSettings.backup.backupPolicy.includeSets = include_sets
61+
if exclude_sets:
62+
param.deviceSettings.backup.backupPolicy.excludeSets = exclude_sets
63+
64+
logging.getLogger().info('Adding template. %s', {'name': name})
65+
response = self._portal.add('/deviceTemplates', param)
66+
logging.getLogger().info('Template added. %s', {'name': name})
67+
return response
68+
69+
def list_templates(self, include=None):
70+
"""
71+
List Configuration Templates.\n
72+
To retrieve templates, you must first browse the tenant, using: `GlobalAdmin.portals.browse()`
73+
74+
:param list[str],optional include: List of fields to retrieve, defaults to ``['name']``
75+
"""
76+
include = union(include or [], Templates.default)
77+
param = query.QueryParamBuilder().include(include).build()
78+
return query.iterator(self._portal, '/deviceTemplates', param)
79+
80+
def delete(self, name):
81+
"""
82+
Delete a Configuration Template
83+
84+
:param str name: Name of the template
85+
"""
86+
logging.getLogger().info('Deleting template. %s', {'name': name})
87+
response = self._portal.delete('/deviceTemplates/%s' % name)
88+
logging.getLogger().info('Template deleted. %s', {'name': name})
89+
return response
90+
91+
def set_default(self, name, wait=False):
92+
"""
93+
Set a Configuration Template as the default template
94+
95+
:param str name: Name of the template
96+
:param bool,optional wait: Wait for all changes to apply, defaults to `False`
97+
"""
98+
logging.getLogger().info('Setting default template. %s', {'name': name})
99+
response = self._portal.execute('/deviceTemplates/%s' % name, 'setAsDefault')
100+
self.auto_assign.apply_changes(wait=wait)
101+
logging.getLogger().info('Set default template. %s', {'name': name})
102+
return response
103+
104+
def remove_default(self, name, wait=False):
105+
"""
106+
Set a Configuration Template not to be the default template
107+
108+
:param str name: Name of the template
109+
:param bool,optional wait: Wait for all changes to apply, defaults to `False`
110+
"""
111+
template = self.get(name, include=['isDefault'])
112+
if template.isDefault:
113+
logging.getLogger().info('Removing default template. %s', {'name': name})
114+
response = self._portal.execute('', 'removeDefaultDeviceTemplate')
115+
logging.getLogger().info('Removed default template. %s', {'name': name})
116+
self.auto_assign.apply_changes(wait=wait)
117+
return response
118+
logging.getLogger().info('Template not set as default. %s', {'name': name})
119+
return None
120+
121+
122+
class TemplateAutoAssignPolicy(BaseCommand):
123+
124+
def apply_changes(self, wait=False):
125+
"""
126+
Apply provisioning changes.\n
127+
128+
:param bool,optional wait: Wait for all changes to apply, defaults to `False`
129+
"""
130+
task = self._portal.execute('', 'applyAutoAssignmentRules')
131+
if wait:
132+
task = self._portal.tasks.wait(task)
133+
return task

cterasdk/edge/enum.py

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -357,34 +357,6 @@ class BackupConfStatusID:
357357
GetFoldersList = "GetFoldersList"
358358

359359

360-
class FileCriteria:
361-
"""
362-
Cloud Sync Exclusion Builder File Criteria
363-
364-
:ivar str Name: File name
365-
:ivar str Path: File path
366-
:ivar str Type: File type
367-
:ivar str Size: File size
368-
:ivar str Modified: Last modified
369-
"""
370-
Name = 'FileName'
371-
Path = 'PathName'
372-
Type = 'FileType'
373-
Size = 'FileSize'
374-
Modified = 'LastModified'
375-
376-
377-
class BooleanFunction:
378-
"""
379-
Boolean function
380-
381-
:ivar str AND: AND
382-
:ivar str AND: OR
383-
"""
384-
AND = 'AND'
385-
OR = 'OR'
386-
387-
388360
class Traffic:
389361
"""
390362
Traffic type

cterasdk/edge/sync.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,8 @@
22

33
from ..lib import track, ErrorStatus
44
from .enum import Mode, SyncStatus, Acl
5-
from .types import FilterBackupSet, FileExclusionBuilder
65
from .base_command import BaseCommand
7-
from ..common import ThrottlingRule
6+
from ..common import ThrottlingRule, FilterBackupSet, FileFilterBuilder
87

98

109
class Sync(BaseCommand):
@@ -116,7 +115,7 @@ def refresh(self):
116115
def exclude_files(self, extensions=None, filenames=None, paths=None, custom_exclusion_rules=None):
117116
"""
118117
Exclude files from Cloud Sync. This method will override any existing file exclusion rules
119-
Use :func:`cterasdk.edge.types.FileExclusionBuilder` to build custom file exclusion rules`
118+
Use :func:`cterasdk.common.types.FileFilterBuilder` to build custom file exclusion rules`
120119
121120
:param list[str] extensions: List of file extensions
122121
:param list[str] filenames: List of file names
@@ -126,15 +125,15 @@ def exclude_files(self, extensions=None, filenames=None, paths=None, custom_excl
126125
rules = list()
127126
if extensions:
128127
param = FilterBackupSet('List of file extensions to exclude from sync',
129-
filter_rules=[FileExclusionBuilder.extensions().include(extensions).build()])
128+
filter_rules=[FileFilterBuilder.extensions().include(extensions).build()])
130129
rules.append(param)
131130
if filenames:
132131
param = FilterBackupSet('List of file names to exclude from sync',
133-
filter_rules=[FileExclusionBuilder.names().include(filenames).build()])
132+
filter_rules=[FileFilterBuilder.names().include(filenames).build()])
134133
rules.append(param)
135134
if paths:
136135
param = FilterBackupSet('List of file paths to exclude from sync',
137-
filter_rules=[FileExclusionBuilder.paths().include(filenames).build()])
136+
filter_rules=[FileFilterBuilder.paths().include(filenames).build()])
138137
rules.append(param)
139138
if custom_exclusion_rules:
140139
rules.extend(custom_exclusion_rules)

0 commit comments

Comments
 (0)