illumio-pylo 0.2.5__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. illumio_pylo/API/APIConnector.py +1308 -0
  2. illumio_pylo/API/AuditLog.py +42 -0
  3. illumio_pylo/API/ClusterHealth.py +136 -0
  4. illumio_pylo/API/CredentialsManager.py +286 -0
  5. illumio_pylo/API/Explorer.py +1077 -0
  6. illumio_pylo/API/JsonPayloadTypes.py +240 -0
  7. illumio_pylo/API/RuleSearchQuery.py +128 -0
  8. illumio_pylo/API/__init__.py +0 -0
  9. illumio_pylo/AgentStore.py +139 -0
  10. illumio_pylo/Exception.py +44 -0
  11. illumio_pylo/Helpers/__init__.py +3 -0
  12. illumio_pylo/Helpers/exports.py +508 -0
  13. illumio_pylo/Helpers/functions.py +166 -0
  14. illumio_pylo/IPList.py +135 -0
  15. illumio_pylo/IPMap.py +285 -0
  16. illumio_pylo/Label.py +25 -0
  17. illumio_pylo/LabelCommon.py +48 -0
  18. illumio_pylo/LabelGroup.py +68 -0
  19. illumio_pylo/LabelStore.py +403 -0
  20. illumio_pylo/LabeledObject.py +25 -0
  21. illumio_pylo/Organization.py +258 -0
  22. illumio_pylo/Query.py +331 -0
  23. illumio_pylo/ReferenceTracker.py +41 -0
  24. illumio_pylo/Rule.py +671 -0
  25. illumio_pylo/Ruleset.py +306 -0
  26. illumio_pylo/RulesetStore.py +101 -0
  27. illumio_pylo/SecurityPrincipal.py +62 -0
  28. illumio_pylo/Service.py +256 -0
  29. illumio_pylo/SoftwareVersion.py +125 -0
  30. illumio_pylo/VirtualService.py +17 -0
  31. illumio_pylo/VirtualServiceStore.py +75 -0
  32. illumio_pylo/Workload.py +506 -0
  33. illumio_pylo/WorkloadStore.py +289 -0
  34. illumio_pylo/__init__.py +82 -0
  35. illumio_pylo/cli/NativeParsers.py +96 -0
  36. illumio_pylo/cli/__init__.py +134 -0
  37. illumio_pylo/cli/__main__.py +10 -0
  38. illumio_pylo/cli/commands/__init__.py +32 -0
  39. illumio_pylo/cli/commands/credential_manager.py +168 -0
  40. illumio_pylo/cli/commands/iplist_import_from_file.py +185 -0
  41. illumio_pylo/cli/commands/misc.py +7 -0
  42. illumio_pylo/cli/commands/ruleset_export.py +129 -0
  43. illumio_pylo/cli/commands/update_pce_objects_cache.py +44 -0
  44. illumio_pylo/cli/commands/ven_duplicate_remover.py +366 -0
  45. illumio_pylo/cli/commands/ven_idle_to_visibility.py +287 -0
  46. illumio_pylo/cli/commands/ven_upgrader.py +226 -0
  47. illumio_pylo/cli/commands/workload_export.py +251 -0
  48. illumio_pylo/cli/commands/workload_import.py +423 -0
  49. illumio_pylo/cli/commands/workload_relabeler.py +510 -0
  50. illumio_pylo/cli/commands/workload_reset_names_to_null.py +83 -0
  51. illumio_pylo/cli/commands/workload_used_in_rule_finder.py +80 -0
  52. illumio_pylo/docs/Doxygen +1757 -0
  53. illumio_pylo/tmp.py +104 -0
  54. illumio_pylo/utilities/__init__.py +0 -0
  55. illumio_pylo/utilities/cli.py +10 -0
  56. illumio_pylo/utilities/credentials.example.json +20 -0
  57. illumio_pylo/utilities/explorer_report_exporter.py +86 -0
  58. illumio_pylo/utilities/health_monitoring.py +102 -0
  59. illumio_pylo/utilities/iplist_analyzer.py +148 -0
  60. illumio_pylo/utilities/iplists_stats_duplicates_unused_finder.py +75 -0
  61. illumio_pylo/utilities/resources/iplists-import-example.csv +3 -0
  62. illumio_pylo/utilities/resources/iplists-import-example.xlsx +0 -0
  63. illumio_pylo/utilities/resources/workload-exporter-filter-example.csv +3 -0
  64. illumio_pylo/utilities/resources/workloads-import-example.csv +2 -0
  65. illumio_pylo/utilities/resources/workloads-import-example.xlsx +0 -0
  66. illumio_pylo/utilities/ven_compatibility_report_export.py +240 -0
  67. illumio_pylo/utilities/ven_idle_to_illumination.py +344 -0
  68. illumio_pylo/utilities/ven_reassign_pce.py +183 -0
  69. illumio_pylo-0.2.5.dist-info/LICENSE +176 -0
  70. illumio_pylo-0.2.5.dist-info/METADATA +197 -0
  71. illumio_pylo-0.2.5.dist-info/RECORD +73 -0
  72. illumio_pylo-0.2.5.dist-info/WHEEL +5 -0
  73. illumio_pylo-0.2.5.dist-info/top_level.txt +1 -0
@@ -0,0 +1,240 @@
1
+ """This module contains the JSON payload types for the PCE API."""
2
+
3
+ from typing import List, Optional, TypedDict, NotRequired, Union, Literal
4
+
5
+
6
+ class HrefReference(TypedDict):
7
+ href: str
8
+
9
+ class HrefReferenceWithName(TypedDict):
10
+ href: str
11
+ name: str
12
+
13
+ class LabelHrefRef(TypedDict):
14
+ label: HrefReference
15
+
16
+ class WorkloadHrefRef(TypedDict):
17
+ workload: HrefReference
18
+
19
+ class IPListHrefRef(TypedDict):
20
+ ip_list: HrefReference
21
+
22
+ class ServiceHrefRef(TypedDict):
23
+ service: HrefReference
24
+
25
+ class VirtualServiceHrefRef(TypedDict):
26
+ virtual_service: HrefReference
27
+
28
+ class LabelObjectJsonStructure(TypedDict):
29
+ created_at: str
30
+ created_by: Optional[HrefReferenceWithName]
31
+ deleted: bool
32
+ href: str
33
+ key: str
34
+ updated_at: str
35
+ updated_by: Optional[HrefReferenceWithName]
36
+ value: str
37
+
38
+
39
+ class LabelObjectCreationJsonStructure(TypedDict):
40
+ value: str
41
+ key: str
42
+
43
+
44
+ class LabelObjectUpdateJsonStructure(TypedDict):
45
+ value: str
46
+
47
+
48
+ class LabelGroupObjectJsonStructure(TypedDict):
49
+ created_at: str
50
+ created_by: Optional[HrefReferenceWithName]
51
+ deleted: bool
52
+ href: str
53
+ key: str
54
+ labels: List[HrefReference]
55
+ name: str
56
+ updated_at: str
57
+ updated_by: Optional[HrefReferenceWithName]
58
+
59
+
60
+ class LabelGroupObjectUpdateJsonStructure(TypedDict):
61
+ labels: NotRequired[List[HrefReference]]
62
+ name: NotRequired[str]
63
+
64
+ class IPListObjectJsonStructure(TypedDict):
65
+ created_at: str
66
+ created_by: Optional[HrefReferenceWithName]
67
+ description: str
68
+ href: str
69
+ ip_ranges: List[TypedDict('record', {'from_ip': str, 'to_ip': str, 'exclusion': bool})]
70
+ name: str
71
+ updated_at: str
72
+ updated_by: Optional[HrefReferenceWithName]
73
+
74
+ class IPListObjectCreationJsonStructure(TypedDict):
75
+ description: str
76
+ ip_ranges: List[TypedDict('record', {'from_ip': str, 'to_ip': str, 'exclusion': bool})]
77
+ name: str
78
+
79
+
80
+ class WorkloadInterfaceObjectJsonStructure(TypedDict):
81
+ name: str
82
+ address: str
83
+
84
+ class WorkloadObjectJsonStructure(TypedDict):
85
+ created_at: str
86
+ created_by: Optional[HrefReferenceWithName]
87
+ description: Optional[str]
88
+ hostname: Optional[str]
89
+ href: str
90
+ interfaces: List[WorkloadInterfaceObjectJsonStructure]
91
+ labels: List[HrefReference]
92
+ name: Optional[str]
93
+ public_ip: Optional[str]
94
+ managed: bool
95
+ updated_at: str
96
+ updated_by: Optional[HrefReferenceWithName]
97
+
98
+ class WorkloadObjectCreateJsonStructure(TypedDict):
99
+ description: Optional[str]
100
+ hostname: Optional[str]
101
+ interfaces: NotRequired[List[WorkloadInterfaceObjectJsonStructure]]
102
+ labels: NotRequired[List[HrefReference]]
103
+ name: Optional[str]
104
+ public_ip: NotRequired[Optional[str]]
105
+
106
+ class RuleServiceReferenceObjectJsonStructure(TypedDict):
107
+ href: str
108
+ name: str
109
+
110
+
111
+ class RuleDirectServiceReferenceObjectJsonStructure(TypedDict):
112
+ port: int
113
+ proto: int
114
+ to_port: NotRequired[int]
115
+
116
+
117
+ class RuleObjectJsonStructure(TypedDict):
118
+ created_at: str
119
+ created_by: Optional[HrefReferenceWithName]
120
+ href: str
121
+ ingress_services: List[RuleDirectServiceReferenceObjectJsonStructure|RuleServiceReferenceObjectJsonStructure]
122
+ updated_at: str
123
+ updated_by: Optional[HrefReferenceWithName]
124
+
125
+
126
+ class RulesetScopeEntryLineJsonStructure(TypedDict):
127
+ label: NotRequired[HrefReference]
128
+ label_group: NotRequired[HrefReference]
129
+
130
+
131
+ class RulesetObjectJsonStructure(TypedDict):
132
+ created_at: str
133
+ created_by: Optional[HrefReferenceWithName]
134
+ description: str
135
+ href: str
136
+ name: str
137
+ rules: List[RuleObjectJsonStructure]
138
+ scopes: List[List[RulesetScopeEntryLineJsonStructure]]
139
+ updated_at: str
140
+ updated_by: Optional[HrefReferenceWithName]
141
+
142
+
143
+ class RulesetObjectUpdateStructure(TypedDict):
144
+ name: NotRequired[str]
145
+ description: NotRequired[str]
146
+ scopes: NotRequired[List[List[RulesetScopeEntryLineJsonStructure]]]
147
+
148
+
149
+ class ServiceObjectJsonStructure(TypedDict):
150
+ created_at: str
151
+ href: str
152
+ name: str
153
+ updated_at: str
154
+ updated_by: Optional[HrefReferenceWithName]
155
+
156
+
157
+ class VirtualServiceObjectJsonStructure(TypedDict):
158
+ created_at: str
159
+ created_by: Optional[HrefReferenceWithName]
160
+ href: str
161
+ name: str
162
+ updated_at: str
163
+ updated_by: Optional[HrefReferenceWithName]
164
+
165
+ class NetworkDeviceConfigObjectJsonStructure(TypedDict):
166
+ device_type: Literal['switch']
167
+ name: str
168
+
169
+ class NetworkDeviceObjectJsonStructure(TypedDict):
170
+ href: str
171
+ config: NetworkDeviceConfigObjectJsonStructure
172
+ supported_endpoint_type: Literal['switch_port']
173
+
174
+ class NetworkDeviceEndpointConfigObjectJsonStructure(TypedDict):
175
+ type: Literal['switch_port']
176
+ name: str
177
+ workload_discovery: bool
178
+
179
+ class NetworkDeviceEndpointObjectJsonStructure(TypedDict):
180
+ href: str
181
+ config: NetworkDeviceEndpointConfigObjectJsonStructure
182
+ status: Literal['unmonitored', 'monitored']
183
+ workloads: List[HrefReference]
184
+
185
+ class SecurityPrincipalObjectJsonStructure(TypedDict):
186
+ created_at: str
187
+ created_by: Optional[HrefReferenceWithName]
188
+ href: str
189
+ name: str
190
+ updated_at: str
191
+ updated_by: Optional[HrefReferenceWithName]
192
+
193
+ class LabelDimensionObjectStructure(TypedDict):
194
+ created_at: str
195
+ created_by: Optional[HrefReferenceWithName]
196
+ display_name: str
197
+ href: str
198
+ key: str
199
+ updated_at: str
200
+ updated_by: Optional[HrefReferenceWithName]
201
+
202
+
203
+ class PCEObjectsJsonStructure(TypedDict):
204
+ iplists: List[IPListObjectJsonStructure]
205
+ labelgroups: List[LabelGroupObjectJsonStructure]
206
+ labels: List[LabelObjectJsonStructure]
207
+ rulesets: List[RulesetObjectJsonStructure]
208
+ security_principals: List[SecurityPrincipalObjectJsonStructure]
209
+ services: List[ServiceObjectJsonStructure]
210
+ virtual_services: List[VirtualServiceObjectJsonStructure]
211
+ workloads: List[WorkloadObjectJsonStructure]
212
+ label_dimensions: List[LabelDimensionObjectStructure]
213
+
214
+
215
+ class PCECacheFileJsonStructure(TypedDict):
216
+ data: PCEObjectsJsonStructure
217
+ pce_version: str
218
+ generation_date: str
219
+
220
+
221
+ class RuleCoverageQueryEntryJsonStructure(TypedDict):
222
+ source: Union[IPListHrefRef, WorkloadHrefRef]
223
+ destination: Union[IPListHrefRef, WorkloadHrefRef]
224
+ services: List
225
+
226
+
227
+ WorkloadsGetQueryLabelFilterJsonStructure = List[List[str]]
228
+
229
+ AuditLogApiEventType = Literal['agent.clone_detected', 'workloads.update', 'workload.update', 'workload_interfaces.update']
230
+
231
+ class AuditLogEntryJsonStructure(TypedDict):
232
+ event_type: AuditLogApiEventType
233
+ timestamp: str
234
+
235
+ class AuditLogApiRequestPayloadStructure(TypedDict):
236
+ pass
237
+
238
+ class AuditLogApiReplyEventJsonStructure(TypedDict):
239
+ pass
240
+
@@ -0,0 +1,128 @@
1
+ from typing import List, Union, Dict, Optional
2
+ import illumio_pylo as pylo
3
+
4
+
5
+ class RuleSearchQueryResolvedResultSet:
6
+ rules: Dict[str, 'pylo.Rule']
7
+ rules_per_ruleset: Dict['pylo.Ruleset', Dict[str, 'pylo.Rule']]
8
+
9
+ def count_results(self):
10
+ return len(self.rules)
11
+
12
+ def __init__(self, raw_json_data, organization: 'pylo.Organization'):
13
+ self._raw_json = raw_json_data
14
+ self.rules = {}
15
+ self.rules_per_ruleset = {}
16
+
17
+ for rule_data in raw_json_data:
18
+ rule_href = rule_data.get('href')
19
+ if rule_href is None:
20
+ raise pylo.PyloEx('Cannot find rule HREF in RuleSearchQuery response', rule_data)
21
+ rule_found = organization.RulesetStore.find_rule_by_href(rule_href)
22
+ if rule_found is None:
23
+ raise pylo.PyloEx("Cannot find rule with HREF '{}' in Organization".format(rule_href), rule_data)
24
+
25
+ self.rules[rule_found.href] = rule_found
26
+ ruleset_found = self.rules_per_ruleset.get(rule_found.owner)
27
+ if ruleset_found is None:
28
+ # print("new ruleset")
29
+ self.rules_per_ruleset[rule_found.owner] = {rule_found.href: rule_found}
30
+ else:
31
+ # print("existing rs")
32
+ self.rules_per_ruleset[rule_found.owner][rule_found.href] = rule_found
33
+
34
+
35
+ class RuleSearchQuery:
36
+ _advanced_mode_consumer_labels: Dict[str, Union['pylo.Label', 'pylo.LabelGroup']] = {}
37
+ _advanced_mode_provider_labels: Dict[str, Union['pylo.Label', 'pylo.LabelGroup']] = {}
38
+ _basic_mode_labels: Dict[str, 'pylo.Label']
39
+ connector: 'pylo.APIConnector'
40
+ max_results: int = 10000
41
+
42
+ def __init__(self, connector: 'pylo.APIConnector'):
43
+ self.connector = connector
44
+ self.mode_is_basic = True
45
+ self._basic_mode_labels = {}
46
+ self._advanced_mode_provider_labels = {}
47
+ self._advanced_mode_consumer_labels = {}
48
+ self._exact_matches = True
49
+ self._mode_is_draft = True
50
+
51
+ def set_basic_mode(self):
52
+ self.mode_is_basic = True
53
+ self._advanced_mode_provider_labels = {}
54
+ self._advanced_mode_consumer_labels = {}
55
+
56
+ def set_advanced_mode(self):
57
+ self.mode_is_basic = False
58
+ self._basic_mode_labels = {}
59
+
60
+ def set_draft_mode(self):
61
+ self._mode_is_draft = True
62
+
63
+ def set_active_mode(self):
64
+ self._mode_is_draft = False
65
+
66
+ def set_max_results(self, max_results: int):
67
+ if max_results < 1:
68
+ raise pylo.PyloEx("max_results must be greater than 0")
69
+ self.max_results = max_results
70
+
71
+ def add_label(self, label: 'pylo.Label'):
72
+ if not self.mode_is_basic:
73
+ raise pylo.PyloEx('You can add labels to RuleSearchQuery only in Basic mode. Use consumer/provider counterparts with Advanced mode')
74
+ self._basic_mode_labels[label.href] = label
75
+
76
+ def add_consumer_label(self, label: 'pylo.Label'):
77
+ if self.mode_is_basic:
78
+ raise pylo.PyloEx('You can add labels to RuleSearchQuery consumers only in Advanced mode')
79
+ self._advanced_mode_consumer_labels[label.href] = label
80
+
81
+ def add_provider_label(self, label: 'pylo.Label'):
82
+ if self.mode_is_basic:
83
+ raise pylo.PyloEx('You can add labels to RuleSearchQuery providers only in Advanced mode')
84
+ self._advanced_mode_provider_labels[label.href] = label
85
+
86
+ def use_exact_matches(self):
87
+ self._exact_matches = True
88
+
89
+ def use_resolved_matches(self):
90
+ self._exact_matches = False
91
+
92
+ def execute(self):
93
+ data = {'max_results': self.max_results}
94
+ if not self._exact_matches:
95
+ data['resolve_actors'] = True
96
+
97
+ uri = '/sec_policy/draft/rule_search'
98
+ if not self._mode_is_draft:
99
+ uri = '/sec_policy/active/rule_search'
100
+
101
+ if self.mode_is_basic:
102
+ if len(self._basic_mode_labels) > 0:
103
+ data['providers_or_consumers'] = []
104
+ for label_href, label in self._basic_mode_labels.items():
105
+ if label.is_label():
106
+ data['providers_or_consumers'].append({'label': {'href': label_href}})
107
+ else:
108
+ data['providers_or_consumers'].append({'label_group': {'href': label_href}})
109
+ else:
110
+ if len(self._advanced_mode_provider_labels) > 0:
111
+ data['providers'] = []
112
+ for label_href, label in self._advanced_mode_provider_labels.items():
113
+ if label.is_label():
114
+ data['providers'].append({'label': {'href': label_href}})
115
+ else:
116
+ data['providers'].append({'label_group': {'href': label_href}})
117
+ if len(self._advanced_mode_consumer_labels) > 0:
118
+ data['consumers'] = []
119
+ for label_href, label in self._advanced_mode_consumer_labels.items():
120
+ if label.is_label():
121
+ data['consumers'].append({'label': {'href': label_href}})
122
+ else:
123
+ data['consumers'].append({'label_group': {'href': label_href}})
124
+
125
+ return self.connector.do_post_call(uri, data)
126
+
127
+ def execute_and_resolve(self, organization: 'pylo.Organization'):
128
+ return RuleSearchQueryResolvedResultSet(self.execute(), organization)
File without changes
@@ -0,0 +1,139 @@
1
+ from datetime import datetime
2
+
3
+ import illumio_pylo as pylo
4
+ from illumio_pylo import log, SoftwareVersion
5
+ from .Helpers import *
6
+ import re
7
+ import datetime
8
+ from typing import *
9
+
10
+ # version_regex = re.compile(r"^(?P<major>[0-9]+)\.(?P<middle>[0-9]+)\.(?P<minor>[0-9]+)-(?P<build>[0-9]+)(u[0-9]+)?$")
11
+
12
+
13
+ class VENAgent(pylo.ReferenceTracker):
14
+
15
+ def __init__(self, href: str, owner: 'pylo.AgentStore', workload: 'pylo.Workload' = None):
16
+ pylo.ReferenceTracker.__init__(self)
17
+ self.href: str = href
18
+ self.owner: 'pylo.AgentStore' = owner
19
+ self.workload: Optional['pylo.Workload'] = workload
20
+
21
+ self.software_version: Optional['pylo.SoftwareVersion'] = None
22
+ self._last_heartbeat: Optional[datetime.datetime] = None
23
+
24
+ self._status_security_policy_sync_state: Optional[str] = None
25
+ self._status_security_policy_applied_at: Optional[str] = None
26
+ self._status_rule_count: Optional[int] = None
27
+
28
+ self.mode = None
29
+
30
+ self.raw_json = None
31
+
32
+ def _get_date_from_json(self, prop_name_in_json: str) -> Optional[datetime.datetime]:
33
+ status_json = self.raw_json.get('status')
34
+ if status_json is None:
35
+ return None
36
+
37
+ prop_value = status_json.get(prop_name_in_json)
38
+ if prop_value is None:
39
+ return None
40
+
41
+ if '.' in prop_value:
42
+ time_found = datetime.datetime.strptime(prop_value, "%Y-%m-%dT%H:%M:%S.%fZ")
43
+ else:
44
+ time_found = datetime.datetime.strptime(prop_value, "%Y-%m-%dT%H:%M:%SZ")
45
+
46
+ return time_found
47
+
48
+ def load_from_json(self, data):
49
+ self.raw_json = data
50
+
51
+ status_json = data.get('status')
52
+ if status_json is None:
53
+ raise pylo.PyloEx("Cannot find VENAgent status in JSON from '{}'".format(self.href))
54
+
55
+ version_string = status_json.get('agent_version')
56
+ if version_string is None:
57
+ raise pylo.PyloEx("Cannot find VENAgent version from '{}'".format(self.href))
58
+ self.software_version = pylo.SoftwareVersion(version_string)
59
+ if self.software_version.is_unknown:
60
+ pylo.log.warn("Agent {} from Workload {}/{} has unknown software version: {}".format(
61
+ self.href,
62
+ self.workload.get_name(),
63
+ self.workload.href,
64
+ self.software_version.version_string))
65
+
66
+ self._status_security_policy_sync_state = status_json.get('security_policy_sync_state')
67
+
68
+ self._status_rule_count = status_json.get('firewall_rule_count')
69
+ # if self._status_rule_count is None:
70
+ # raise pylo.PyloEx("Cannot find firewall_rule_count VENAgent '{}' rule count ".format(self.href), status_json)
71
+
72
+ config_json = data.get('config')
73
+ if config_json is None:
74
+ raise pylo.PyloEx("Cannot find Agent's config in JSON", data)
75
+
76
+ self.mode = config_json.get('mode')
77
+ if self.mode is None:
78
+ raise pylo.PyloEx("Cannot find Agent's mode in config JSON", config_json)
79
+
80
+ if self.mode == 'illuminated':
81
+ log_traffic = config_json.get('log_traffic')
82
+ if log_traffic:
83
+ self.mode = "test"
84
+ else:
85
+ self.mode = "build"
86
+
87
+ @property
88
+ def status(self) -> Literal['stopped','active','suspended','uninstalled']:
89
+ return self.raw_json['status']['status']
90
+
91
+ def get_last_heartbeat_date(self) -> Optional[datetime.datetime]:
92
+ if self._last_heartbeat is None:
93
+ self._last_heartbeat = self._get_date_from_json('last_heartbeat_on')
94
+ return self._last_heartbeat
95
+
96
+ def get_status_security_policy_applied_at(self):
97
+ if self._status_security_policy_applied_at is None:
98
+ self._status_security_policy_applied_at = self._get_date_from_json('security_policy_applied_at')
99
+ return self._status_security_policy_applied_at
100
+
101
+ def get_status_security_policy_sync_state(self):
102
+ return self._status_security_policy_sync_state
103
+
104
+
105
+ class AgentStore:
106
+
107
+ def __init__(self, owner: 'pylo.Organization'):
108
+ self.owner = owner
109
+ self.items_by_href: Dict[str, pylo.VENAgent] = {}
110
+
111
+ @property
112
+ def agents(self) -> List['pylo.VENAgent']:
113
+ """
114
+ Returns a copy of the list of all agents in the store
115
+ :return:
116
+ """
117
+ return list(self.items_by_href.values())
118
+
119
+ def find_by_href(self, href: str) -> VENAgent:
120
+ return self.items_by_href.get(href)
121
+
122
+ def create_ven_agent_from_workload_record(self, workload: 'pylo.Workload', json_data) -> 'pylo.VENAgent':
123
+ # For developer use only. This is called by the WorkloadStore when it creates a new workload as the Agent records are read from there
124
+ href = json_data.get('href')
125
+ if href is None:
126
+ raise pylo.PyloEx("Cannot extract Agent href from workload '{}'".format(workload.href))
127
+
128
+ agent = pylo.VENAgent(href, self, workload)
129
+ agent.load_from_json(json_data)
130
+
131
+ self.items_by_href[href] = agent
132
+
133
+ return agent
134
+
135
+ def count_agents(self) -> int:
136
+ return len(self.items_by_href)
137
+
138
+
139
+
@@ -0,0 +1,44 @@
1
+ from .Helpers import nice_json
2
+
3
+
4
+ class PyloEx(Exception):
5
+ """
6
+ Base class for all Pylo exceptions
7
+ """
8
+ def __init__(self, arg, json_object=None):
9
+ if json_object is None:
10
+ Exception.__init__(self, arg)
11
+ return
12
+
13
+ text = "{}\nJSON output:\n{}".format(arg, nice_json(json_object))
14
+ super().__init__(self, text)
15
+
16
+
17
+ class PyloObjectNotFound(PyloEx):
18
+ def __init__(self, arg, json_object=None):
19
+ PyloEx(arg, json_object)
20
+
21
+
22
+ class PyloApiEx(PyloEx):
23
+ """
24
+ Base class for all Pylo API exceptions
25
+ """
26
+ def __init__(self, arg, json_object=None):
27
+ PyloEx(arg, json_object)
28
+
29
+
30
+ class PyloApiTooManyRequestsEx(PyloApiEx):
31
+ def __init__(self, arg, json_object=None):
32
+ PyloApiEx(arg, json_object)
33
+
34
+
35
+ class PyloApiUnexpectedSyntax(PyloApiEx):
36
+ def __init__(self, arg, json_object=None):
37
+ PyloApiEx(arg, json_object)
38
+
39
+ class PyloApiRequestForbiddenEx(PyloApiEx):
40
+ def __init__(self, arg, json_object=None):
41
+ PyloApiEx(arg, json_object)
42
+
43
+
44
+
@@ -0,0 +1,3 @@
1
+
2
+ from .functions import *
3
+ from .exports import *