osi-dump 0.1.3.3.4.3__py3-none-any.whl → 0.1.4__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.
- osi_dump/__main__.py +4 -4
- osi_dump/api/keystone.py +70 -70
- osi_dump/api/neutron.py +65 -65
- osi_dump/api/octavia.py +94 -94
- osi_dump/api/placement.py +41 -41
- osi_dump/batch_handler/__init__.py +6 -5
- osi_dump/batch_handler/external_port_batch_handler.py +55 -55
- osi_dump/batch_handler/flavor_batch_handler.py +51 -51
- osi_dump/batch_handler/floating_ip_batch_handler.py +57 -57
- osi_dump/batch_handler/hypervisor_batch_handler.py +55 -55
- osi_dump/batch_handler/image_batch_handler.py +51 -51
- osi_dump/batch_handler/instance_batch_handler.py +46 -54
- osi_dump/batch_handler/load_balancer_batch_handler.py +59 -59
- osi_dump/batch_handler/network_batch_handler.py +51 -51
- osi_dump/batch_handler/project_batch_handler.py +51 -51
- osi_dump/batch_handler/role_assignment_batch_handler.py +38 -61
- osi_dump/batch_handler/router_batch_handler.py +49 -49
- osi_dump/batch_handler/security_group_batch_handler.py +37 -0
- osi_dump/batch_handler/volume_batch_handler.py +45 -45
- osi_dump/cli.py +267 -257
- osi_dump/exporter/external_port/excel_external_port_exporter.py +34 -34
- osi_dump/exporter/external_port/external_port_exporter.py +7 -7
- osi_dump/exporter/flavor/excel_flavor_exporter.py +30 -30
- osi_dump/exporter/flavor/flavor_exporter.py +7 -7
- osi_dump/exporter/floating_ip/excel_floating_ip_exporter.py +30 -30
- osi_dump/exporter/floating_ip/floating_ip_exporter.py +7 -7
- osi_dump/exporter/hypervisor/excel_hypervisor_exporter.py +32 -32
- osi_dump/exporter/hypervisor/hypervisor_exporter.py +7 -7
- osi_dump/exporter/image/excel_image_exporter.py +28 -28
- osi_dump/exporter/image/image_exporter.py +7 -7
- osi_dump/exporter/instance/excel_instance_exporter.py +29 -29
- osi_dump/exporter/instance/instance_exporter.py +8 -7
- osi_dump/exporter/load_balancer/excel_load_balancer_exporter.py +36 -36
- osi_dump/exporter/load_balancer/load_balancer_exporter.py +7 -7
- osi_dump/exporter/network/excel_network_exporter.py +32 -32
- osi_dump/exporter/network/network_exporter.py +7 -7
- osi_dump/exporter/project/excel_project_exporter.py +30 -30
- osi_dump/exporter/project/project_exporter.py +7 -7
- osi_dump/exporter/role_assignment/excel_role_assignment_exporter.py +43 -35
- osi_dump/exporter/role_assignment/role_assignment_exporter.py +7 -7
- osi_dump/exporter/router/excel_router_exporter.py +30 -30
- osi_dump/exporter/router/router_exporter.py +7 -7
- osi_dump/exporter/security_group/excel_security_group_exporter.py +35 -0
- osi_dump/exporter/security_group/security_group_exporter.py +8 -0
- osi_dump/exporter/volume/excel_volume_exporter.py +29 -29
- osi_dump/exporter/volume/volume_exporter.py +7 -7
- osi_dump/importer/external_port/external_port_importer.py +9 -9
- osi_dump/importer/external_port/openstack_external_port_importer.py +177 -177
- osi_dump/importer/flavor/flavor_importer.py +9 -9
- osi_dump/importer/flavor/openstack_flavor_importer.py +64 -64
- osi_dump/importer/floating_ip/floating_ip_importer.py +9 -9
- osi_dump/importer/floating_ip/openstack_floating_ip_importer.py +68 -68
- osi_dump/importer/hypervisor/hypervisor_importer.py +9 -9
- osi_dump/importer/hypervisor/openstack_hypervisor_importer.py +137 -137
- osi_dump/importer/image/image_importer.py +9 -9
- osi_dump/importer/image/openstack_image_importer.py +81 -81
- osi_dump/importer/instance/instance_importer.py +8 -9
- osi_dump/importer/instance/openstack_instance_importer.py +136 -136
- osi_dump/importer/load_balancer/load_balancer_importer.py +9 -9
- osi_dump/importer/load_balancer/openstack_load_balancer_importer.py +113 -113
- osi_dump/importer/network/network_importer.py +9 -9
- osi_dump/importer/network/openstack_network_importer.py +93 -93
- osi_dump/importer/project/openstack_project_importer.py +141 -141
- osi_dump/importer/project/project_importer.py +9 -9
- osi_dump/importer/role_assignment/openstack_role_assignment_importer.py +117 -147
- osi_dump/importer/role_assignment/role_assignment_importer.py +16 -9
- osi_dump/importer/router/openstack_router_importer.py +87 -87
- osi_dump/importer/router/router_importer.py +9 -9
- osi_dump/importer/security_group/openstack_security_group_importer.py +54 -0
- osi_dump/importer/security_group/security_group_importer.py +8 -0
- osi_dump/importer/volume/openstack_volume_importer.py +81 -81
- osi_dump/importer/volume/volume_importer.py +9 -9
- osi_dump/model/authentication_info.py +15 -15
- osi_dump/model/external_port.py +30 -30
- osi_dump/model/flavor.py +20 -20
- osi_dump/model/floating_ip.py +24 -24
- osi_dump/model/hypervisor.py +24 -24
- osi_dump/model/image.py +27 -27
- osi_dump/model/instance.py +29 -29
- osi_dump/model/load_balancer.py +26 -26
- osi_dump/model/network.py +25 -25
- osi_dump/model/project.py +33 -33
- osi_dump/model/role_assignment.py +30 -23
- osi_dump/model/router.py +23 -23
- osi_dump/model/security_group.py +22 -0
- osi_dump/model/volume.py +25 -25
- osi_dump/os_connection/get_connections.py +67 -67
- osi_dump/util/__init__.py +7 -7
- osi_dump/util/create_file.py +11 -11
- osi_dump/util/excel_autosize_column.py +39 -39
- osi_dump/util/excel_sort_sheet.py +35 -35
- osi_dump/util/export_data_excel.py +36 -36
- osi_dump/util/extract_hostname.py +5 -5
- osi_dump/util/openstack_util.py +48 -48
- osi_dump/util/panda_excel.py +26 -26
- osi_dump/util/validate_dir_path.py +20 -20
- {osi_dump-0.1.3.3.4.3.dist-info → osi_dump-0.1.4.dist-info}/METADATA +40 -40
- osi_dump-0.1.4.dist-info/RECORD +130 -0
- osi_dump-0.1.3.3.4.3.dist-info/RECORD +0 -124
- {osi_dump-0.1.3.3.4.3.dist-info → osi_dump-0.1.4.dist-info}/WHEEL +0 -0
- {osi_dump-0.1.3.3.4.3.dist-info → osi_dump-0.1.4.dist-info}/entry_points.txt +0 -0
- {osi_dump-0.1.3.3.4.3.dist-info → osi_dump-0.1.4.dist-info}/top_level.txt +0 -0
@@ -1,141 +1,141 @@
|
|
1
|
-
import logging
|
2
|
-
|
3
|
-
import concurrent
|
4
|
-
|
5
|
-
from openstack.connection import Connection
|
6
|
-
from openstack.identity.v3.project import Project as OSProject
|
7
|
-
from openstack.load_balancer.v2.load_balancer import LoadBalancer as OSLoadBalancer
|
8
|
-
|
9
|
-
from osi_dump.importer.project.project_importer import ProjectImporter
|
10
|
-
from osi_dump.model.project import Project
|
11
|
-
import osi_dump.api.octavia as octavia_api
|
12
|
-
logger = logging.getLogger(__name__)
|
13
|
-
|
14
|
-
|
15
|
-
class OpenStackProjectImporter(ProjectImporter):
|
16
|
-
def __init__(self, connection: Connection):
|
17
|
-
self.connection = connection
|
18
|
-
|
19
|
-
def import_projects(self) -> list[Project]:
|
20
|
-
"""Import projects information from Openstack
|
21
|
-
|
22
|
-
Raises:
|
23
|
-
Exception: Raises exception if fetching project failed
|
24
|
-
|
25
|
-
Returns:
|
26
|
-
list[Instance]: _description_
|
27
|
-
"""
|
28
|
-
|
29
|
-
logger.info(f"Importing projects for {self.connection.auth['auth_url']}")
|
30
|
-
try:
|
31
|
-
osload_balancers: list[OSLoadBalancer] = octavia_api.get_load_balancers(
|
32
|
-
connection=self.connection
|
33
|
-
)
|
34
|
-
except Exception as e:
|
35
|
-
raise Exception(
|
36
|
-
f"Can not fetch load_balancers for {self.connection.auth['auth_url']} {e}"
|
37
|
-
) from e
|
38
|
-
|
39
|
-
project_lb_dict = {}
|
40
|
-
|
41
|
-
for osload_balancer in osload_balancers:
|
42
|
-
if project_lb_dict.get(osload_balancer["project_id"]):
|
43
|
-
project_lb_dict[osload_balancer["project_id"]] += 1
|
44
|
-
else:
|
45
|
-
project_lb_dict[osload_balancer["project_id"]] = 1
|
46
|
-
|
47
|
-
try:
|
48
|
-
osprojects: list[OSProject] = list(self.connection.identity.projects())
|
49
|
-
except Exception as e:
|
50
|
-
raise Exception(
|
51
|
-
f"Can not fetch projects for {self.connection.auth['auth_url']}"
|
52
|
-
) from e
|
53
|
-
|
54
|
-
projects: list[Project] = []
|
55
|
-
|
56
|
-
with concurrent.futures.ThreadPoolExecutor() as executor:
|
57
|
-
futures = [
|
58
|
-
executor.submit(self._get_project_info, project, project_lb_dict)
|
59
|
-
for project in osprojects
|
60
|
-
]
|
61
|
-
for future in concurrent.futures.as_completed(futures):
|
62
|
-
projects.append(future.result())
|
63
|
-
|
64
|
-
logger.info(f"Imported projects for {self.connection.auth['auth_url']}")
|
65
|
-
|
66
|
-
return projects
|
67
|
-
|
68
|
-
def _get_project_info(self, project: OSProject, project_lb_dict: dict) -> Project:
|
69
|
-
|
70
|
-
usage_instance=None
|
71
|
-
quota_instance=None
|
72
|
-
usage_ram=None
|
73
|
-
quota_ram=None
|
74
|
-
usage_vcpu=None
|
75
|
-
quota_vcpu=None
|
76
|
-
|
77
|
-
try:
|
78
|
-
compute_quotas = self.connection.compute.get_quota_set(
|
79
|
-
project.id, usage=True
|
80
|
-
)
|
81
|
-
|
82
|
-
usage_instance=compute_quotas.usage["instances"]
|
83
|
-
quota_instance=compute_quotas.instances
|
84
|
-
usage_ram=compute_quotas.usage["ram"]
|
85
|
-
quota_ram=compute_quotas.ram
|
86
|
-
usage_vcpu=compute_quotas.usage["cores"]
|
87
|
-
quota_vcpu=compute_quotas.cores
|
88
|
-
except Exception as e:
|
89
|
-
logger.warning(f"Get compute quotas failed for {project.id} error: {e}")
|
90
|
-
|
91
|
-
usage_volume=None
|
92
|
-
quota_volume=None
|
93
|
-
usage_snapshot=None
|
94
|
-
quota_snapshot=None
|
95
|
-
usage_storage=None
|
96
|
-
quota_storage=None
|
97
|
-
try:
|
98
|
-
storage_quotas = self.connection.block_storage.get_quota_set(
|
99
|
-
project.id, usage=True
|
100
|
-
)
|
101
|
-
usage_volume=storage_quotas.usage["volumes"]
|
102
|
-
quota_volume=storage_quotas.volumes
|
103
|
-
usage_snapshot=storage_quotas.usage["snapshots"]
|
104
|
-
quota_snapshot=storage_quotas.snapshots
|
105
|
-
usage_storage=storage_quotas.usage["gigabytes"]
|
106
|
-
quota_storage=storage_quotas.gigabytes
|
107
|
-
except Exception as e:
|
108
|
-
logger.warning(f"Get storage quotas failed for {project.id} error: {e}")
|
109
|
-
|
110
|
-
domain_name = None
|
111
|
-
try:
|
112
|
-
domain = self.connection.identity.get_domain(project.domain_id)
|
113
|
-
domain_name = domain.name
|
114
|
-
except Exception as e:
|
115
|
-
logger.warning(f"Get domain failed for {project.domain_id} error: {e}")
|
116
|
-
|
117
|
-
lb_count = project_lb_dict.get(project.id)
|
118
|
-
|
119
|
-
project_ret = Project(
|
120
|
-
project_id=project.id,
|
121
|
-
project_name=project.name,
|
122
|
-
domain_id=project.domain_id,
|
123
|
-
domain_name=domain_name,
|
124
|
-
enabled=project.is_enabled,
|
125
|
-
parent_id=project.parent_id,
|
126
|
-
usage_instance=usage_instance,
|
127
|
-
quota_instance=quota_instance,
|
128
|
-
usage_ram=usage_ram,
|
129
|
-
quota_ram=quota_ram,
|
130
|
-
usage_vcpu=usage_vcpu,
|
131
|
-
quota_vcpu=quota_vcpu,
|
132
|
-
usage_volume=usage_volume,
|
133
|
-
quota_volume=quota_volume,
|
134
|
-
usage_snapshot=usage_snapshot,
|
135
|
-
quota_snapshot=quota_snapshot,
|
136
|
-
usage_storage=usage_storage,
|
137
|
-
quota_storage=quota_storage,
|
138
|
-
load_balancer_count=lb_count
|
139
|
-
)
|
140
|
-
|
141
|
-
return project_ret
|
1
|
+
import logging
|
2
|
+
|
3
|
+
import concurrent
|
4
|
+
|
5
|
+
from openstack.connection import Connection
|
6
|
+
from openstack.identity.v3.project import Project as OSProject
|
7
|
+
from openstack.load_balancer.v2.load_balancer import LoadBalancer as OSLoadBalancer
|
8
|
+
|
9
|
+
from osi_dump.importer.project.project_importer import ProjectImporter
|
10
|
+
from osi_dump.model.project import Project
|
11
|
+
import osi_dump.api.octavia as octavia_api
|
12
|
+
logger = logging.getLogger(__name__)
|
13
|
+
|
14
|
+
|
15
|
+
class OpenStackProjectImporter(ProjectImporter):
|
16
|
+
def __init__(self, connection: Connection):
|
17
|
+
self.connection = connection
|
18
|
+
|
19
|
+
def import_projects(self) -> list[Project]:
|
20
|
+
"""Import projects information from Openstack
|
21
|
+
|
22
|
+
Raises:
|
23
|
+
Exception: Raises exception if fetching project failed
|
24
|
+
|
25
|
+
Returns:
|
26
|
+
list[Instance]: _description_
|
27
|
+
"""
|
28
|
+
|
29
|
+
logger.info(f"Importing projects for {self.connection.auth['auth_url']}")
|
30
|
+
try:
|
31
|
+
osload_balancers: list[OSLoadBalancer] = octavia_api.get_load_balancers(
|
32
|
+
connection=self.connection
|
33
|
+
)
|
34
|
+
except Exception as e:
|
35
|
+
raise Exception(
|
36
|
+
f"Can not fetch load_balancers for {self.connection.auth['auth_url']} {e}"
|
37
|
+
) from e
|
38
|
+
|
39
|
+
project_lb_dict = {}
|
40
|
+
|
41
|
+
for osload_balancer in osload_balancers:
|
42
|
+
if project_lb_dict.get(osload_balancer["project_id"]):
|
43
|
+
project_lb_dict[osload_balancer["project_id"]] += 1
|
44
|
+
else:
|
45
|
+
project_lb_dict[osload_balancer["project_id"]] = 1
|
46
|
+
|
47
|
+
try:
|
48
|
+
osprojects: list[OSProject] = list(self.connection.identity.projects())
|
49
|
+
except Exception as e:
|
50
|
+
raise Exception(
|
51
|
+
f"Can not fetch projects for {self.connection.auth['auth_url']}"
|
52
|
+
) from e
|
53
|
+
|
54
|
+
projects: list[Project] = []
|
55
|
+
|
56
|
+
with concurrent.futures.ThreadPoolExecutor() as executor:
|
57
|
+
futures = [
|
58
|
+
executor.submit(self._get_project_info, project, project_lb_dict)
|
59
|
+
for project in osprojects
|
60
|
+
]
|
61
|
+
for future in concurrent.futures.as_completed(futures):
|
62
|
+
projects.append(future.result())
|
63
|
+
|
64
|
+
logger.info(f"Imported projects for {self.connection.auth['auth_url']}")
|
65
|
+
|
66
|
+
return projects
|
67
|
+
|
68
|
+
def _get_project_info(self, project: OSProject, project_lb_dict: dict) -> Project:
|
69
|
+
|
70
|
+
usage_instance=None
|
71
|
+
quota_instance=None
|
72
|
+
usage_ram=None
|
73
|
+
quota_ram=None
|
74
|
+
usage_vcpu=None
|
75
|
+
quota_vcpu=None
|
76
|
+
|
77
|
+
try:
|
78
|
+
compute_quotas = self.connection.compute.get_quota_set(
|
79
|
+
project.id, usage=True
|
80
|
+
)
|
81
|
+
|
82
|
+
usage_instance=compute_quotas.usage["instances"]
|
83
|
+
quota_instance=compute_quotas.instances
|
84
|
+
usage_ram=compute_quotas.usage["ram"]
|
85
|
+
quota_ram=compute_quotas.ram
|
86
|
+
usage_vcpu=compute_quotas.usage["cores"]
|
87
|
+
quota_vcpu=compute_quotas.cores
|
88
|
+
except Exception as e:
|
89
|
+
logger.warning(f"Get compute quotas failed for {project.id} error: {e}")
|
90
|
+
|
91
|
+
usage_volume=None
|
92
|
+
quota_volume=None
|
93
|
+
usage_snapshot=None
|
94
|
+
quota_snapshot=None
|
95
|
+
usage_storage=None
|
96
|
+
quota_storage=None
|
97
|
+
try:
|
98
|
+
storage_quotas = self.connection.block_storage.get_quota_set(
|
99
|
+
project.id, usage=True
|
100
|
+
)
|
101
|
+
usage_volume=storage_quotas.usage["volumes"]
|
102
|
+
quota_volume=storage_quotas.volumes
|
103
|
+
usage_snapshot=storage_quotas.usage["snapshots"]
|
104
|
+
quota_snapshot=storage_quotas.snapshots
|
105
|
+
usage_storage=storage_quotas.usage["gigabytes"]
|
106
|
+
quota_storage=storage_quotas.gigabytes
|
107
|
+
except Exception as e:
|
108
|
+
logger.warning(f"Get storage quotas failed for {project.id} error: {e}")
|
109
|
+
|
110
|
+
domain_name = None
|
111
|
+
try:
|
112
|
+
domain = self.connection.identity.get_domain(project.domain_id)
|
113
|
+
domain_name = domain.name
|
114
|
+
except Exception as e:
|
115
|
+
logger.warning(f"Get domain failed for {project.domain_id} error: {e}")
|
116
|
+
|
117
|
+
lb_count = project_lb_dict.get(project.id)
|
118
|
+
|
119
|
+
project_ret = Project(
|
120
|
+
project_id=project.id,
|
121
|
+
project_name=project.name,
|
122
|
+
domain_id=project.domain_id,
|
123
|
+
domain_name=domain_name,
|
124
|
+
enabled=project.is_enabled,
|
125
|
+
parent_id=project.parent_id,
|
126
|
+
usage_instance=usage_instance,
|
127
|
+
quota_instance=quota_instance,
|
128
|
+
usage_ram=usage_ram,
|
129
|
+
quota_ram=quota_ram,
|
130
|
+
usage_vcpu=usage_vcpu,
|
131
|
+
quota_vcpu=quota_vcpu,
|
132
|
+
usage_volume=usage_volume,
|
133
|
+
quota_volume=quota_volume,
|
134
|
+
usage_snapshot=usage_snapshot,
|
135
|
+
quota_snapshot=quota_snapshot,
|
136
|
+
usage_storage=usage_storage,
|
137
|
+
quota_storage=quota_storage,
|
138
|
+
load_balancer_count=lb_count
|
139
|
+
)
|
140
|
+
|
141
|
+
return project_ret
|
@@ -1,9 +1,9 @@
|
|
1
|
-
from abc import ABC, abstractmethod
|
2
|
-
|
3
|
-
from osi_dump.model.project import Project
|
4
|
-
|
5
|
-
|
6
|
-
class ProjectImporter(ABC):
|
7
|
-
@abstractmethod
|
8
|
-
def import_projects(self) -> list[Project]:
|
9
|
-
pass
|
1
|
+
from abc import ABC, abstractmethod
|
2
|
+
|
3
|
+
from osi_dump.model.project import Project
|
4
|
+
|
5
|
+
|
6
|
+
class ProjectImporter(ABC):
|
7
|
+
@abstractmethod
|
8
|
+
def import_projects(self) -> list[Project]:
|
9
|
+
pass
|
@@ -1,147 +1,117 @@
|
|
1
|
-
import logging
|
2
|
-
|
3
|
-
import
|
4
|
-
|
5
|
-
|
6
|
-
from
|
7
|
-
from
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
)
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
def
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
try:
|
54
|
-
self.
|
55
|
-
except Exception as e:
|
56
|
-
logger.
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
logger.warning(f"Can not get role name: {e}")
|
119
|
-
|
120
|
-
try:
|
121
|
-
password_expires_at = self.users[user_id]["password_expires_at"]
|
122
|
-
except Exception as e:
|
123
|
-
logger.warning(f"Can not get password expires at: {e}")
|
124
|
-
|
125
|
-
|
126
|
-
try:
|
127
|
-
options = self.users[user_id]["options"]
|
128
|
-
except Exception as e:
|
129
|
-
logger.warning(f"Can not get option")
|
130
|
-
|
131
|
-
try:
|
132
|
-
enabled = self.users[user_id]["enabled"]
|
133
|
-
except Exception as e:
|
134
|
-
logger.warning(f"Can not get enabled")
|
135
|
-
|
136
|
-
role_assignment_ret = RoleAssignment(
|
137
|
-
user_id=user_id,
|
138
|
-
user_name=user_name,
|
139
|
-
role_id=role_id,
|
140
|
-
role_name=role_name,
|
141
|
-
enabled=enabled,
|
142
|
-
scope=role_assignment["scope"],
|
143
|
-
password_expires_at=password_expires_at,
|
144
|
-
options=options
|
145
|
-
)
|
146
|
-
|
147
|
-
return role_assignment_ret
|
1
|
+
import logging
|
2
|
+
from openstack.connection import Connection
|
3
|
+
from openstack.identity.v3.role_assignment import RoleAssignment as OSRoleAssignment
|
4
|
+
from typing import Generator
|
5
|
+
|
6
|
+
from .role_assignment_importer import RoleAssignmentImporter
|
7
|
+
from osi_dump.model.role_assignment import UserRoleAssignment, GroupRoleAssignment, EffectiveUserRole
|
8
|
+
from osi_dump.api.keystone import get_users
|
9
|
+
|
10
|
+
logger = logging.getLogger(__name__)
|
11
|
+
|
12
|
+
class OpenStackRoleAssignmentImporter(RoleAssignmentImporter):
|
13
|
+
def __init__(self, connection: Connection):
|
14
|
+
self.connection = connection
|
15
|
+
self.users = {}
|
16
|
+
self.roles = {}
|
17
|
+
self.groups = {}
|
18
|
+
self.group_members = {} # group_id -> [user_id_1, user_id_2]
|
19
|
+
self._raw_assignments = None # Cache
|
20
|
+
|
21
|
+
def _fetch_all_data(self):
|
22
|
+
"""pre-cache for metadata."""
|
23
|
+
if self._raw_assignments is not None:
|
24
|
+
return
|
25
|
+
|
26
|
+
logger.info(f"Pre-caching all role assignment metadata for {self.connection.auth['auth_url']}")
|
27
|
+
|
28
|
+
try:
|
29
|
+
os_users = get_users(self.connection)
|
30
|
+
for os_user in os_users: self.users[os_user["id"]] = os_user
|
31
|
+
except Exception as e:
|
32
|
+
logger.error(f"Could not fetch users: {e}")
|
33
|
+
|
34
|
+
try:
|
35
|
+
os_roles = self.connection.identity.roles()
|
36
|
+
for os_role in os_roles: self.roles[os_role.id] = os_role.name
|
37
|
+
except Exception as e:
|
38
|
+
logger.error(f"Could not fetch roles: {e}")
|
39
|
+
|
40
|
+
try:
|
41
|
+
os_groups = list(self.connection.identity.groups())
|
42
|
+
for group in os_groups:
|
43
|
+
self.groups[group.id] = group.name
|
44
|
+
try:
|
45
|
+
members = list(self.connection.identity.group_users(group))
|
46
|
+
self.group_members[group.id] = [user.id for user in members]
|
47
|
+
except Exception as e:
|
48
|
+
logger.warning(f"Could not fetch members for group {group.name}: {e}")
|
49
|
+
self.group_members[group.id] = []
|
50
|
+
except Exception as e:
|
51
|
+
logger.error(f"Could not fetch groups: {e}")
|
52
|
+
|
53
|
+
try:
|
54
|
+
self._raw_assignments = list(self.connection.identity.role_assignments())
|
55
|
+
except Exception as e:
|
56
|
+
logger.error(f"Could not fetch role assignments: {e}")
|
57
|
+
self._raw_assignments = []
|
58
|
+
|
59
|
+
def get_user_roles(self) -> Generator[UserRoleAssignment, None, None]:
|
60
|
+
self._fetch_all_data()
|
61
|
+
for assignment in self._raw_assignments:
|
62
|
+
if assignment.user:
|
63
|
+
user_id = assignment.user.get('id')
|
64
|
+
user_info = self.users.get(user_id, {})
|
65
|
+
role_id = assignment.role.get('id') if assignment.role else None
|
66
|
+
yield UserRoleAssignment(
|
67
|
+
user_id=user_id,
|
68
|
+
user_name=user_info.get('name'),
|
69
|
+
role_id=role_id,
|
70
|
+
role_name=self.roles.get(role_id),
|
71
|
+
scope=assignment.scope,
|
72
|
+
enabled=user_info.get('enabled'),
|
73
|
+
password_expires_at=user_info.get('password_expires_at'),
|
74
|
+
options=user_info.get('options')
|
75
|
+
)
|
76
|
+
|
77
|
+
def get_group_roles(self) -> Generator[GroupRoleAssignment, None, None]:
|
78
|
+
self._fetch_all_data()
|
79
|
+
for assignment in self._raw_assignments:
|
80
|
+
if assignment.group:
|
81
|
+
group_id = assignment.group.get('id')
|
82
|
+
role_id = assignment.role.get('id') if assignment.role else None
|
83
|
+
yield GroupRoleAssignment(
|
84
|
+
group_id=group_id,
|
85
|
+
group_name=self.groups.get(group_id),
|
86
|
+
role_id=role_id,
|
87
|
+
role_name=self.roles.get(role_id),
|
88
|
+
scope=assignment.scope
|
89
|
+
)
|
90
|
+
|
91
|
+
def calculate_effective_roles(self) -> Generator[EffectiveUserRole, None, None]:
|
92
|
+
# generator
|
93
|
+
# direct role
|
94
|
+
for user_role in self.get_user_roles():
|
95
|
+
yield EffectiveUserRole(
|
96
|
+
user_id=user_role.user_id,
|
97
|
+
user_name=user_role.user_name,
|
98
|
+
role_id=user_role.role_id,
|
99
|
+
role_name=user_role.role_name,
|
100
|
+
scope=user_role.scope,
|
101
|
+
inherited_from_group='[Direct]'
|
102
|
+
)
|
103
|
+
|
104
|
+
# inherited_from_group
|
105
|
+
for group_role in self.get_group_roles():
|
106
|
+
group_id = group_role.group_id
|
107
|
+
if group_id in self.group_members:
|
108
|
+
for user_id in self.group_members[group_id]:
|
109
|
+
user_info = self.users.get(user_id, {})
|
110
|
+
yield EffectiveUserRole(
|
111
|
+
user_id=user_id,
|
112
|
+
user_name=user_info.get('name'),
|
113
|
+
role_id=group_role.role_id,
|
114
|
+
role_name=group_role.role_name,
|
115
|
+
scope=group_role.scope,
|
116
|
+
inherited_from_group=group_role.group_name
|
117
|
+
)
|
@@ -1,9 +1,16 @@
|
|
1
|
-
from abc import ABC, abstractmethod
|
2
|
-
|
3
|
-
from osi_dump.model.role_assignment import
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
1
|
+
from abc import ABC, abstractmethod
|
2
|
+
from typing import Generator
|
3
|
+
from osi_dump.model.role_assignment import UserRoleAssignment, GroupRoleAssignment, EffectiveUserRole
|
4
|
+
|
5
|
+
class RoleAssignmentImporter(ABC):
|
6
|
+
@abstractmethod
|
7
|
+
def get_user_roles(self) -> Generator[UserRoleAssignment, None, None]:
|
8
|
+
pass
|
9
|
+
|
10
|
+
@abstractmethod
|
11
|
+
def get_group_roles(self) -> Generator[GroupRoleAssignment, None, None]:
|
12
|
+
pass
|
13
|
+
|
14
|
+
@abstractmethod
|
15
|
+
def calculate_effective_roles(self) -> Generator[EffectiveUserRole, None, None]:
|
16
|
+
pass
|