osism 0.20251004.0__py3-none-any.whl → 0.20251012.0__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.
osism/commands/manage.py CHANGED
@@ -14,10 +14,12 @@ from osism.data import (
14
14
  TEMPLATE_IMAGE_CLUSTERAPI,
15
15
  TEMPLATE_IMAGE_OCTAVIA,
16
16
  TEMPLATE_IMAGE_GARDENLINUX,
17
+ TEMPLATE_IMAGE_CLUSTERAPI_GARDENER,
17
18
  )
18
19
  from osism.tasks import openstack, ansible, handle_task
19
20
 
20
- SUPPORTED_CLUSTERAPI_K8S_IMAGES = ["1.31", "1.32", "1.33"]
21
+ SUPPORTED_CLUSTERAPI_GARDENER_K8S_IMAGES = ["1.33"]
22
+ SUPPORTED_CLUSTERAPI_K8S_IMAGES = ["1.32", "1.33", "1.34"]
21
23
  SUPPORTED_GARDENLINUX_VERSIONS = {"1877.2": "2025-08-07"}
22
24
 
23
25
 
@@ -133,6 +135,120 @@ class ImageClusterapi(Command):
133
135
  return handle_task(task, wait, format="script", timeout=3600)
134
136
 
135
137
 
138
+ class ImageClusterapiGardener(Command):
139
+ def get_parser(self, prog_name):
140
+ parser = super(ImageClusterapiGardener, self).get_parser(prog_name)
141
+
142
+ parser.add_argument(
143
+ "--no-wait",
144
+ default=False,
145
+ help="Do not wait until image management has been completed",
146
+ action="store_true",
147
+ )
148
+ parser.add_argument(
149
+ "--base-url",
150
+ type=str,
151
+ help="Base URL",
152
+ default="https://swift.services.a.regiocloud.tech/swift/v1/AUTH_b182637428444b9aa302bb8d5a5a418c/openstack-k8s-capi-images/",
153
+ )
154
+ parser.add_argument(
155
+ "--cloud",
156
+ type=str,
157
+ help="Cloud name in clouds.yaml (will be overruled by OS_AUTH_URL envvar)",
158
+ default="admin",
159
+ )
160
+ parser.add_argument(
161
+ "--dry-run",
162
+ action="store_true",
163
+ help="Do not perform any changes (--dry-run passed to openstack-image-manager)",
164
+ )
165
+ parser.add_argument(
166
+ "--tag",
167
+ type=str,
168
+ help="Name of the tag used to identify managed images (use openstack-image-manager's default if unset)",
169
+ default=None,
170
+ )
171
+ parser.add_argument(
172
+ "--filter",
173
+ type=str,
174
+ help="Filter the version to be managed (e.g. 1.33)",
175
+ default=None,
176
+ )
177
+ return parser
178
+
179
+ def take_action(self, parsed_args):
180
+ # Check if tasks are locked before proceeding
181
+ utils.check_task_lock_and_exit()
182
+
183
+ base_url = parsed_args.base_url
184
+ cloud = parsed_args.cloud
185
+ filter = parsed_args.filter
186
+ tag = parsed_args.tag
187
+ wait = not parsed_args.no_wait
188
+
189
+ if filter:
190
+ supported_cluterapi_gardener_k8s_images = [filter]
191
+ else:
192
+ supported_cluterapi_gardener_k8s_images = (
193
+ SUPPORTED_CLUSTERAPI_GARDENER_K8S_IMAGES
194
+ )
195
+
196
+ result = []
197
+ for kubernetes_release in supported_cluterapi_gardener_k8s_images:
198
+ url = urljoin(base_url, f"last-{kubernetes_release}-gardener")
199
+
200
+ response = requests.get(url)
201
+ splitted = response.text.strip().split(" ")
202
+
203
+ logger.info(f"date: {splitted[0]}")
204
+ logger.info(f"image: {splitted[1]}")
205
+
206
+ r = findall(
207
+ r".*ubuntu-[0-9][02468]04-kube-v(.*\..*\..*)\.qcow2", splitted[1]
208
+ )
209
+ logger.info(f"version: {r[0].strip()}")
210
+
211
+ url = urljoin(base_url, splitted[1])
212
+ logger.info(f"url: {url}")
213
+
214
+ logger.info(f"checksum_url: {url}.CHECKSUM")
215
+ response_checksum = requests.get(f"{url}.CHECKSUM")
216
+ splitted_checksum = response_checksum.text.strip().split(" ")
217
+ logger.info(f"checksum: {splitted_checksum[0]}")
218
+
219
+ template = Template(TEMPLATE_IMAGE_CLUSTERAPI_GARDENER)
220
+ result.extend(
221
+ [
222
+ template.render(
223
+ image_url=url,
224
+ image_checksum=f"sha256:{splitted_checksum[0]}",
225
+ image_version=r[0].strip(),
226
+ image_builddate=splitted[0],
227
+ )
228
+ ]
229
+ )
230
+
231
+ args = [
232
+ "--cloud",
233
+ cloud,
234
+ "--filter",
235
+ "ubuntu-capi-image-gardener",
236
+ ]
237
+ if tag is not None:
238
+ args.extend(["--tag", tag])
239
+ if parsed_args.dry_run:
240
+ args.append("--dry-run")
241
+
242
+ task_signature = openstack.image_manager.si(*args, configs=result, cloud=cloud)
243
+ task = task_signature.apply_async()
244
+ if wait:
245
+ logger.info(
246
+ f"It takes a moment until task {task.task_id} (image-manager) has been started and output is visible here."
247
+ )
248
+
249
+ return handle_task(task, wait, format="script", timeout=3600)
250
+
251
+
136
252
  class ImageGardenlinux(Command):
137
253
  def get_parser(self, prog_name):
138
254
  parser = super(ImageGardenlinux, self).get_parser(prog_name)
@@ -512,3 +628,611 @@ class Dnsmasq(Command):
512
628
  )
513
629
 
514
630
  return handle_task(task, wait, format="log", timeout=300)
631
+
632
+
633
+ class ProjectCreate(Command):
634
+ def get_parser(self, prog_name):
635
+ parser = super(ProjectCreate, self).get_parser(prog_name)
636
+
637
+ parser.add_argument(
638
+ "--no-wait",
639
+ default=False,
640
+ help="Do not wait until project creation has been completed",
641
+ action="store_true",
642
+ )
643
+
644
+ # Boolean flags with positive and negative forms
645
+ parser.add_argument(
646
+ "--assign-admin-user",
647
+ dest="assign_admin_user",
648
+ default=True,
649
+ help="Assign admin user to the project (default: True)",
650
+ action="store_true",
651
+ )
652
+ parser.add_argument(
653
+ "--noassign-admin-user",
654
+ dest="assign_admin_user",
655
+ help="Do not assign admin user to the project",
656
+ action="store_false",
657
+ )
658
+
659
+ parser.add_argument(
660
+ "--create-admin-user",
661
+ dest="create_admin_user",
662
+ default=True,
663
+ help="Create admin user for the project (default: True)",
664
+ action="store_true",
665
+ )
666
+ parser.add_argument(
667
+ "--nocreate-admin-user",
668
+ dest="create_admin_user",
669
+ help="Do not create admin user for the project",
670
+ action="store_false",
671
+ )
672
+
673
+ parser.add_argument(
674
+ "--create-domain",
675
+ dest="create_domain",
676
+ default=False,
677
+ help="Create a new domain for the project (default: False)",
678
+ action="store_true",
679
+ )
680
+ parser.add_argument(
681
+ "--nocreate-domain",
682
+ dest="create_domain",
683
+ help="Do not create a new domain for the project",
684
+ action="store_false",
685
+ )
686
+
687
+ parser.add_argument(
688
+ "--create-user",
689
+ dest="create_user",
690
+ default=False,
691
+ help="Create a new user for the project (default: False)",
692
+ action="store_true",
693
+ )
694
+ parser.add_argument(
695
+ "--nocreate-user",
696
+ dest="create_user",
697
+ help="Do not create a new user for the project",
698
+ action="store_false",
699
+ )
700
+
701
+ parser.add_argument(
702
+ "--domain-name-prefix",
703
+ dest="domain_name_prefix",
704
+ default=True,
705
+ help="Use domain name as prefix for project name (default: True)",
706
+ action="store_true",
707
+ )
708
+ parser.add_argument(
709
+ "--nodomain-name-prefix",
710
+ dest="domain_name_prefix",
711
+ help="Do not use domain name as prefix for project name",
712
+ action="store_false",
713
+ )
714
+
715
+ parser.add_argument(
716
+ "--has-service-network",
717
+ dest="has_service_network",
718
+ default=False,
719
+ help="Create a service network for the project (default: False)",
720
+ action="store_true",
721
+ )
722
+ parser.add_argument(
723
+ "--nohas-service-network",
724
+ dest="has_service_network",
725
+ help="Do not create a service network for the project",
726
+ action="store_false",
727
+ )
728
+
729
+ parser.add_argument(
730
+ "--has-public-network",
731
+ dest="has_public_network",
732
+ default=True,
733
+ help="Attach public network to the project (default: True)",
734
+ action="store_true",
735
+ )
736
+ parser.add_argument(
737
+ "--nohas-public-network",
738
+ dest="has_public_network",
739
+ help="Do not attach public network to the project",
740
+ action="store_false",
741
+ )
742
+
743
+ parser.add_argument(
744
+ "--has-shared-images",
745
+ dest="has_shared_images",
746
+ default=True,
747
+ help="Allow access to shared images (default: True)",
748
+ action="store_true",
749
+ )
750
+ parser.add_argument(
751
+ "--nohas-shared-images",
752
+ dest="has_shared_images",
753
+ help="Do not allow access to shared images",
754
+ action="store_false",
755
+ )
756
+
757
+ parser.add_argument(
758
+ "--random",
759
+ dest="random",
760
+ default=False,
761
+ help="Use random values for certain parameters (default: False)",
762
+ action="store_true",
763
+ )
764
+ parser.add_argument(
765
+ "--norandom",
766
+ dest="random",
767
+ help="Do not use random values",
768
+ action="store_false",
769
+ )
770
+
771
+ parser.add_argument(
772
+ "--managed-network-resources",
773
+ dest="managed_network_resources",
774
+ default=False,
775
+ help="Manage network resources (default: False)",
776
+ action="store_true",
777
+ )
778
+ parser.add_argument(
779
+ "--nomanaged-network-resources",
780
+ dest="managed_network_resources",
781
+ help="Do not manage network resources",
782
+ action="store_false",
783
+ )
784
+
785
+ # Integer arguments
786
+ parser.add_argument(
787
+ "--password-length",
788
+ dest="password_length",
789
+ type=int,
790
+ default=16,
791
+ help="Length of generated passwords (default: 16)",
792
+ )
793
+
794
+ parser.add_argument(
795
+ "--quota-multiplier",
796
+ dest="quota_multiplier",
797
+ type=int,
798
+ default=1,
799
+ help="Quota multiplier for all resources (default: 1)",
800
+ )
801
+
802
+ parser.add_argument(
803
+ "--quota-multiplier-compute",
804
+ dest="quota_multiplier_compute",
805
+ type=int,
806
+ default=None,
807
+ help="Quota multiplier for compute resources (default: None)",
808
+ )
809
+
810
+ parser.add_argument(
811
+ "--quota-multiplier-network",
812
+ dest="quota_multiplier_network",
813
+ type=int,
814
+ default=None,
815
+ help="Quota multiplier for network resources (default: None)",
816
+ )
817
+
818
+ parser.add_argument(
819
+ "--quota-multiplier-storage",
820
+ dest="quota_multiplier_storage",
821
+ type=int,
822
+ default=None,
823
+ help="Quota multiplier for storage resources (default: None)",
824
+ )
825
+
826
+ parser.add_argument(
827
+ "--quota-router",
828
+ dest="quota_router",
829
+ type=int,
830
+ default=1,
831
+ help="Router quota (default: 1)",
832
+ )
833
+
834
+ # String arguments
835
+ parser.add_argument(
836
+ "--admin-domain",
837
+ dest="admin_domain",
838
+ type=str,
839
+ default="default",
840
+ help="Admin domain name (default: default)",
841
+ )
842
+
843
+ parser.add_argument(
844
+ "--cloud",
845
+ type=str,
846
+ default="admin",
847
+ help="Cloud name in clouds.yaml (default: admin)",
848
+ )
849
+
850
+ parser.add_argument(
851
+ "--domain",
852
+ type=str,
853
+ default="default",
854
+ help="Domain name for the project (default: default)",
855
+ )
856
+
857
+ parser.add_argument(
858
+ "--internal-id",
859
+ dest="internal_id",
860
+ type=str,
861
+ default=None,
862
+ help="Internal ID for the project (default: None)",
863
+ )
864
+
865
+ parser.add_argument(
866
+ "--name",
867
+ type=str,
868
+ default="sandbox",
869
+ help="Project name (default: sandbox)",
870
+ )
871
+
872
+ parser.add_argument(
873
+ "--owner",
874
+ type=str,
875
+ default=None,
876
+ help="Project owner (default: None)",
877
+ )
878
+
879
+ parser.add_argument(
880
+ "--password",
881
+ type=str,
882
+ default=None,
883
+ help="Password for created users (default: None, auto-generated)",
884
+ )
885
+
886
+ parser.add_argument(
887
+ "--public-network",
888
+ dest="public_network",
889
+ type=str,
890
+ default="public",
891
+ help="Public network name (default: public)",
892
+ )
893
+
894
+ parser.add_argument(
895
+ "--quota-class",
896
+ dest="quota_class",
897
+ type=str,
898
+ default="basic",
899
+ help="Quota class to apply (default: basic)",
900
+ )
901
+
902
+ parser.add_argument(
903
+ "--service-network-cidr",
904
+ dest="service_network_cidr",
905
+ type=str,
906
+ default=None,
907
+ help="Service network CIDR (default: None)",
908
+ )
909
+
910
+ return parser
911
+
912
+ def take_action(self, parsed_args):
913
+ # Check if tasks are locked before proceeding
914
+ utils.check_task_lock_and_exit()
915
+
916
+ wait = not parsed_args.no_wait
917
+ cloud = parsed_args.cloud
918
+
919
+ # Build arguments list from all parsed_args
920
+ arguments = []
921
+
922
+ # Add boolean flags
923
+ if parsed_args.assign_admin_user:
924
+ arguments.append("--assign-admin-user")
925
+ else:
926
+ arguments.append("--noassign-admin-user")
927
+
928
+ if parsed_args.create_admin_user:
929
+ arguments.append("--create-admin-user")
930
+ else:
931
+ arguments.append("--nocreate-admin-user")
932
+
933
+ if parsed_args.create_domain:
934
+ arguments.append("--create-domain")
935
+ else:
936
+ arguments.append("--nocreate-domain")
937
+
938
+ if parsed_args.create_user:
939
+ arguments.append("--create-user")
940
+ else:
941
+ arguments.append("--nocreate-user")
942
+
943
+ if parsed_args.domain_name_prefix:
944
+ arguments.append("--domain-name-prefix")
945
+ else:
946
+ arguments.append("--nodomain-name-prefix")
947
+
948
+ if parsed_args.has_service_network:
949
+ arguments.append("--has-service-network")
950
+ else:
951
+ arguments.append("--nohas-service-network")
952
+
953
+ if parsed_args.has_public_network:
954
+ arguments.append("--has-public-network")
955
+ else:
956
+ arguments.append("--nohas-public-network")
957
+
958
+ if parsed_args.has_shared_images:
959
+ arguments.append("--has-shared-images")
960
+ else:
961
+ arguments.append("--nohas-shared-images")
962
+
963
+ if parsed_args.random:
964
+ arguments.append("--random")
965
+ else:
966
+ arguments.append("--norandom")
967
+
968
+ if parsed_args.managed_network_resources:
969
+ arguments.append("--managed-network-resources")
970
+ else:
971
+ arguments.append("--nomanaged-network-resources")
972
+
973
+ # Add integer arguments
974
+ arguments.extend(["--password-length", str(parsed_args.password_length)])
975
+ arguments.extend(["--quota-multiplier", str(parsed_args.quota_multiplier)])
976
+ arguments.extend(["--quota-router", str(parsed_args.quota_router)])
977
+
978
+ if parsed_args.quota_multiplier_compute is not None:
979
+ arguments.extend(
980
+ [
981
+ "--quota-multiplier-compute",
982
+ str(parsed_args.quota_multiplier_compute),
983
+ ]
984
+ )
985
+
986
+ if parsed_args.quota_multiplier_network is not None:
987
+ arguments.extend(
988
+ [
989
+ "--quota-multiplier-network",
990
+ str(parsed_args.quota_multiplier_network),
991
+ ]
992
+ )
993
+
994
+ if parsed_args.quota_multiplier_storage is not None:
995
+ arguments.extend(
996
+ [
997
+ "--quota-multiplier-storage",
998
+ str(parsed_args.quota_multiplier_storage),
999
+ ]
1000
+ )
1001
+
1002
+ # Add string arguments
1003
+ arguments.extend(["--admin-domain", parsed_args.admin_domain])
1004
+ arguments.extend(["--cloud", cloud])
1005
+ arguments.extend(["--domain", parsed_args.domain])
1006
+ arguments.extend(["--name", parsed_args.name])
1007
+ arguments.extend(["--public-network", parsed_args.public_network])
1008
+ arguments.extend(["--quota-class", parsed_args.quota_class])
1009
+
1010
+ if parsed_args.internal_id is not None:
1011
+ arguments.extend(["--internal-id", parsed_args.internal_id])
1012
+
1013
+ if parsed_args.owner is not None:
1014
+ arguments.extend(["--owner", parsed_args.owner])
1015
+
1016
+ if parsed_args.password is not None:
1017
+ arguments.extend(["--password", parsed_args.password])
1018
+
1019
+ if parsed_args.service_network_cidr is not None:
1020
+ arguments.extend(
1021
+ ["--service-network-cidr", parsed_args.service_network_cidr]
1022
+ )
1023
+
1024
+ # Call the task
1025
+ task_signature = openstack.project_manager.si(*arguments, cloud=cloud)
1026
+ task = task_signature.apply_async()
1027
+ if wait:
1028
+ logger.info(
1029
+ f"It takes a moment until task {task.task_id} (project-manager) has been started and output is visible here."
1030
+ )
1031
+
1032
+ return handle_task(task, wait, format="script", timeout=3600)
1033
+
1034
+
1035
+ class ProjectSync(Command):
1036
+ def get_parser(self, prog_name):
1037
+ parser = super(ProjectSync, self).get_parser(prog_name)
1038
+
1039
+ # Boolean flags
1040
+ parser.add_argument(
1041
+ "--assign-admin-user",
1042
+ dest="assign_admin_user",
1043
+ default=False,
1044
+ help="Assign admin user to projects (default: False)",
1045
+ action="store_true",
1046
+ )
1047
+ parser.add_argument(
1048
+ "--noassign-admin-user",
1049
+ dest="assign_admin_user",
1050
+ help="Do not assign admin user to projects",
1051
+ action="store_false",
1052
+ )
1053
+
1054
+ parser.add_argument(
1055
+ "--dry-run",
1056
+ dest="dry_run",
1057
+ default=False,
1058
+ help="Do not really do anything, just simulate (default: False)",
1059
+ action="store_true",
1060
+ )
1061
+ parser.add_argument(
1062
+ "--nodry-run",
1063
+ dest="dry_run",
1064
+ help="Execute actions (not a dry run)",
1065
+ action="store_false",
1066
+ )
1067
+
1068
+ parser.add_argument(
1069
+ "--manage-endpoints",
1070
+ dest="manage_endpoints",
1071
+ default=False,
1072
+ help="Manage endpoints (default: False)",
1073
+ action="store_true",
1074
+ )
1075
+ parser.add_argument(
1076
+ "--nomanage-endpoints",
1077
+ dest="manage_endpoints",
1078
+ help="Do not manage endpoints",
1079
+ action="store_false",
1080
+ )
1081
+
1082
+ parser.add_argument(
1083
+ "--manage-homeprojects",
1084
+ dest="manage_homeprojects",
1085
+ default=False,
1086
+ help="Manage home projects (default: False)",
1087
+ action="store_true",
1088
+ )
1089
+ parser.add_argument(
1090
+ "--nomanage-homeprojects",
1091
+ dest="manage_homeprojects",
1092
+ help="Do not manage home projects",
1093
+ action="store_false",
1094
+ )
1095
+
1096
+ parser.add_argument(
1097
+ "--manage-privatevolumetypes",
1098
+ dest="manage_privatevolumetypes",
1099
+ default=True,
1100
+ help="Manage private volume types (default: True)",
1101
+ action="store_true",
1102
+ )
1103
+ parser.add_argument(
1104
+ "--nomanage-privatevolumetypes",
1105
+ dest="manage_privatevolumetypes",
1106
+ help="Do not manage private volume types",
1107
+ action="store_false",
1108
+ )
1109
+
1110
+ parser.add_argument(
1111
+ "--manage-privateflavors",
1112
+ dest="manage_privateflavors",
1113
+ default=True,
1114
+ help="Manage private flavors (default: True)",
1115
+ action="store_true",
1116
+ )
1117
+ parser.add_argument(
1118
+ "--nomanage-privateflavors",
1119
+ dest="manage_privateflavors",
1120
+ help="Do not manage private flavors",
1121
+ action="store_false",
1122
+ )
1123
+
1124
+ # String arguments
1125
+ parser.add_argument(
1126
+ "--admin-domain",
1127
+ dest="admin_domain",
1128
+ type=str,
1129
+ default="default",
1130
+ help="Admin domain name (default: default)",
1131
+ )
1132
+
1133
+ parser.add_argument(
1134
+ "--classes",
1135
+ type=str,
1136
+ default="etc/classes.yml",
1137
+ help="Path to the classes.yml file (default: etc/classes.yml)",
1138
+ )
1139
+
1140
+ parser.add_argument(
1141
+ "--endpoints",
1142
+ type=str,
1143
+ default="etc/endpoints.yml",
1144
+ help="Path to the endpoints.yml file (default: etc/endpoints.yml)",
1145
+ )
1146
+
1147
+ parser.add_argument(
1148
+ "--cloud",
1149
+ type=str,
1150
+ default="admin",
1151
+ help="Cloud name in clouds.yaml (default: admin)",
1152
+ )
1153
+
1154
+ parser.add_argument(
1155
+ "--domain",
1156
+ type=str,
1157
+ default=None,
1158
+ help="Domain to be managed (default: None)",
1159
+ )
1160
+
1161
+ parser.add_argument(
1162
+ "--name",
1163
+ type=str,
1164
+ default=None,
1165
+ help="Project to be managed (default: None)",
1166
+ )
1167
+
1168
+ parser.add_argument(
1169
+ "--no-wait",
1170
+ default=False,
1171
+ help="Do not wait until project sync has been completed",
1172
+ action="store_true",
1173
+ )
1174
+
1175
+ return parser
1176
+
1177
+ def take_action(self, parsed_args):
1178
+ # Check if tasks are locked before proceeding
1179
+ utils.check_task_lock_and_exit()
1180
+
1181
+ wait = not parsed_args.no_wait
1182
+ cloud = parsed_args.cloud
1183
+
1184
+ # Build arguments list from all parsed_args
1185
+ arguments = []
1186
+
1187
+ # Add boolean flags
1188
+ if parsed_args.assign_admin_user:
1189
+ arguments.append("--assign-admin-user")
1190
+ else:
1191
+ arguments.append("--noassign-admin-user")
1192
+
1193
+ if parsed_args.dry_run:
1194
+ arguments.append("--dry-run")
1195
+ else:
1196
+ arguments.append("--nodry-run")
1197
+
1198
+ if parsed_args.manage_endpoints:
1199
+ arguments.append("--manage-endpoints")
1200
+ else:
1201
+ arguments.append("--nomanage-endpoints")
1202
+
1203
+ if parsed_args.manage_homeprojects:
1204
+ arguments.append("--manage-homeprojects")
1205
+ else:
1206
+ arguments.append("--nomanage-homeprojects")
1207
+
1208
+ if parsed_args.manage_privatevolumetypes:
1209
+ arguments.append("--manage-privatevolumetypes")
1210
+ else:
1211
+ arguments.append("--nomanage-privatevolumetypes")
1212
+
1213
+ if parsed_args.manage_privateflavors:
1214
+ arguments.append("--manage-privateflavors")
1215
+ else:
1216
+ arguments.append("--nomanage-privateflavors")
1217
+
1218
+ # Add string arguments
1219
+ arguments.extend(["--admin-domain", parsed_args.admin_domain])
1220
+ arguments.extend(["--classes", parsed_args.classes])
1221
+ arguments.extend(["--endpoints", parsed_args.endpoints])
1222
+ arguments.extend(["--cloud", cloud])
1223
+
1224
+ if parsed_args.domain is not None:
1225
+ arguments.extend(["--domain", parsed_args.domain])
1226
+
1227
+ if parsed_args.name is not None:
1228
+ arguments.extend(["--name", parsed_args.name])
1229
+
1230
+ # Call the task
1231
+ task_signature = openstack.project_manager_sync.si(*arguments, cloud=cloud)
1232
+ task = task_signature.apply_async()
1233
+ if wait:
1234
+ logger.info(
1235
+ f"It takes a moment until task {task.task_id} (project-manager-sync) has been started and output is visible here."
1236
+ )
1237
+
1238
+ return handle_task(task, wait, format="script", timeout=3600)