Coverage for plugins/modules/purefb_info.py: 64%
677 statements
« prev ^ index » next coverage.py v7.14.1, created at 2026-10-30 17:06 -0400
« prev ^ index » next coverage.py v7.14.1, created at 2026-10-30 17:06 -0400
1#!/usr/bin/python
2# -*- coding: utf-8 -*-
4# (c) 2019, Simon Dodsley (simon@everpuredata.com)
5# GNU General Public License v3.0+ (see COPYING or
6# https://www.gnu.org/licenses/gpl-3.0.txt)
8from __future__ import absolute_import, division, print_function
10__metaclass__ = type
12ANSIBLE_METADATA = {
13 "metadata_version": "1.1",
14 "status": ["preview"],
15 "supported_by": "community",
16}
18DOCUMENTATION = r"""
19---
20module: purefb_info
21version_added: '1.0.0'
22short_description: Collect information from Everpure FlashBlade
23description:
24 - Collect information from a Everpure FlashBlade running the
25 Purity//FB operating system. By default, the module will collect basic
26 information including hosts, host groups, protection
27 groups and volume counts. Additional information can be collected
28 based on the configured set of arguements.
29author:
30 - Everpure Ansible Team (@sdodsley) <pure-ansible-team@everpuredata.com>
31options:
32 gather_subset:
33 description:
34 - When supplied, this argument will define the information to be collected.
35 Possible values for this include all, minimum, config, performance,
36 capacity, network, subnets, lags, filesystems, snapshots, buckets,
37 replication, policies, arrays, accounts, admins, ad, kerberos,
38 drives, servers, fleet and realms.
39 - The C(filesystems) subset surfaces the legacy file system keys
40 C(nfs_rules), C(export_policy), C(smb_client_policy) and
41 C(smb_share_policy). Purity//FB marks the underlying fields as
42 deprecated in favour of File System Exports, so these keys may
43 return C(null) in future releases. Read export policy
44 assignments from the per-filesystem C(file_system_exports)
45 list (and the top-level C(file_system_exports) view under
46 C(subset=filesystems)/C(subset=all)) instead. These keys will
47 be removed from this module in 2.0.0.
48 required: false
49 type: list
50 elements: str
51 default: minimum
52extends_documentation_fragment:
53 - everpure.flashblade.everpure.fb
54"""
56EXAMPLES = r"""
57- name: collect default set of info
58 everpure.flashblade.purefb_info:
59 fb_url: 10.10.10.2
60 api_token: T-55a68eb5-c785-4720-a2ca-8b03903bf641
61 register: blade_info
62- name: show default information
63 debug:
64 msg: "{{ blade_info['purefb_info']['default'] }}"
66- name: collect configuration and capacity info
67 everpure.flashblade.purefb_info:
68 gather_subset:
69 - config
70 fb_url: 10.10.10.2
71 api_token: T-55a68eb5-c785-4720-a2ca-8b03903bf641
72 register: blade_info
73- name: show config information
74 debug:
75 msg: "{{ blade_info['purefb_info']['config'] }}"
77- name: collect all info
78 everpure.flashblade.purefb_info:
79 gather_subset:
80 - all
81 fb_url: 10.10.10.2
82 api_token: T-55a68eb5-c785-4720-a2ca-8b03903bf641
83 register: blade_info
84- name: show all information
85 debug:
86 msg: "{{ blade_info['purefb_info'] }}"
87"""
89RETURN = r"""
90purefb_info:
91 description: Returns the information collected from the FlashBlade
92 returned: always
93 type: dict
94"""
97from ansible.module_utils.basic import AnsibleModule
98from ansible_collections.everpure.flashblade.plugins.module_utils.purefb import (
99 get_system,
100 purefb_argument_spec,
101)
102from ansible_collections.everpure.flashblade.plugins.module_utils.version import (
103 LooseVersion,
104)
105from ansible_collections.everpure.flashblade.plugins.module_utils.common import (
106 get_rest_api_version,
107)
108from ansible_collections.everpure.flashblade.plugins.module_utils.time_utils import (
109 milliseconds_to_time,
110)
111from datetime import datetime, timezone
112import time
114KMIP_API_VERSION = "2.1"
115DRIVES_API_VERSION = "2.5"
116SECURITY_API_VERSION = "2.7"
117BUCKET_API_VERSION = "2.8"
118SMB_CLIENT_API_VERSION = "2.10"
119SPACE_API_VERSION = "2.11"
120PUBLIC_API_VERSION = "2.12"
121NAP_API_VERSION = "2.13"
122RA_DURATION_API_VERSION = "2.14"
123SMTP_ENCRYPT_API_VERSION = "2.15"
124WORM_API_VERSION = "2.15"
125SERVERS_API_VERSION = "2.16"
126PASSWORD_POLICY_API_VERSION = "2.16"
127FLEET_API_VERSION = "2.17"
128TLS_API_VERSION = "2.17"
129REALMS_API_VERSION = "2.19"
130S3_EXPORT_API_VERSION = "2.20"
133def _bytes_to_human(bytes_number):
134 if bytes_number:
135 labels = ["B/s", "KB/s", "MB/s", "GB/s", "TB/s", "PB/s"]
136 i = 0
137 double_bytes = bytes_number
138 while i < len(labels) and bytes_number >= 1024:
139 double_bytes = bytes_number / 1024.0
140 i += 1
141 bytes_number = bytes_number / 1024
142 return str(round(double_bytes, 2)) + " " + labels[i]
143 return None
146def generate_default_dict(blade):
147 default_info = {}
148 # NOTE(latent bug): this previously read list(blade.get_versions().items)[0]
149 # - a single version string - so the SECURITY/NAP/RA_DURATION gates below
150 # were substring checks that could never match as intended. The
151 # get_rest_api_version()/LooseVersion migration makes these gates evaluate
152 # correctly, so the gated sections can now appear where they never did.
153 api_version = get_rest_api_version(blade)
154 defaults = list(blade.get_arrays().items)[0]
155 default_info["flashblade_name"] = defaults.name
156 default_info["purity_version"] = defaults.version
157 # the login (MOTD) banner, so purefb_banner changes can be read back
158 default_info["banner"] = getattr(defaults, "banner", None)
159 default_info["filesystems"] = blade.get_file_systems().total_item_count
160 default_info["snapshots"] = blade.get_file_system_snapshots().total_item_count
161 default_info["buckets"] = blade.get_buckets().total_item_count
162 default_info["object_store_users"] = blade.get_object_store_users().total_item_count
163 default_info["object_store_accounts"] = (
164 blade.get_object_store_accounts().total_item_count
165 )
166 default_info["blades"] = blade.get_blades().total_item_count
167 default_info["certificates"] = blade.get_certificates().total_item_count
168 default_info["total_capacity"] = list(blade.get_arrays_space().items)[0].capacity
169 default_info["api_versions"] = api_version
170 default_info["policies"] = blade.get_policies_all().total_item_count
171 # Count policies by type
172 all_policies = list(blade.get_policies_all().items)
173 default_info["snapshot_policies"] = sum(
174 1 for p in all_policies if p.policy_type == "snapshot"
175 )
176 default_info["access_policies"] = sum(
177 1 for p in all_policies if p.policy_type == "access"
178 )
179 default_info["nfs_policies"] = sum(
180 1 for p in all_policies if p.policy_type == "nfs"
181 )
182 default_info["smb_share_policies"] = sum(
183 1 for p in all_policies if p.policy_type == "smb_share"
184 )
185 default_info["smb_client_policies"] = sum(
186 1 for p in all_policies if p.policy_type == "smb_client"
187 )
188 default_info["network_policies"] = sum(
189 1 for p in all_policies if p.policy_type == "network"
190 )
191 default_info["worm_policies"] = sum(
192 1 for p in all_policies if p.policy_type == "worm"
193 )
194 default_info["telemetry_metrics_policies"] = sum(
195 1 for p in all_policies if p.policy_type == "telemetry-metrics"
196 )
197 default_info["certificate_groups"] = blade.get_certificate_groups().total_item_count
198 default_info["fs_replicas"] = blade.get_file_system_replica_links().total_item_count
199 default_info["remote_credentials"] = (
200 blade.get_object_store_remote_credentials().total_item_count
201 )
202 default_info["bucket_replicas"] = blade.get_bucket_replica_links().total_item_count
203 default_info["connected_arrays"] = blade.get_array_connections().total_item_count
204 default_info["targets"] = blade.get_targets().total_item_count
205 default_info["kerberos_keytabs"] = blade.get_keytabs().total_item_count
206 default_info["syslog_servers"] = len(blade.get_syslog_servers().items)
207 default_info["object_store_virtual_hosts"] = len(
208 blade.get_object_store_virtual_hosts().items
209 )
210 default_info["api_clients"] = len(blade.get_api_clients().items)
211 default_info["idle_timeout"] = int(defaults.idle_timeout / 60000)
212 if list(blade.get_arrays_eula().items)[0].signature.accepted:
213 default_info["EULA"] = "Signed"
214 else:
215 default_info["EULA"] = "Not Signed"
216 admin_settings = list(blade.get_admins_settings().items)[0]
217 default_info["max_login_attempts"] = admin_settings.max_login_attempts
218 default_info["min_password_length"] = admin_settings.min_password_length
219 if admin_settings.lockout_duration:
220 default_info["lockout_duration"] = (
221 str(admin_settings.lockout_duration / 1000) + " seconds"
222 )
223 default_info["smb_mode"] = getattr(defaults, "smb_mode", None)
224 default_info["timezone"] = defaults.time_zone
225 default_info["product_type"] = getattr(defaults, "product_type", "Unknown")
226 if LooseVersion(SECURITY_API_VERSION) <= LooseVersion(api_version):
227 dar = defaults.encryption.data_at_rest
228 default_info["encryption"] = {
229 "data_at_rest_enabled": dar.enabled,
230 "data_at_rest_algorithms": dar.algorithms,
231 "data_at_rest_entropy_source": dar.entropy_source,
232 }
233 keys = list(blade.get_support_verification_keys().items)
234 default_info["support_keys"] = {}
235 for key in keys:
236 keyname = key.name
237 default_info["support_keys"][keyname] = {key.verification_key}
238 default_info["security_update"] = getattr(defaults, "security_update", None)
239 if LooseVersion(NAP_API_VERSION) <= LooseVersion(api_version):
240 default_info["network_access_protocol"] = getattr(
241 defaults.network_access_policy, "name", "None"
242 )
243 ra_info = list(blade.get_support().items)[0]
244 if ra_info.remote_assist_active:
245 ra_expires = datetime.fromtimestamp(
246 int(ra_info.remote_assist_expires) / 1000
247 ).strftime("%Y-%m-%d %H:%M:%S")
248 ra_opened = datetime.fromtimestamp(
249 int(ra_info.remote_assist_opened) / 1000
250 ).strftime("%Y-%m-%d %H:%M:%S")
251 else:
252 ra_expires = ra_opened = None
253 default_info["remote_assist"] = {
254 "phonehome_enabled": ra_info.phonehome_enabled,
255 "proxy": ra_info.proxy,
256 "ra_active": ra_info.remote_assist_active,
257 "ra_expires": ra_expires,
258 "ra_opened": ra_opened,
259 "ra_status": ra_info.remote_assist_status,
260 }
261 if LooseVersion(RA_DURATION_API_VERSION) <= LooseVersion(api_version):
262 default_info["remote_assist"]["ra_duration"] = ra_info.remote_assist_duration
263 return default_info
266def generate_perf_dict(blade):
267 perf_info = {}
268 total_perf = list(blade.get_arrays_performance().items)[0]
269 http_perf = list(blade.get_arrays_performance(protocol="http").items)[0]
270 s3_perf = list(blade.get_arrays_performance(protocol="s3").items)[0]
271 nfs_perf = list(blade.get_arrays_performance(protocol="nfs").items)[0]
272 perf_info["aggregate"] = {
273 "bytes_per_op": total_perf.bytes_per_op,
274 "bytes_per_read": total_perf.bytes_per_read,
275 "bytes_per_write": total_perf.bytes_per_write,
276 "read_bytes_per_sec": total_perf.read_bytes_per_sec,
277 "reads_per_sec": total_perf.reads_per_sec,
278 "usec_per_other_op": total_perf.usec_per_other_op,
279 "usec_per_read_op": total_perf.usec_per_read_op,
280 "usec_per_write_op": total_perf.usec_per_write_op,
281 "write_bytes_per_sec": total_perf.write_bytes_per_sec,
282 "writes_per_sec": total_perf.writes_per_sec,
283 }
284 perf_info["http"] = {
285 "bytes_per_op": http_perf.bytes_per_op,
286 "bytes_per_read": http_perf.bytes_per_read,
287 "bytes_per_write": http_perf.bytes_per_write,
288 "read_bytes_per_sec": http_perf.read_bytes_per_sec,
289 "reads_per_sec": http_perf.reads_per_sec,
290 "usec_per_other_op": http_perf.usec_per_other_op,
291 "usec_per_read_op": http_perf.usec_per_read_op,
292 "usec_per_write_op": http_perf.usec_per_write_op,
293 "write_bytes_per_sec": http_perf.write_bytes_per_sec,
294 "writes_per_sec": http_perf.writes_per_sec,
295 }
296 perf_info["s3"] = {
297 "bytes_per_op": s3_perf.bytes_per_op,
298 "bytes_per_read": s3_perf.bytes_per_read,
299 "bytes_per_write": s3_perf.bytes_per_write,
300 "read_bytes_per_sec": s3_perf.read_bytes_per_sec,
301 "reads_per_sec": s3_perf.reads_per_sec,
302 "usec_per_other_op": s3_perf.usec_per_other_op,
303 "usec_per_read_op": s3_perf.usec_per_read_op,
304 "usec_per_write_op": s3_perf.usec_per_write_op,
305 "write_bytes_per_sec": s3_perf.write_bytes_per_sec,
306 "writes_per_sec": s3_perf.writes_per_sec,
307 }
308 perf_info["nfs"] = {
309 "bytes_per_op": nfs_perf.bytes_per_op,
310 "bytes_per_read": nfs_perf.bytes_per_read,
311 "bytes_per_write": nfs_perf.bytes_per_write,
312 "read_bytes_per_sec": nfs_perf.read_bytes_per_sec,
313 "reads_per_sec": nfs_perf.reads_per_sec,
314 "usec_per_other_op": nfs_perf.usec_per_other_op,
315 "usec_per_read_op": nfs_perf.usec_per_read_op,
316 "usec_per_write_op": nfs_perf.usec_per_write_op,
317 "write_bytes_per_sec": nfs_perf.write_bytes_per_sec,
318 "writes_per_sec": nfs_perf.writes_per_sec,
319 }
320 if blade.get_array_connections_performance_replication().total_item_count > 0:
321 file_repl_perf = list(
322 blade.get_array_connections_performance_replication(
323 type="file-system"
324 ).items
325 )[0]
326 obj_repl_perf = list(
327 blade.get_array_connections_performance_replication(
328 type="object-store"
329 ).items
330 )[0]
331 perf_info["file_replication"] = {
332 "received_bytes_per_sec": getattr(
333 file_repl_perf.periodic, "received_bytes_per_sec", None
334 ),
335 "transmitted_bytes_per_sec": getattr(
336 file_repl_perf.periodic, "transmitted_bytes_per_sec", None
337 ),
338 }
339 perf_info["object_replication"] = {
340 "received_bytes_per_sec": getattr(
341 obj_repl_perf.periodic, "received_bytes_per_sec", None
342 ),
343 "transmitted_bytes_per_sec": getattr(
344 obj_repl_perf.periodic, "transmitted_bytes_per_sec", None
345 ),
346 }
347 return perf_info
350def generate_config_dict(blade):
351 config_info = {}
352 api_version = get_rest_api_version(blade)
353 config_info["dns"] = {}
354 dns_configs = list(blade.get_dns().items)
355 for config in dns_configs:
356 config_info["dns"][config.name] = {
357 "nameservers": config.nameservers,
358 "domain": config.domain,
359 "services": getattr(config, "services", None),
360 }
361 if hasattr(config, "sources"):
362 # sources is a list of references (at most one), not a reference
363 sources = getattr(config, "sources", None)
364 if isinstance(sources, list):
365 source = getattr(sources[0], "name", None) if sources else None
366 else:
367 source = getattr(sources, "name", None)
368 config_info["dns"][config.name]["source"] = source
369 smtp_config = list(blade.get_smtp_servers().items)
370 config_info["smtp"] = {}
371 for config in smtp_config:
372 config_info["smtp"][config.name] = {
373 "relay_host": getattr(config, "relay_host", None),
374 "sender_domain": getattr(config, "sender_domain", None),
375 "encryption_mode": getattr(config, "encryption_mode", None),
376 }
377 alert_config = list(blade.get_alert_watchers().items)
378 config_info["alert_watchers"] = {}
379 for config in alert_config:
380 config_info["alert_watchers"][config.name] = {
381 "enabled": config.enabled,
382 "minimum_notification_severity": config.minimum_notification_severity,
383 }
384 directory_services = list(blade.get_directory_services().items)
385 for ds_service in directory_services:
386 if ds_service.name in {"management", "nfs", "smb"}:
387 key = f"{ds_service.name}_directory_service"
388 config_info[key] = {
389 "base_dn": ds_service.base_dn,
390 "bind_user": ds_service.bind_user,
391 "ca_certificate": ds_service.ca_certificate.name,
392 "ca_certificate_group": ds_service.ca_certificate_group.name,
393 "enabled": ds_service.enabled,
394 "management": {
395 "user_login_attribute": ds_service.management.user_login_attribute,
396 "user_object_class": ds_service.management.user_object_class,
397 },
398 "nis_servers": ds_service.nfs.nis_servers,
399 "nis_domains": ds_service.nfs.nis_domains,
400 "services": ds_service.services,
401 "join_ou": ds_service.smb.join_ou,
402 "uris": ds_service.uris,
403 }
404 # Forward backwards compatability
405 config_info["array_management"] = config_info["management_directory_service"]
407 config_info["directory_service_roles"] = {}
408 roles = list(blade.get_directory_services_roles().items)
409 for ds_role in roles:
410 role_name = ds_role.name
411 config_info["directory_service_roles"][role_name] = {
412 "group": ds_role.group,
413 "group_base": ds_role.group_base,
414 "role": ds_role.role.name,
415 }
416 config_info["ntp"] = list(blade.get_arrays().items)[0].ntp_servers
417 certs = list(blade.get_certificates().items)
418 config_info["ssl_certs"] = {}
419 for cert in certs:
420 cert_name = cert.name
421 valid_from = time.strftime(
422 "%a, %d %b %Y %H:%M:%S %Z",
423 time.localtime(cert.valid_from / 1000),
424 )
425 valid_to = time.strftime(
426 "%a, %d %b %Y %H:%M:%S %Z",
427 time.localtime(cert.valid_to / 1000),
428 )
429 config_info["ssl_certs"][cert_name] = {
430 "certificate": getattr(cert, "certificate", None),
431 "certificate_type": getattr(cert, "certificate_type", None),
432 "common_name": getattr(cert, "common_name", None),
433 "country": getattr(cert, "country", None),
434 "email": getattr(cert, "email", None),
435 "intermediate_certificate": getattr(cert, "intermediate_certificate", None),
436 "issued_by": getattr(cert, "issued_by", None),
437 "issued_to": getattr(cert, "issued_to", None),
438 "key_size": getattr(cert, "key_size", None),
439 "locality": getattr(cert, "locality", None),
440 "organization": getattr(cert, "organization", None),
441 "organizational_unit": getattr(cert, "organizational_unit", None),
442 "state": getattr(cert, "state", None),
443 "status": getattr(cert, "status", None),
444 "subject_alternative_names": getattr(
445 cert, "subject_alternative_names", None
446 ),
447 "valid_from": valid_from,
448 "valid_to": valid_to,
449 }
450 crt_grps = list(blade.get_certificate_groups().items)
451 config_info["certificate_groups"] = []
452 for crt_grp in crt_grps:
453 config_info["certificate_groups"].append(crt_grp.name)
454 config_info["syslog_servers"] = {}
455 syslog_servers = list(blade.get_syslog_servers().items)
456 for server in syslog_servers:
457 server_name = server.name
458 config_info["syslog_servers"][server_name] = {
459 "uri": server.uri,
460 "services": getattr(server, "services", None),
461 }
462 snmp_agents = list(blade.get_snmp_agents().items)
463 config_info["snmp_agents"] = {}
464 for agent in snmp_agents:
465 agent_name = agent.name
466 config_info["snmp_agents"][agent_name] = {
467 "version": agent.version,
468 "engine_id": agent.engine_id,
469 }
470 if config_info["snmp_agents"][agent_name]["version"] == "v3":
471 config_info["snmp_agents"][agent_name]["auth_protocol"] = getattr(
472 agent.v3, "auth_protocol", None
473 )
474 config_info["snmp_agents"][agent_name]["privacy_protocol"] = getattr(
475 agent.v3, "privacy_protocol", None
476 )
477 config_info["snmp_agents"][agent_name]["user"] = getattr(
478 agent.v3, "user", None
479 )
480 config_info["snmp_managers"] = {}
481 snmp_managers = list(blade.get_snmp_managers().items)
482 for manager in snmp_managers:
483 mgr_name = manager.name
484 config_info["snmp_managers"][mgr_name] = {
485 "version": manager.version,
486 "host": manager.host,
487 "notification": manager.notification,
488 }
489 if config_info["snmp_managers"][mgr_name]["version"] == "v3":
490 config_info["snmp_managers"][mgr_name]["auth_protocol"] = getattr(
491 manager.v3, "auth_protocol", None
492 )
493 config_info["snmp_managers"][mgr_name]["privacy_protocol"] = getattr(
494 manager.v3, "privacy_protocol", None
495 )
496 config_info["snmp_managers"][mgr_name]["user"] = getattr(
497 manager.v3, "user", None
498 )
499 if LooseVersion(KMIP_API_VERSION) <= LooseVersion(api_version):
500 config_info["kmip"] = {}
501 for kmip in list(blade.get_kmip().items):
502 config_info["kmip"][kmip.name] = {
503 "uris": getattr(kmip, "uris", None),
504 "ca_certificate": getattr(
505 getattr(kmip, "ca_certificate", None), "name", None
506 ),
507 "ca_certificate_group": getattr(
508 getattr(kmip, "ca_certificate_group", None), "name", None
509 ),
510 }
511 if LooseVersion(SMTP_ENCRYPT_API_VERSION) <= LooseVersion(api_version):
512 config_info["saml2sso"] = {}
513 saml2 = list(blade.get_sso_saml2_idps().items)
514 if saml2:
515 config_info["saml2sso"] = {
516 "enabled": saml2[0].enabled,
517 "array_url": saml2[0].array_url,
518 "name": saml2[0].name,
519 "idp": {
520 "url": getattr(saml2[0].idp, "url", None),
521 "encrypt_enabled": saml2[0].idp.encrypt_assertion_enabled,
522 "sign_enabled": saml2[0].idp.sign_request_enabled,
523 "metadata_url": saml2[0].idp.metadata_url,
524 },
525 "sp": {
526 "decrypt_cred": getattr(
527 saml2[0].sp.decryption_credential, "name", None
528 ),
529 "sign_cred": getattr(saml2[0].sp.signing_credential, "name", None),
530 },
531 }
532 return config_info
535def generate_subnet_dict(blade):
536 sub_info = {}
537 subnets = list(blade.get_subnets().items)
538 for sub in subnets:
539 sub_name = sub.name
540 if sub.enabled:
541 sub_info[sub_name] = {
542 "gateway": sub.gateway,
543 "mtu": sub.mtu,
544 "vlan": sub.vlan,
545 "prefix": sub.prefix,
546 "services": sub.services,
547 }
548 # Subnets on an array in Layer 3 (BGP) mode have no LAG
549 sub_info[sub_name]["lag"] = getattr(
550 sub.link_aggregation_group, "name", None
551 )
552 sub_info[sub_name]["interfaces"] = []
553 for iface in sub.interfaces or []:
554 sub_info[sub_name]["interfaces"].append({"name": iface.name})
555 return sub_info
558def generate_lag_dict(blade):
559 lag_info = {}
560 res = blade.get_link_aggregation_groups()
561 if res.status_code != 200:
562 # An array in Layer 3 (BGP) mode has no link aggregation groups
563 # and the endpoint returns an error rather than an empty list
564 return lag_info
565 for groupcnt in list(res.items):
566 lag_name = groupcnt.name
567 lag_info[lag_name] = {
568 "lag_speed": groupcnt.lag_speed,
569 "port_speed": groupcnt.port_speed,
570 "mac_address": getattr(groupcnt, "mac_address", None),
571 "status": groupcnt.status,
572 }
573 lag_info[lag_name]["ports"] = []
574 for port in groupcnt.ports or []:
575 lag_info[lag_name]["ports"].append({"name": port.name})
576 return lag_info
579def generate_admin_dict(blade):
580 admin_info = {}
581 admins = list(blade.get_admins().items)
582 for admin in admins:
583 admin_name = admin.name
584 admin_info[admin_name] = {
585 "public_key": admin.public_key,
586 "local": admin.is_local,
587 "role": admin.role.name,
588 "locked": admin.locked,
589 "lockout_remaining": getattr(admin, "lockout_remaining", None),
590 }
591 if hasattr(admin.api_token, "expires_at"):
592 if admin.api_token.expires_at:
593 admin_info[admin_name]["token_expires"] = datetime.fromtimestamp(
594 admin.api_token.expires_at / 1000
595 ).strftime("%Y-%m-%d %H:%M:%S")
596 else:
597 admin_info[admin_name]["token_expires"] = None
598 if hasattr(admin.api_token, "created_at"):
599 if admin.api_token.created_at:
600 admin_info[admin_name]["token_created"] = datetime.fromtimestamp(
601 admin.api_token.created_at / 1000
602 ).strftime("%Y-%m-%d %H:%M:%S")
603 else:
604 admin_info[admin_name]["token_created"] = None
605 return admin_info
608def generate_targets_dict(blade):
609 targets_info = {}
610 targets = list(blade.get_targets().items)
611 for target in targets:
612 target_name = target.name
613 targets_info[target_name] = {
614 "address": target.address,
615 "status": target.status,
616 "status_details": target.status_details,
617 "ca_certificate_group": getattr(
618 getattr(target, "ca_certificate_group", None), "name", None
619 ),
620 }
621 return targets_info
624def generate_remote_creds_dict(blade):
625 remote_creds_info = {}
626 remote_creds = list(blade.get_object_store_remote_credentials().items)
627 for remote_cred in remote_creds:
628 cred_name = remote_cred.name
629 remote_creds_info[cred_name] = {
630 "access_key": remote_cred.access_key_id,
631 "remote_array": remote_cred.remote.name,
632 "secret_access_key": remote_cred.secret_access_key,
633 }
634 return remote_creds_info
637def generate_file_repl_dict(blade):
638 file_repl_info = {}
639 file_links = list(blade.get_file_system_replica_links().items)
640 for file_link in file_links:
641 fs_name = file_link.local_file_system.name
642 file_repl_info[fs_name] = {
643 "direction": file_link.direction,
644 "link_type": file_link.link_type,
645 "lag": file_link.lag,
646 "status": file_link.status,
647 # the SDK model calls this status_details; the key is kept for
648 # backwards compatibility
649 "status_detail": getattr(file_link, "status_details", None),
650 "remote_fs": file_link.remote.name
651 + ":"
652 + file_link.remote_file_system.name,
653 "recovery_point": file_link.recovery_point,
654 }
655 file_repl_info[fs_name]["policies"] = []
656 for policy_cnt in file_link.policies:
657 file_repl_info[fs_name]["policies"].append(policy_cnt.display_name)
658 return file_repl_info
661def generate_bucket_repl_dict(blade):
662 bucket_repl_info = {}
663 bucket_links = list(blade.get_bucket_replica_links().items)
664 for bucket_link in bucket_links:
665 bucket_name = bucket_link.local_bucket.name
666 bucket_repl_info[bucket_name] = {
667 "direction": bucket_link.direction,
668 "lag": bucket_link.lag,
669 "paused": bucket_link.paused,
670 "status": bucket_link.status,
671 "status_details": bucket_link.status_details,
672 "remote_bucket": bucket_link.remote_bucket.name,
673 "remote_array": bucket_link.remote.name,
674 "remote_credentials": bucket_link.remote_credentials.name,
675 "recovery_point": bucket_link.recovery_point,
676 "object_backlog": {
677 "bytes_count": bucket_link.object_backlog.bytes_count,
678 "delete_ops_count": bucket_link.object_backlog.delete_ops_count,
679 "other_ops_count": bucket_link.object_backlog.other_ops_count,
680 "put_ops_count": bucket_link.object_backlog.put_ops_count,
681 },
682 "cascading_enabled": bucket_link.cascading_enabled,
683 }
684 return bucket_repl_info
687def generate_network_dict(blade):
688 net_info = {}
689 ports = list(blade.get_network_interfaces().items)
690 for port in ports:
691 int_name = port.name
692 # attached_servers is a list of references on the API even though an
693 # interface carries at most one server; the subnet comes back as a
694 # plain dict on current SDKs and as a reference on older ones.
695 attached = getattr(port, "attached_servers", None) or []
696 if not isinstance(attached, list):
697 attached = []
698 attached_names = [getattr(srv, "name", None) for srv in attached]
699 subnet = getattr(port, "subnet", None)
700 if isinstance(subnet, dict):
701 subnet_name = subnet.get("name")
702 else:
703 subnet_name = getattr(subnet, "name", None)
704 net_info[int_name] = {
705 "type": getattr(port, "type", None),
706 "mtu": getattr(port, "mtu", None),
707 "vlan": getattr(port, "vlan", None),
708 "address": getattr(port, "address", None),
709 "services": getattr(port, "services", None),
710 "gateway": getattr(port, "gateway", None),
711 "netmask": getattr(port, "netmask", None),
712 "server": attached_names[0] if attached_names else None,
713 "attached_servers": attached_names,
714 "subnet": subnet_name,
715 "enabled": port.enabled,
716 }
717 return net_info
720def generate_capacity_dict(blade):
721 capacity_info = {}
722 total_cap = list(blade.get_arrays_space().items)[0]
723 file_cap = list(blade.get_arrays_space(type="file-system").items)[0]
724 object_cap = list(blade.get_arrays_space(type="object-store").items)[0]
725 capacity_info["total"] = total_cap.capacity
726 capacity_info["aggregate"] = {
727 "data_reduction": total_cap.space.data_reduction,
728 "snapshots": total_cap.space.snapshots,
729 "total_physical": total_cap.space.total_physical,
730 "unique": total_cap.space.unique,
731 "virtual": total_cap.space.virtual,
732 "total_provisioned": total_cap.space.total_provisioned,
733 "available_provisioned": total_cap.space.available_provisioned,
734 "available_ratio": total_cap.space.available_ratio,
735 "destroyed": total_cap.space.destroyed,
736 "destroyed_virtual": total_cap.space.destroyed_virtual,
737 "shared": getattr(total_cap.space, "shared", None),
738 }
739 capacity_info["file-system"] = {
740 "data_reduction": file_cap.space.data_reduction,
741 "snapshots": file_cap.space.snapshots,
742 "total_physical": file_cap.space.total_physical,
743 "unique": file_cap.space.unique,
744 "virtual": file_cap.space.virtual,
745 "total_provisioned": total_cap.space.total_provisioned,
746 "available_provisioned": total_cap.space.available_provisioned,
747 "available_ratio": total_cap.space.available_ratio,
748 "destroyed": total_cap.space.destroyed,
749 "destroyed_virtual": total_cap.space.destroyed_virtual,
750 "shared": getattr(total_cap.space, "shared", None),
751 }
752 capacity_info["object-store"] = {
753 "data_reduction": object_cap.space.data_reduction,
754 "snapshots": object_cap.space.snapshots,
755 "total_physical": object_cap.space.total_physical,
756 "unique": object_cap.space.unique,
757 "virtual": file_cap.space.virtual,
758 "total_provisioned": total_cap.space.total_provisioned,
759 "available_provisioned": total_cap.space.available_provisioned,
760 "available_ratio": total_cap.space.available_ratio,
761 "destroyed": total_cap.space.destroyed,
762 "destroyed_virtual": total_cap.space.destroyed_virtual,
763 "shared": getattr(total_cap.space, "shared", None),
764 }
766 return capacity_info
769def generate_snap_dict(blade):
770 snap_info = {}
771 snaps = list(blade.get_file_system_snapshots().items)
772 api_version = get_rest_api_version(blade)
773 for snap in snaps:
774 snapshot = snap.name
775 snap_info[snapshot] = {
776 "destroyed": snap.destroyed,
777 "source": snap.source.location.name,
778 "suffix": snap.suffix,
779 "created": datetime.fromtimestamp(
780 snap.created / 1000,
781 tz=timezone.utc,
782 ).strftime("%Y-%m-%dT%H:%M:%SZ"),
783 "time_remaining": getattr(snap, "time_remaining", None),
784 "policy": getattr(getattr(snap, "policy", None), "name", None),
785 "owner": snap.owner.name,
786 "owner_destroyed": snap.owner_destroyed,
787 "source_display_name": snap.source.display_name,
788 "source_is_local": snap.source.is_local,
789 "source_location": snap.source.location.name,
790 "policies": [],
791 }
792 if LooseVersion(PUBLIC_API_VERSION) <= LooseVersion(api_version):
793 if hasattr(snap, "policies"):
794 for policy in snap.policies:
795 snap_info[snapshot]["policies"].append(
796 {
797 "name": policy.name,
798 "location": policy.location.name,
799 }
800 )
801 return snap_info
804def generate_snap_transfer_dict(blade):
805 snap_transfer_info = {}
806 snap_transfers = list(blade.get_file_system_snapshots_transfer().items)
807 for snap_transfer in snap_transfers:
808 transfer = snap_transfer.name
809 snap_transfer_info[transfer] = {
810 "completed": snap_transfer.completed,
811 "data_transferred": snap_transfer.data_transferred,
812 "progress": snap_transfer.progress,
813 "direction": snap_transfer.direction,
814 "remote": snap_transfer.remote.name,
815 "remote_snapshot": snap_transfer.remote_snapshot.name,
816 "started": snap_transfer.started,
817 "status": snap_transfer.status,
818 }
819 return snap_transfer_info
822def generate_array_conn_dict(blade):
823 array_conn_info = {}
824 arrays = list(blade.get_array_connections().items)
825 for array in arrays:
826 # Keep the connection object intact: rebinding `array` to the remote
827 # name here made every attribute read below fail with AttributeError.
828 remote_name = array.remote.name
829 array_conn_info[remote_name] = {
830 "encrypted": array.encrypted,
831 "replication_addresses": array.replication_addresses,
832 "management_address": array.management_address,
833 "status": array.status,
834 "version": array.version,
835 "ca_certificate_group": getattr(
836 getattr(array, "ca_certificate_group", None), "name", None
837 ),
838 "throttle": {
839 "default_limit": _bytes_to_human(
840 getattr(array.throttle, "default_limit", None)
841 ),
842 "window_limit": _bytes_to_human(
843 getattr(array.throttle, "window_limit", None)
844 ),
845 "window_start": (
846 milliseconds_to_time(getattr(array.throttle.window, "start", None))
847 if getattr(array.throttle.window, "start", None)
848 else None
849 ),
850 "window_end": (
851 milliseconds_to_time(getattr(array.throttle.window, "end", None))
852 if getattr(array.throttle.window, "end", None)
853 else None
854 ),
855 },
856 }
857 return array_conn_info
860def generate_policies_dict(blade):
861 policies_info = {}
862 policies = list(blade.get_policies().items)
863 for policy in policies:
864 policy_name = policy.name
865 policies_info[policy_name] = {
866 "enabled": policy.enabled,
867 "retention_lock": getattr(policy, "retention_lock", None),
868 "policy_type": policy.policy_type,
869 "rules": {},
870 }
871 rules = getattr(policy, "rules", None)
872 if rules:
873 policies_info[policy_name]["rules"] = {
874 "at": getattr(rules[0], "at", None),
875 "every": getattr(rules[0], "every", None),
876 "keep_for": getattr(rules[0], "keep_for", None),
877 "time_zone": getattr(rules[0], "time_zone", None),
878 }
879 return policies_info
882def generate_all_policies_dict(blade):
883 policies_info = {}
884 policies = list(blade.get_policies_all().items)
885 for policy in policies:
886 policy_name = policy.name
887 policies_info[policy_name] = {
888 "id": policy.id,
889 "enabled": policy.enabled,
890 "is_local": getattr(policy, "is_local", None),
891 "location": getattr(getattr(policy, "location", None), "name", None),
892 "policy_type": policy.policy_type,
893 }
894 return policies_info
897def generate_bucket_dict(blade):
898 bucket_info = {}
899 buckets = list(blade.get_buckets().items)
900 for bucket in buckets:
901 bucket_name = bucket.name
902 bucket_info[bucket_name] = {
903 "versioning": bucket.versioning,
904 "bucket_type": getattr(bucket, "bucket_type", None),
905 "object_count": bucket.object_count,
906 "id": bucket.id,
907 "account_name": bucket.account.name,
908 "data_reduction": getattr(bucket.space, "data_reduction", None),
909 "snapshot_space": bucket.space.snapshots,
910 "total_physical_space": bucket.space.total_physical,
911 "unique_space": bucket.space.unique,
912 "virtual_space": bucket.space.virtual,
913 "total_provisioned_space": getattr(bucket.space, "total_provisioned", None),
914 "available_provisioned_space": getattr(
915 bucket.space, "available_provisioned", None
916 ),
917 "available_ratio": getattr(bucket.space, "available_ratio", None),
918 "destroyed_space": getattr(bucket.space, "destroyed", None),
919 "destroyed_virtual_space": getattr(bucket.space, "destroyed_virtual", None),
920 "created": bucket.created,
921 "destroyed": bucket.destroyed,
922 "time_remaining": getattr(bucket, "time_remaining", None),
923 "time_remaining_status": getattr(bucket, "time_remaining_status", None),
924 "retention_lock": bucket.retention_lock,
925 "quota_limit": bucket.quota_limit,
926 "object_lock_config": {
927 "enabled": bucket.object_lock_config.enabled,
928 "freeze_locked_objects": bucket.object_lock_config.freeze_locked_objects,
929 "default_retention": getattr(
930 bucket.object_lock_config, "default_retention", None
931 ),
932 "default_retention_mode": getattr(
933 bucket.object_lock_config,
934 "default_retention_mode",
935 None,
936 ),
937 },
938 "eradication_config": {
939 "eradication_delay": getattr(
940 bucket.eradication_config, "eradication_delay", None
941 ),
942 "eradication_mode": getattr(
943 bucket.eradication_config, "eradication_mode", None
944 ),
945 "manual_eradication": bucket.eradication_config.manual_eradication,
946 },
947 "public_status": getattr(bucket, "public_status", None),
948 "public_access_config": {
949 "block_new_public_policies": getattr(
950 getattr(bucket, "public_access_config", None),
951 "block_new_public_policies",
952 None,
953 ),
954 "block_public_access": getattr(
955 getattr(bucket, "public_access_config", None),
956 "block_public_access",
957 None,
958 ),
959 },
960 "lifecycle_rules": {},
961 }
962 if not bucket.destroyed:
963 all_rules = list(blade.get_lifecycle_rules(bucket_ids=[bucket.id]).items)
964 for rule in all_rules:
965 bucket_name = rule.bucket.name
966 rule_id = rule.rule_id
967 if rule.keep_previous_version_for:
968 keep_previous_version_for = int(
969 rule.keep_previous_version_for / 86400000
970 )
971 else:
972 keep_previous_version_for = None
973 if rule.keep_current_version_for:
974 keep_current_version_for = int(
975 rule.keep_current_version_for / 86400000
976 )
977 else:
978 keep_current_version_for = None
979 if rule.abort_incomplete_multipart_uploads_after:
980 abort_incomplete_multipart_uploads_after = int(
981 rule.abort_incomplete_multipart_uploads_after / 86400000
982 )
983 else:
984 abort_incomplete_multipart_uploads_after = None
985 if rule.keep_current_version_until:
986 keep_current_version_until = datetime.fromtimestamp(
987 rule.keep_current_version_until / 1000
988 ).strftime("%Y-%m-%d")
989 else:
990 keep_current_version_until = None
991 bucket_info[bucket_name]["lifecycle_rules"][rule_id] = {
992 "keep_previous_version_for (days)": keep_previous_version_for,
993 "keep_current_version_for (days)": keep_current_version_for,
994 "keep_current_version_until": keep_current_version_until,
995 "prefix": rule.prefix,
996 "enabled": rule.enabled,
997 "abort_incomplete_multipart_uploads_after (days)": abort_incomplete_multipart_uploads_after,
998 "cleanup_expired_object_delete_marker": rule.cleanup_expired_object_delete_marker,
999 }
1001 return bucket_info
1004def generate_kerb_dict(blade):
1005 kerb_info = {}
1007 for keytab in blade.get_keytabs().items:
1008 prefix = keytab.prefix
1009 suffix = keytab.suffix
1011 if prefix not in kerb_info:
1012 kerb_info[prefix] = {}
1014 kerb_info[prefix][suffix] = {
1015 "fqdn": keytab.fqdn,
1016 "kvno": keytab.kvno,
1017 "principal": keytab.principal,
1018 "realm": keytab.realm,
1019 "encryption_type": keytab.encryption_type,
1020 "server": getattr(getattr(keytab, "server", None), "name", None),
1021 "source": getattr(getattr(keytab, "source", None), "name", None),
1022 }
1024 return kerb_info
1027def generate_ad_dict(blade):
1028 ad_info = {}
1030 active_directory = blade.get_active_directory()
1031 if active_directory.total_item_count == 0:
1032 return ad_info
1034 for ad in active_directory.items:
1035 ad_info[ad.name] = {
1036 "computer": ad.computer_name,
1037 "domain": ad.domain,
1038 "directory_servers": ad.directory_servers,
1039 "kerberos_servers": ad.kerberos_servers,
1040 "service_principals": ad.service_principal_names,
1041 "join_ou": ad.join_ou,
1042 "encryption_types": ad.encryption_types,
1043 "global_catalog_servers": getattr(ad, "global_catalog_servers", None),
1044 "server": getattr(getattr(ad, "server", None), "name", None),
1045 }
1047 return ad_info
1050def generate_bucket_access_policies_dict(blade):
1051 policies_info = {}
1053 for bucket in blade.get_buckets().items:
1054 res = blade.get_buckets_bucket_access_policies(bucket_names=[bucket.name])
1056 if res.status_code == 200 and res.total_item_count != 0:
1057 for policy in res.items:
1058 policies_info[policy.name] = {
1059 # BucketAccessPolicy has no description field, unlike the
1060 # object store access policy reported further down.
1061 "description": getattr(policy, "description", None),
1062 "enabled": policy.enabled,
1063 "local": policy.is_local,
1064 "rules": [],
1065 }
1067 for rule in policy.rules:
1068 policies_info[policy.name]["rules"].append(
1069 {
1070 "actions": rule.actions,
1071 "resources": rule.resources,
1072 "all_principals": rule.principals.all,
1073 "effect": rule.effect,
1074 "name": rule.name,
1075 }
1076 )
1078 return policies_info
1081def generate_bucket_cross_object_policies_dict(blade):
1082 policies_info = {}
1084 for bucket in blade.get_buckets().items:
1085 res = blade.get_buckets_cross_origin_resource_sharing_policies(
1086 bucket_names=[bucket.name]
1087 )
1088 if res.status_code != 200:
1089 continue
1091 for policy in res.items:
1092 # The allowed_* lists live on the rules, not on the policy.
1093 policies_info[policy.name] = {
1094 "enabled": getattr(policy, "enabled", None),
1095 "local": getattr(policy, "is_local", None),
1096 "rules": [],
1097 }
1098 rules = blade.get_buckets_cross_origin_resource_sharing_policies_rules(
1099 policy_names=[policy.name]
1100 )
1101 if rules.status_code != 200:
1102 continue
1103 for rule in rules.items:
1104 policies_info[policy.name]["rules"].append(
1105 {
1106 "name": rule.name,
1107 "allowed_headers": getattr(rule, "allowed_headers", None),
1108 "allowed_methods": getattr(rule, "allowed_methods", None),
1109 "allowed_origins": getattr(rule, "allowed_origins", None),
1110 }
1111 )
1113 return policies_info
1116def generate_object_store_access_policies_dict(blade):
1117 policies_info = {}
1119 for policy in blade.get_object_store_access_policies().items:
1120 policy_name = policy.name
1121 policies_info[policy_name] = {
1122 "ARN": policy.arn,
1123 "description": policy.description,
1124 "enabled": policy.enabled,
1125 "local": policy.is_local,
1126 "rules": [],
1127 }
1129 for rule in policy.rules:
1130 policies_info[policy_name]["rules"].append(
1131 {
1132 "actions": rule.actions,
1133 "conditions": {
1134 "source_ips": rule.conditions.source_ips,
1135 "s3_delimiters": rule.conditions.s3_delimiters,
1136 "s3_prefixes": rule.conditions.s3_prefixes,
1137 },
1138 "effect": rule.effect,
1139 "name": rule.name,
1140 }
1141 )
1143 return policies_info
1146def generate_nfs_export_policies_dict(blade):
1147 policies_info = {}
1149 for policy in blade.get_nfs_export_policies().items:
1150 policy_name = policy.name
1151 policies_info[policy_name] = {
1152 "local": policy.is_local,
1153 "enabled": policy.enabled,
1154 "rules": [],
1155 }
1157 for rule in policy.rules:
1158 policies_info[policy_name]["rules"].append(
1159 {
1160 "access": rule.access,
1161 "anongid": rule.anongid,
1162 "anonuid": rule.anonuid,
1163 "atime": rule.atime,
1164 "client": rule.client,
1165 "fileid_32bit": rule.fileid_32bit,
1166 "permission": rule.permission,
1167 "secure": rule.secure,
1168 "security": rule.security,
1169 "index": rule.index,
1170 }
1171 )
1173 return policies_info
1176def generate_smb_client_policies_dict(blade):
1177 policies_info = {}
1179 for policy in blade.get_smb_client_policies().items:
1180 policy_name = policy.name
1181 policies_info[policy_name] = {
1182 "local": policy.is_local,
1183 "enabled": policy.enabled,
1184 "version": policy.version,
1185 "rules": [],
1186 }
1188 for rule in policy.rules:
1189 policies_info[policy_name]["rules"].append(
1190 {
1191 "name": rule.name,
1192 "change": getattr(rule, "change", None),
1193 "full_control": getattr(rule, "full_control", None),
1194 "principal": getattr(rule, "principal", None),
1195 "read": getattr(rule, "read", None),
1196 "client": getattr(rule, "client", None),
1197 "index": getattr(rule, "index", None),
1198 "policy_version": getattr(rule, "policy_version", None),
1199 "encryption": getattr(rule, "encryption", None),
1200 "permission": getattr(rule, "permission", None),
1201 }
1202 )
1204 return policies_info
1207def generate_smb_share_policies_dict(blade):
1208 policies_info = {}
1210 for policy in blade.get_smb_share_policies().items:
1211 policy_name = policy.name
1212 policies_info[policy_name] = {
1213 "local": getattr(policy, "is_local", None),
1214 "enabled": getattr(policy, "enabled", None),
1215 "rules": [],
1216 }
1218 for rule in getattr(policy, "rules", None) or []:
1219 policies_info[policy_name]["rules"].append(
1220 {
1221 "name": rule.name,
1222 "principal": getattr(rule, "principal", None),
1223 "change": getattr(rule, "change", None),
1224 "full_control": getattr(rule, "full_control", None),
1225 "read": getattr(rule, "read", None),
1226 }
1227 )
1229 return policies_info
1232def generate_network_access_policies_dict(blade):
1233 policies_info = {}
1235 for policy in blade.get_network_access_policies().items:
1236 policy_name = policy.name
1237 policies_info[policy_name] = {
1238 "local": getattr(policy, "is_local", None),
1239 "enabled": getattr(policy, "enabled", None),
1240 "version": getattr(policy, "version", None),
1241 "rules": [],
1242 }
1244 for rule in getattr(policy, "rules", None) or []:
1245 policies_info[policy_name]["rules"].append(
1246 {
1247 "name": rule.name,
1248 "client": getattr(rule, "client", None),
1249 "effect": getattr(rule, "effect", None),
1250 "interfaces": getattr(rule, "interfaces", None),
1251 "index": getattr(rule, "index", None),
1252 }
1253 )
1255 return policies_info
1258def generate_worm_data_policies_dict(blade):
1259 policies_info = {}
1261 for policy in blade.get_worm_data_policies().items:
1262 policies_info[policy.name] = {
1263 "local": getattr(policy, "is_local", None),
1264 "enabled": getattr(policy, "enabled", None),
1265 "mode": getattr(policy, "mode", None),
1266 "retention_lock": getattr(policy, "retention_lock", None),
1267 "default_retention": getattr(policy, "default_retention", None),
1268 "min_retention": getattr(policy, "min_retention", None),
1269 "max_retention": getattr(policy, "max_retention", None),
1270 "members": [],
1271 }
1273 for member in blade.get_worm_data_policies_members().items:
1274 policy_name = getattr(getattr(member, "policy", None), "name", None)
1275 if policy_name in policies_info:
1276 policies_info[policy_name]["members"].append(
1277 getattr(getattr(member, "member", None), "name", None)
1278 )
1280 return policies_info
1283def generate_tls_policies_dict(blade):
1284 policies_info = {}
1286 for policy in blade.get_tls_policies().items:
1287 policies_info[policy.name] = {
1288 "local": getattr(policy, "is_local", None),
1289 "enabled": getattr(policy, "enabled", None),
1290 "appliance_certificate": getattr(
1291 getattr(policy, "appliance_certificate", None), "name", None
1292 ),
1293 "min_tls_version": getattr(policy, "min_tls_version", None),
1294 "enabled_tls_ciphers": getattr(policy, "enabled_tls_ciphers", None),
1295 "disabled_tls_ciphers": getattr(policy, "disabled_tls_ciphers", None),
1296 "client_certificates_required": getattr(
1297 policy, "client_certificates_required", None
1298 ),
1299 "verify_client_certificate_trust": getattr(
1300 policy, "verify_client_certificate_trust", None
1301 ),
1302 "trusted_client_certificate_authority": getattr(
1303 getattr(policy, "trusted_client_certificate_authority", None),
1304 "name",
1305 None,
1306 ),
1307 "network_interfaces": [],
1308 }
1310 for member in blade.get_tls_policies_network_interfaces().items:
1311 policy_name = getattr(getattr(member, "policy", None), "name", None)
1312 if policy_name in policies_info:
1313 policies_info[policy_name]["network_interfaces"].append(
1314 getattr(getattr(member, "member", None), "name", None)
1315 )
1317 return policies_info
1320def generate_s3_export_policies_dict(blade):
1321 policies_info = {}
1323 for policy in blade.get_s3_export_policies().items:
1324 policy_name = policy.name
1325 policies_info[policy_name] = {
1326 "local": getattr(policy, "is_local", None),
1327 "enabled": getattr(policy, "enabled", None),
1328 "rules": [],
1329 }
1331 for rule in getattr(policy, "rules", None) or []:
1332 policies_info[policy_name]["rules"].append(
1333 {
1334 "name": rule.name,
1335 "actions": getattr(rule, "actions", None),
1336 "effect": getattr(rule, "effect", None),
1337 "resources": getattr(rule, "resources", None),
1338 }
1339 )
1341 return policies_info
1344def generate_object_store_account_exports_dict(blade):
1345 exports_info = {}
1347 for export in blade.get_object_store_account_exports().items:
1348 exports_info[export.name] = {
1349 "enabled": getattr(export, "enabled", None),
1350 "account": getattr(getattr(export, "member", None), "name", None),
1351 "policy": getattr(getattr(export, "policy", None), "name", None),
1352 "server": getattr(getattr(export, "server", None), "name", None),
1353 }
1355 return exports_info
1358def generate_object_store_virtual_hosts_dict(blade):
1359 vhosts_info = {}
1361 for vhost in blade.get_object_store_virtual_hosts().items:
1362 attached = getattr(vhost, "attached_servers", None) or []
1363 if not isinstance(attached, list):
1364 attached = []
1365 vhosts_info[vhost.name] = {
1366 # Before REST 2.20 the name is the hostname
1367 "hostname": getattr(vhost, "hostname", None) or vhost.name,
1368 "attached_servers": [getattr(srv, "name", None) for srv in attached],
1369 }
1371 return vhosts_info
1374def generate_realms_dict(blade):
1375 realms_info = {}
1377 for realm in blade.get_realms().items:
1378 space = getattr(realm, "space", None)
1379 created = getattr(realm, "created", None)
1380 realms_info[realm.name] = {
1381 "created": (
1382 datetime.fromtimestamp(created / 1000).strftime("%Y-%m-%d %H:%M:%S")
1383 if created
1384 else None
1385 ),
1386 "destroyed": getattr(realm, "destroyed", None),
1387 "time_remaining": getattr(realm, "time_remaining", None),
1388 "default_inbound_tls_policy": getattr(
1389 getattr(realm, "default_inbound_tls_policy", None), "name", None
1390 ),
1391 "space": {
1392 "data_reduction": getattr(space, "data_reduction", None),
1393 "snapshots": getattr(space, "snapshots", None),
1394 "total_physical": getattr(space, "total_physical", None),
1395 "total_used": getattr(space, "total_used", None),
1396 "unique": getattr(space, "unique", None),
1397 "virtual": getattr(space, "virtual", None),
1398 "total_provisioned": getattr(space, "total_provisioned", None),
1399 "available_provisioned": getattr(space, "available_provisioned", None),
1400 "available_ratio": getattr(space, "available_ratio", None),
1401 "destroyed": getattr(space, "destroyed", None),
1402 "destroyed_virtual": getattr(space, "destroyed_virtual", None),
1403 },
1404 }
1406 return realms_info
1409def generate_resource_accesses_dict(blade):
1410 accesses_info = []
1412 for access in blade.get_resource_accesses().items:
1413 resource = getattr(access, "resource", None)
1414 scope = getattr(access, "scope", None)
1415 accesses_info.append(
1416 {
1417 "id": getattr(access, "id", None),
1418 "resource": getattr(resource, "name", None),
1419 "resource_type": getattr(resource, "resource_type", None),
1420 "scope": getattr(scope, "name", None),
1421 "scope_type": getattr(scope, "resource_type", None),
1422 }
1423 )
1425 return accesses_info
1428def generate_password_policies_dict(blade):
1429 policies_info = {}
1430 policies = list(blade.get_password_policies().items)
1431 for policy in policies:
1432 # Rules disabled on the array (set to 0) read back as absent and
1433 # are reported as null. Durations are reported in seconds, matching
1434 # the purefb_password_policy module and the admin settings values
1435 # in the default subset; the API stores milliseconds.
1436 lockout = getattr(policy, "lockout_duration", None)
1437 min_age = getattr(policy, "min_password_age", None)
1438 max_age = getattr(policy, "max_password_age", None)
1439 policies_info[policy.name] = {
1440 "enabled": getattr(policy, "enabled", None),
1441 "min_password_length": getattr(policy, "min_password_length", None),
1442 "max_login_attempts": getattr(policy, "max_login_attempts", None),
1443 "lockout_duration": (int(lockout / 1000) if lockout is not None else None),
1444 "password_history": getattr(policy, "password_history", None),
1445 "min_password_age": (int(min_age / 1000) if min_age is not None else None),
1446 "max_password_age": (int(max_age / 1000) if max_age is not None else None),
1447 "min_character_groups": getattr(policy, "min_character_groups", None),
1448 "min_characters_per_group": getattr(
1449 policy, "min_characters_per_group", None
1450 ),
1451 "enforce_username_check": getattr(policy, "enforce_username_check", None),
1452 "enforce_dictionary_check": getattr(
1453 policy, "enforce_dictionary_check", None
1454 ),
1455 }
1456 return policies_info
1459def generate_object_store_accounts_dict(blade):
1460 account_info = {}
1462 for account in blade.get_object_store_accounts().items:
1463 acc_name = account.name
1464 space = account.space
1466 account_info[acc_name] = {
1467 "object_count": account.object_count,
1468 "data_reduction": space.data_reduction,
1469 "snapshots_space": space.snapshots,
1470 "total_physical_space": space.total_physical,
1471 "unique_space": space.unique,
1472 "virtual_space": space.virtual,
1473 "total_provisioned_space": getattr(space, "total_provisioned", None),
1474 "available_provisioned_space": getattr(
1475 space, "available_provisioned", None
1476 ),
1477 "available_ratio": getattr(space, "available_ratio", None),
1478 "destroyed_space": getattr(space, "destroyed", None),
1479 "destroyed_virtual_space": getattr(space, "destroyed_virtual", None),
1480 "quota_limit": getattr(account, "quota_limit", None),
1481 "hard_limit_enabled": getattr(account, "hard_limit_enabled", None),
1482 "total_provisioned": getattr(space, "total_provisioned", None),
1483 "users": {},
1484 "bucket_defaults": {
1485 "hard_limit_enabled": getattr(
1486 getattr(account, "bucket_defaults", None),
1487 "hard_limit_enabled",
1488 None,
1489 ),
1490 "quota_limit": getattr(
1491 getattr(account, "bucket_defaults", None),
1492 "quota_limit",
1493 None,
1494 ),
1495 },
1496 "public_access_config": {
1497 "block_new_public_policies": getattr(
1498 getattr(account, "public_access_config", None),
1499 "block_new_public_policies",
1500 None,
1501 ),
1502 "block_public_access": getattr(
1503 getattr(account, "public_access_config", None),
1504 "block_public_access",
1505 None,
1506 ),
1507 },
1508 }
1510 # Users for this account
1511 acc_users = blade.get_object_store_users(filter=f'name="{acc_name}/*"').items
1513 for acc_user in acc_users:
1514 user_name = acc_user.name.split("/")[1]
1515 account_info[acc_name]["users"][user_name] = {
1516 "keys": [],
1517 "policies": [],
1518 }
1520 # Access Keys
1521 keys_res = blade.get_object_store_access_keys(
1522 filter=f'user.name="{acc_user.name}"'
1523 )
1524 if keys_res.total_item_count != 0:
1525 for key in keys_res.items:
1526 account_info[acc_name]["users"][user_name]["keys"].append(
1527 {
1528 "name": key.name,
1529 "enabled": bool(key.enabled),
1530 }
1531 )
1533 # Policies
1534 policies_res = blade.get_object_store_access_policies_object_store_users(
1535 member_names=[acc_user.name]
1536 )
1537 if policies_res.total_item_count != 0:
1538 for policy in policies_res.items:
1539 account_info[acc_name]["users"][user_name]["policies"].append(
1540 policy.policy.name
1541 )
1543 return account_info
1546def generate_fs_dict(blade, exports=None):
1547 fs_info = {}
1548 by_fs = {}
1549 for e in exports or []:
1550 by_fs.setdefault(e.get("filesystem"), []).append(e)
1551 for fsystem in blade.get_file_systems().items:
1552 share = fsystem.name
1554 nfs = getattr(fsystem, "nfs", None)
1555 smb = getattr(fsystem, "smb", None)
1556 multi = getattr(fsystem, "multi_protocol", None)
1557 source = getattr(fsystem, "source", None)
1558 location = getattr(fsystem, "location", None)
1560 fs_info[share] = {
1561 "fast_remove": fsystem.fast_remove_directory_enabled,
1562 "snapshot_enabled": fsystem.snapshot_directory_enabled,
1563 "provisioned": fsystem.provisioned,
1564 "destroyed": fsystem.destroyed,
1565 "nfs_rules": getattr(nfs, "rules", None),
1566 "nfs_v3": getattr(nfs, "v3_enabled", False),
1567 "nfs_v4_1": getattr(nfs, "v4_1_enabled", False),
1568 "user_quotas": {},
1569 "group_quotas": {},
1570 "http": fsystem.http.enabled,
1571 "smb_mode": getattr(smb, "acl_mode", None),
1572 "multi_protocol": {
1573 # safegaurd_acls is kept for backwards compatibility
1574 "safegaurd_acls": getattr(multi, "safeguard_acls", None),
1575 "safeguard_acls": getattr(multi, "safeguard_acls", None),
1576 "access_control_style": getattr(multi, "access_control_style", None),
1577 },
1578 "hard_limit": fsystem.hard_limit_enabled,
1579 "promotion_status": fsystem.promotion_status,
1580 "requested_promotion_state": fsystem.requested_promotion_state,
1581 "writable": fsystem.writable,
1582 "source": {
1583 "is_local": getattr(source, "is_local", None),
1584 "name": getattr(source, "name", None),
1585 "location": getattr(location, "name", None),
1586 },
1587 "default_group_quota": fsystem.default_group_quota,
1588 "default_user_quota": fsystem.default_user_quota,
1589 "export_policy": getattr(getattr(nfs, "export_policy", None), "name", None),
1590 "smb_client_policy": getattr(
1591 getattr(smb, "client_policy", None), "name", None
1592 ),
1593 "smb_share_policy": getattr(
1594 getattr(smb, "share_policy", None), "name", None
1595 ),
1596 "smb_continuous_availability_enabled": getattr(
1597 smb, "continuous_availability_enabled", False
1598 ),
1599 "multi_protocol_access_control_style": getattr(
1600 multi, "access_control_style", None
1601 ),
1602 "multi_protocol_safeguard_acls": getattr(multi, "safeguard_acls", None),
1603 }
1604 if exports is not None:
1605 fs_info[share]["file_system_exports"] = by_fs.get(share, [])
1607 # Group quotas
1608 for group_quota in blade.get_quotas_groups(file_system_names=[share]).items:
1609 group_name = group_quota.name.rsplit("/", 1)[1]
1610 fs_info[share]["group_quotas"][group_name] = {
1611 "group_id": getattr(group_quota.group, "id", None),
1612 "group_name": getattr(group_quota.group, "name", None),
1613 "quota": group_quota.quota,
1614 "usage": group_quota.usage,
1615 }
1617 # User quotas
1618 for user_quota in blade.get_quotas_users(file_system_names=[share]).items:
1619 user_name = user_quota.name.rsplit("/", 1)[1]
1620 fs_info[share]["user_quotas"][user_name] = {
1621 "user_id": getattr(user_quota.user, "id", None),
1622 "user_name": getattr(user_quota.user, "name", None),
1623 "quota": user_quota.quota,
1624 "usage": user_quota.usage,
1625 }
1627 return fs_info
1630def generate_drives_dict(blade):
1631 """
1632 Drives information is only available for the Legend chassis.
1633 The Legend chassis product_name has // in it so only bother if
1634 that is the case.
1635 """
1636 drives_info = {}
1637 arrays = list(blade.get_arrays().items)
1638 if not arrays:
1639 return drives_info
1641 product_type = getattr(arrays[0], "product_type", "")
1643 if "//" not in product_type:
1644 return drives_info
1646 for drive in blade.get_drives().items:
1647 drives_info[drive.name] = {
1648 "progress": getattr(drive, "progress", None),
1649 "raw_capacity": getattr(drive, "raw_capacity", None),
1650 "status": getattr(drive, "status", None),
1651 "details": getattr(drive, "details", None),
1652 "type": getattr(drive, "type", None),
1653 }
1654 return drives_info
1657def generate_servers_dict(blade):
1658 servers_info = {}
1659 for server in blade.get_servers().items:
1660 servers_info[server.name] = {
1661 "created": datetime.fromtimestamp(server.created / 1000).strftime(
1662 "%Y-%m-%d %H:%M:%S"
1663 ),
1664 "dns": [dns.name for dns in server.dns],
1665 "directory_services": [ds.name for ds in server.directory_services],
1666 }
1667 return servers_info
1670def generate_file_system_exports_dict(blade):
1671 """Return the list of File System Exports for the whole array.
1673 Requires REST API 2.16+ (SERVERS_API_VERSION). Callers must gate on
1674 the API version before invoking; this helper does not check.
1675 """
1676 exports = []
1677 for exp in blade.get_file_system_exports().items:
1678 member = getattr(exp, "member", None)
1679 server = getattr(exp, "server", None)
1680 policy = getattr(exp, "policy", None)
1681 share_policy = getattr(exp, "share_policy", None)
1682 ptype = getattr(exp, "policy_type", None)
1683 exports.append(
1684 {
1685 "export_name": getattr(exp, "export_name", None),
1686 "type": ptype,
1687 "filesystem": getattr(member, "name", None),
1688 "server": getattr(server, "name", None),
1689 "export_policy": (
1690 getattr(policy, "name", None) if ptype == "NFS" else None
1691 ),
1692 "client_policy": (
1693 getattr(policy, "name", None) if ptype == "SMB" else None
1694 ),
1695 "share_policy": (
1696 getattr(share_policy, "name", None) if ptype == "SMB" else None
1697 ),
1698 }
1699 )
1700 return exports
1703def generate_fleet_dict(blade):
1704 fleet_items = list(blade.get_fleets().items)
1705 if not fleet_items:
1706 return {}
1708 fleet_name = fleet_items[0].name
1709 members = blade.get_fleets_members().items
1711 fleet_info = {
1712 fleet_name: {
1713 "members": {
1714 member.member.name: {
1715 "status": member.status,
1716 "status_details": member.status_details,
1717 }
1718 for member in members
1719 }
1720 }
1721 }
1722 return fleet_info
1725def main():
1726 argument_spec = purefb_argument_spec()
1727 argument_spec.update(
1728 dict(gather_subset=dict(default="minimum", type="list", elements="str"))
1729 )
1731 module = AnsibleModule(argument_spec, supports_check_mode=True)
1733 blade = get_system(module)
1734 api_version = get_rest_api_version(blade)
1736 if not module.params["gather_subset"]:
1737 module.params["gather_subset"] = ["minimum"]
1738 subset = [test.lower() for test in module.params["gather_subset"]]
1739 valid_subsets = (
1740 "all",
1741 "minimum",
1742 "config",
1743 "performance",
1744 "capacity",
1745 "network",
1746 "subnets",
1747 "lags",
1748 "filesystems",
1749 "snapshots",
1750 "buckets",
1751 "arrays",
1752 "replication",
1753 "policies",
1754 "accounts",
1755 "admins",
1756 "ad",
1757 "kerberos",
1758 "drives",
1759 "servers",
1760 "fleet",
1761 "realms",
1762 )
1763 subset_test = (test in valid_subsets for test in subset)
1764 if not all(subset_test):
1765 module.fail_json(
1766 msg="value must gather_subset must be one or more of: %s, got: %s"
1767 % (",".join(valid_subsets), ",".join(subset))
1768 )
1770 info = {}
1772 if "minimum" in subset or "all" in subset:
1773 info["default"] = generate_default_dict(blade)
1774 if "performance" in subset or "all" in subset:
1775 info["performance"] = generate_perf_dict(blade)
1776 if "config" in subset or "all" in subset:
1777 info["config"] = generate_config_dict(blade)
1778 if "capacity" in subset or "all" in subset:
1779 info["capacity"] = generate_capacity_dict(blade)
1780 if "lags" in subset or "all" in subset:
1781 info["lag"] = generate_lag_dict(blade)
1782 if "network" in subset or "all" in subset:
1783 info["network"] = generate_network_dict(blade)
1784 if "subnets" in subset or "all" in subset:
1785 info["subnet"] = generate_subnet_dict(blade)
1786 if "filesystems" in subset or "all" in subset:
1787 exports_list = None
1788 if LooseVersion(SERVERS_API_VERSION) <= LooseVersion(api_version):
1789 exports_list = generate_file_system_exports_dict(blade)
1790 info["file_system_exports"] = exports_list
1791 info["filesystems"] = generate_fs_dict(blade, exports_list)
1792 if "admins" in subset or "all" in subset:
1793 info["admins"] = generate_admin_dict(blade)
1794 if "snapshots" in subset or "all" in subset:
1795 info["snapshots"] = generate_snap_dict(blade)
1796 if "buckets" in subset or "all" in subset:
1797 info["buckets"] = generate_bucket_dict(blade)
1798 info["object_store_virtual_hosts"] = generate_object_store_virtual_hosts_dict(
1799 blade
1800 )
1801 if "policies" in subset or "all" in subset:
1802 info["policies"] = generate_all_policies_dict(blade)
1803 info["snapshot_policies"] = generate_policies_dict(blade)
1804 if "arrays" in subset or "all" in subset:
1805 info["arrays"] = generate_array_conn_dict(blade)
1806 if "replication" in subset or "all" in subset:
1807 info["file_replication"] = generate_file_repl_dict(blade)
1808 info["bucket_replication"] = generate_bucket_repl_dict(blade)
1809 info["snap_transfers"] = generate_snap_transfer_dict(blade)
1810 info["remote_credentials"] = generate_remote_creds_dict(blade)
1811 info["targets"] = generate_targets_dict(blade)
1812 if "accounts" in subset or "all" in subset:
1813 info["accounts"] = generate_object_store_accounts_dict(blade)
1814 if LooseVersion(S3_EXPORT_API_VERSION) <= LooseVersion(api_version):
1815 info["object_store_account_exports"] = (
1816 generate_object_store_account_exports_dict(blade)
1817 )
1818 if "ad" in subset or "all" in subset:
1819 info["active_directory"] = generate_ad_dict(blade)
1820 if "kerberos" in subset or "all" in subset:
1821 info["kerberos"] = generate_kerb_dict(blade)
1822 if "policies" in subset or "all" in subset:
1823 info["access_policies"] = generate_object_store_access_policies_dict(blade)
1824 if LooseVersion(PUBLIC_API_VERSION) <= LooseVersion(api_version):
1825 info["bucket_access_policies"] = generate_bucket_access_policies_dict(blade)
1826 info["bucket_cross_origin_policies"] = (
1827 generate_bucket_cross_object_policies_dict(blade)
1828 )
1829 info["export_policies"] = generate_nfs_export_policies_dict(blade)
1830 if LooseVersion(SMB_CLIENT_API_VERSION) <= LooseVersion(api_version):
1831 # share_policies has always held the SMB *client* policies; it is
1832 # kept for backwards compatibility alongside the correct key.
1833 info["smb_client_policies"] = generate_smb_client_policies_dict(blade)
1834 info["share_policies"] = info["smb_client_policies"]
1835 info["smb_share_policies"] = generate_smb_share_policies_dict(blade)
1836 if LooseVersion(NAP_API_VERSION) <= LooseVersion(api_version):
1837 info["network_access_policies"] = generate_network_access_policies_dict(
1838 blade
1839 )
1840 if LooseVersion(WORM_API_VERSION) <= LooseVersion(api_version):
1841 info["worm_data_policies"] = generate_worm_data_policies_dict(blade)
1842 if LooseVersion(PASSWORD_POLICY_API_VERSION) <= LooseVersion(api_version):
1843 info["password_policies"] = generate_password_policies_dict(blade)
1844 if LooseVersion(FLEET_API_VERSION) <= LooseVersion(api_version):
1845 info["fleet"] = generate_fleet_dict(blade)
1846 if LooseVersion(TLS_API_VERSION) <= LooseVersion(api_version):
1847 info["tls_policies"] = generate_tls_policies_dict(blade)
1848 if LooseVersion(S3_EXPORT_API_VERSION) <= LooseVersion(api_version):
1849 info["s3_export_policies"] = generate_s3_export_policies_dict(blade)
1850 if ("realms" in subset or "all" in subset) and LooseVersion(
1851 REALMS_API_VERSION
1852 ) <= LooseVersion(api_version):
1853 info["realms"] = generate_realms_dict(blade)
1854 info["resource_accesses"] = generate_resource_accesses_dict(blade)
1855 if ("drives" in subset or "all" in subset) and LooseVersion(
1856 DRIVES_API_VERSION
1857 ) <= LooseVersion(api_version):
1858 info["drives"] = generate_drives_dict(blade)
1859 # `fleet` is a documented subset of its own; until now the data was only
1860 # produced as part of `policies`, so gather_subset=fleet returned nothing.
1861 if ("fleet" in subset or "all" in subset) and LooseVersion(
1862 FLEET_API_VERSION
1863 ) <= LooseVersion(api_version):
1864 info["fleet"] = generate_fleet_dict(blade)
1865 if ("servers" in subset or "all" in subset) and LooseVersion(
1866 SERVERS_API_VERSION
1867 ) <= LooseVersion(api_version):
1868 info["servers"] = generate_servers_dict(blade)
1869 module.exit_json(changed=False, purefb_info=info)
1872if __name__ == "__main__":
1873 main()