1# Licensed under the Apache License, Version 2.0 (the "License"); 2# you may not use this file except in compliance with the License. 3# You may obtain a copy of the License at 4# 5# http://www.apache.org/licenses/LICENSE-2.0 6# 7# Unless required by applicable law or agreed to in writing, software 8# distributed under the License is distributed on an "AS IS" BASIS, 9# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10# See the License for the specific language governing permissions and 11# limitations under the License. 12 13# import types so that we can reference ListType in sphinx param declarations. 14# We can't just use list, because sphinx gets confused by 15# openstack.resource.Resource.list and openstack.resource2.Resource.list 16import types # noqa 17 18from openstack.cloud import _normalize 19from openstack.cloud import _utils 20from openstack.cloud import exc 21from openstack import utils 22 23 24class ClusteringCloudMixin(_normalize.Normalizer): 25 26 @property 27 def _clustering_client(self): 28 if 'clustering' not in self._raw_clients: 29 clustering_client = self._get_versioned_client( 30 'clustering', min_version=1, max_version='1.latest') 31 self._raw_clients['clustering'] = clustering_client 32 return self._raw_clients['clustering'] 33 34 def create_cluster(self, name, profile, config=None, desired_capacity=0, 35 max_size=None, metadata=None, min_size=None, 36 timeout=None): 37 profile = self.get_cluster_profile(profile) 38 profile_id = profile['id'] 39 body = { 40 'desired_capacity': desired_capacity, 41 'name': name, 42 'profile_id': profile_id 43 } 44 45 if config is not None: 46 body['config'] = config 47 48 if max_size is not None: 49 body['max_size'] = max_size 50 51 if metadata is not None: 52 body['metadata'] = metadata 53 54 if min_size is not None: 55 body['min_size'] = min_size 56 57 if timeout is not None: 58 body['timeout'] = timeout 59 60 data = self._clustering_client.post( 61 '/clusters', json={'cluster': body}, 62 error_message="Error creating cluster {name}".format(name=name)) 63 64 return self._get_and_munchify(key=None, data=data) 65 66 def set_cluster_metadata(self, name_or_id, metadata): 67 cluster = self.get_cluster(name_or_id) 68 if not cluster: 69 raise exc.OpenStackCloudException( 70 'Invalid Cluster {cluster}'.format(cluster=name_or_id)) 71 72 self._clustering_client.post( 73 '/clusters/{cluster_id}/metadata'.format(cluster_id=cluster['id']), 74 json={'metadata': metadata}, 75 error_message='Error updating cluster metadata') 76 77 def get_cluster_by_id(self, cluster_id): 78 try: 79 data = self._clustering_client.get( 80 "/clusters/{cluster_id}".format(cluster_id=cluster_id), 81 error_message="Error fetching cluster {name}".format( 82 name=cluster_id)) 83 return self._get_and_munchify('cluster', data) 84 except Exception: 85 return None 86 87 def get_cluster(self, name_or_id, filters=None): 88 return _utils._get_entity( 89 cloud=self, resource='cluster', 90 name_or_id=name_or_id, filters=filters) 91 92 def update_cluster(self, name_or_id, new_name=None, 93 profile_name_or_id=None, config=None, metadata=None, 94 timeout=None, profile_only=False): 95 old_cluster = self.get_cluster(name_or_id) 96 if old_cluster is None: 97 raise exc.OpenStackCloudException( 98 'Invalid Cluster {cluster}'.format(cluster=name_or_id)) 99 cluster = { 100 'profile_only': profile_only 101 } 102 103 if config is not None: 104 cluster['config'] = config 105 106 if metadata is not None: 107 cluster['metadata'] = metadata 108 109 if profile_name_or_id is not None: 110 profile = self.get_cluster_profile(profile_name_or_id) 111 if profile is None: 112 raise exc.OpenStackCloudException( 113 'Invalid Cluster Profile {profile}'.format( 114 profile=profile_name_or_id)) 115 cluster['profile_id'] = profile.id 116 117 if timeout is not None: 118 cluster['timeout'] = timeout 119 120 if new_name is not None: 121 cluster['name'] = new_name 122 123 data = self._clustering_client.patch( 124 "/clusters/{cluster_id}".format(cluster_id=old_cluster['id']), 125 json={'cluster': cluster}, 126 error_message="Error updating cluster " 127 "{name}".format(name=name_or_id)) 128 129 return self._get_and_munchify(key=None, data=data) 130 131 def delete_cluster(self, name_or_id): 132 cluster = self.get_cluster(name_or_id) 133 if cluster is None: 134 self.log.debug("Cluster %s not found for deleting", name_or_id) 135 return False 136 137 for policy in self.list_policies_on_cluster(name_or_id): 138 detach_policy = self.get_cluster_policy_by_id( 139 policy['policy_id']) 140 self.detach_policy_from_cluster(cluster, detach_policy) 141 142 for receiver in self.list_cluster_receivers(): 143 if cluster["id"] == receiver["cluster_id"]: 144 self.delete_cluster_receiver(receiver["id"], wait=True) 145 146 self._clustering_client.delete( 147 "/clusters/{cluster_id}".format(cluster_id=name_or_id), 148 error_message="Error deleting cluster {name}".format( 149 name=name_or_id)) 150 151 return True 152 153 def search_clusters(self, name_or_id=None, filters=None): 154 clusters = self.list_clusters() 155 return _utils._filter_list(clusters, name_or_id, filters) 156 157 def list_clusters(self): 158 try: 159 data = self._clustering_client.get( 160 '/clusters', 161 error_message="Error fetching clusters") 162 return self._get_and_munchify('clusters', data) 163 except exc.OpenStackCloudURINotFound as e: 164 self.log.debug(str(e), exc_info=True) 165 return [] 166 167 def attach_policy_to_cluster(self, name_or_id, policy_name_or_id, 168 is_enabled): 169 cluster = self.get_cluster(name_or_id) 170 policy = self.get_cluster_policy(policy_name_or_id) 171 if cluster is None: 172 raise exc.OpenStackCloudException( 173 'Cluster {cluster} not found for attaching'.format( 174 cluster=name_or_id)) 175 176 if policy is None: 177 raise exc.OpenStackCloudException( 178 'Policy {policy} not found for attaching'.format( 179 policy=policy_name_or_id)) 180 181 body = { 182 'policy_id': policy['id'], 183 'enabled': is_enabled 184 } 185 186 self._clustering_client.post( 187 "/clusters/{cluster_id}/actions".format(cluster_id=cluster['id']), 188 error_message="Error attaching policy {policy} to cluster " 189 "{cluster}".format( 190 policy=policy['id'], 191 cluster=cluster['id']), 192 json={'policy_attach': body}) 193 194 return True 195 196 def detach_policy_from_cluster( 197 self, name_or_id, policy_name_or_id, wait=False, timeout=3600): 198 cluster = self.get_cluster(name_or_id) 199 policy = self.get_cluster_policy(policy_name_or_id) 200 if cluster is None: 201 raise exc.OpenStackCloudException( 202 'Cluster {cluster} not found for detaching'.format( 203 cluster=name_or_id)) 204 205 if policy is None: 206 raise exc.OpenStackCloudException( 207 'Policy {policy} not found for detaching'.format( 208 policy=policy_name_or_id)) 209 210 body = {'policy_id': policy['id']} 211 self._clustering_client.post( 212 "/clusters/{cluster_id}/actions".format(cluster_id=cluster['id']), 213 error_message="Error detaching policy {policy} from cluster " 214 "{cluster}".format( 215 policy=policy['id'], 216 cluster=cluster['id']), 217 json={'policy_detach': body}) 218 219 if not wait: 220 return True 221 222 value = [] 223 224 for count in utils.iterate_timeout( 225 timeout, "Timeout waiting for cluster policy to detach"): 226 227 # TODO(bjjohnson) This logic will wait until there are no policies. 228 # Since we're detaching a specific policy, checking to make sure 229 # that policy is not in the list of policies would be better. 230 policy_status = self.get_cluster_by_id(cluster['id'])['policies'] 231 232 if policy_status == value: 233 break 234 return True 235 236 def update_policy_on_cluster(self, name_or_id, policy_name_or_id, 237 is_enabled): 238 cluster = self.get_cluster(name_or_id) 239 policy = self.get_cluster_policy(policy_name_or_id) 240 if cluster is None: 241 raise exc.OpenStackCloudException( 242 'Cluster {cluster} not found for updating'.format( 243 cluster=name_or_id)) 244 245 if policy is None: 246 raise exc.OpenStackCloudException( 247 'Policy {policy} not found for updating'.format( 248 policy=policy_name_or_id)) 249 250 body = { 251 'policy_id': policy['id'], 252 'enabled': is_enabled 253 } 254 self._clustering_client.post( 255 "/clusters/{cluster_id}/actions".format(cluster_id=cluster['id']), 256 error_message="Error updating policy {policy} on cluster " 257 "{cluster}".format( 258 policy=policy['id'], 259 cluster=cluster['id']), 260 json={'policy_update': body}) 261 262 return True 263 264 def get_policy_on_cluster(self, name_or_id, policy_name_or_id): 265 try: 266 policy = self._clustering_client.get( 267 "/clusters/{cluster_id}/policies/{policy_id}".format( 268 cluster_id=name_or_id, policy_id=policy_name_or_id), 269 error_message="Error fetching policy " 270 "{name}".format(name=policy_name_or_id)) 271 return self._get_and_munchify('cluster_policy', policy) 272 except Exception: 273 return False 274 275 def list_policies_on_cluster(self, name_or_id): 276 endpoint = "/clusters/{cluster_id}/policies".format( 277 cluster_id=name_or_id) 278 try: 279 data = self._clustering_client.get( 280 endpoint, 281 error_message="Error fetching cluster policies") 282 except exc.OpenStackCloudURINotFound as e: 283 self.log.debug(str(e), exc_info=True) 284 return [] 285 return self._get_and_munchify('cluster_policies', data) 286 287 def create_cluster_profile(self, name, spec, metadata=None): 288 profile = { 289 'name': name, 290 'spec': spec 291 } 292 293 if metadata is not None: 294 profile['metadata'] = metadata 295 296 data = self._clustering_client.post( 297 '/profiles', json={'profile': profile}, 298 error_message="Error creating profile {name}".format(name=name)) 299 300 return self._get_and_munchify('profile', data) 301 302 def set_cluster_profile_metadata(self, name_or_id, metadata): 303 profile = self.get_cluster_profile(name_or_id) 304 if not profile: 305 raise exc.OpenStackCloudException( 306 'Invalid Profile {profile}'.format(profile=name_or_id)) 307 308 self._clustering_client.post( 309 '/profiles/{profile_id}/metadata'.format(profile_id=profile['id']), 310 json={'metadata': metadata}, 311 error_message='Error updating profile metadata') 312 313 def search_cluster_profiles(self, name_or_id=None, filters=None): 314 cluster_profiles = self.list_cluster_profiles() 315 return _utils._filter_list(cluster_profiles, name_or_id, filters) 316 317 def list_cluster_profiles(self): 318 try: 319 data = self._clustering_client.get( 320 '/profiles', 321 error_message="Error fetching profiles") 322 except exc.OpenStackCloudURINotFound as e: 323 self.log.debug(str(e), exc_info=True) 324 return [] 325 return self._get_and_munchify('profiles', data) 326 327 def get_cluster_profile_by_id(self, profile_id): 328 try: 329 data = self._clustering_client.get( 330 "/profiles/{profile_id}".format(profile_id=profile_id), 331 error_message="Error fetching profile {name}".format( 332 name=profile_id)) 333 return self._get_and_munchify('profile', data) 334 except exc.OpenStackCloudURINotFound as e: 335 self.log.debug(str(e), exc_info=True) 336 return None 337 338 def get_cluster_profile(self, name_or_id, filters=None): 339 return _utils._get_entity(self, 'cluster_profile', name_or_id, filters) 340 341 def delete_cluster_profile(self, name_or_id): 342 profile = self.get_cluster_profile(name_or_id) 343 if profile is None: 344 self.log.debug("Profile %s not found for deleting", name_or_id) 345 return False 346 347 for cluster in self.list_clusters(): 348 if (name_or_id, profile.id) in cluster.items(): 349 self.log.debug( 350 "Profile %s is being used by cluster %s, won't delete", 351 name_or_id, cluster.name) 352 return False 353 354 self._clustering_client.delete( 355 "/profiles/{profile_id}".format(profile_id=profile['id']), 356 error_message="Error deleting profile " 357 "{name}".format(name=name_or_id)) 358 359 return True 360 361 def update_cluster_profile(self, name_or_id, metadata=None, new_name=None): 362 old_profile = self.get_cluster_profile(name_or_id) 363 if not old_profile: 364 raise exc.OpenStackCloudException( 365 'Invalid Profile {profile}'.format(profile=name_or_id)) 366 367 profile = {} 368 369 if metadata is not None: 370 profile['metadata'] = metadata 371 372 if new_name is not None: 373 profile['name'] = new_name 374 375 data = self._clustering_client.patch( 376 "/profiles/{profile_id}".format(profile_id=old_profile.id), 377 json={'profile': profile}, 378 error_message="Error updating profile {name}".format( 379 name=name_or_id)) 380 381 return self._get_and_munchify(key=None, data=data) 382 383 def create_cluster_policy(self, name, spec): 384 policy = { 385 'name': name, 386 'spec': spec 387 } 388 389 data = self._clustering_client.post( 390 '/policies', json={'policy': policy}, 391 error_message="Error creating policy {name}".format( 392 name=policy['name'])) 393 return self._get_and_munchify('policy', data) 394 395 def search_cluster_policies(self, name_or_id=None, filters=None): 396 cluster_policies = self.list_cluster_policies() 397 return _utils._filter_list(cluster_policies, name_or_id, filters) 398 399 def list_cluster_policies(self): 400 endpoint = "/policies" 401 try: 402 data = self._clustering_client.get( 403 endpoint, 404 error_message="Error fetching cluster policies") 405 except exc.OpenStackCloudURINotFound as e: 406 self.log.debug(str(e), exc_info=True) 407 return [] 408 return self._get_and_munchify('policies', data) 409 410 def get_cluster_policy_by_id(self, policy_id): 411 try: 412 data = self._clustering_client.get( 413 "/policies/{policy_id}".format(policy_id=policy_id), 414 error_message="Error fetching policy {name}".format( 415 name=policy_id)) 416 return self._get_and_munchify('policy', data) 417 except exc.OpenStackCloudURINotFound as e: 418 self.log.debug(str(e), exc_info=True) 419 return None 420 421 def get_cluster_policy(self, name_or_id, filters=None): 422 return _utils._get_entity( 423 self, 'cluster_policie', name_or_id, filters) 424 425 def delete_cluster_policy(self, name_or_id): 426 policy = self.get_cluster_policy_by_id(name_or_id) 427 if policy is None: 428 self.log.debug("Policy %s not found for deleting", name_or_id) 429 return False 430 431 for cluster in self.list_clusters(): 432 if (name_or_id, policy.id) in cluster.items(): 433 self.log.debug( 434 "Policy %s is being used by cluster %s, won't delete", 435 name_or_id, cluster.name) 436 return False 437 438 self._clustering_client.delete( 439 "/policies/{policy_id}".format(policy_id=name_or_id), 440 error_message="Error deleting policy " 441 "{name}".format(name=name_or_id)) 442 443 return True 444 445 def update_cluster_policy(self, name_or_id, new_name): 446 old_policy = self.get_cluster_policy(name_or_id) 447 if not old_policy: 448 raise exc.OpenStackCloudException( 449 'Invalid Policy {policy}'.format(policy=name_or_id)) 450 policy = {'name': new_name} 451 452 data = self._clustering_client.patch( 453 "/policies/{policy_id}".format(policy_id=old_policy.id), 454 json={'policy': policy}, 455 error_message="Error updating policy " 456 "{name}".format(name=name_or_id)) 457 return self._get_and_munchify(key=None, data=data) 458 459 def create_cluster_receiver(self, name, receiver_type, 460 cluster_name_or_id=None, action=None, 461 actor=None, params=None): 462 cluster = self.get_cluster(cluster_name_or_id) 463 if cluster is None: 464 raise exc.OpenStackCloudException( 465 'Invalid cluster {cluster}'.format(cluster=cluster_name_or_id)) 466 467 receiver = { 468 'name': name, 469 'type': receiver_type 470 } 471 472 if cluster_name_or_id is not None: 473 receiver['cluster_id'] = cluster.id 474 475 if action is not None: 476 receiver['action'] = action 477 478 if actor is not None: 479 receiver['actor'] = actor 480 481 if params is not None: 482 receiver['params'] = params 483 484 data = self._clustering_client.post( 485 '/receivers', json={'receiver': receiver}, 486 error_message="Error creating receiver {name}".format(name=name)) 487 return self._get_and_munchify('receiver', data) 488 489 def search_cluster_receivers(self, name_or_id=None, filters=None): 490 cluster_receivers = self.list_cluster_receivers() 491 return _utils._filter_list(cluster_receivers, name_or_id, filters) 492 493 def list_cluster_receivers(self): 494 try: 495 data = self._clustering_client.get( 496 '/receivers', 497 error_message="Error fetching receivers") 498 except exc.OpenStackCloudURINotFound as e: 499 self.log.debug(str(e), exc_info=True) 500 return [] 501 return self._get_and_munchify('receivers', data) 502 503 def get_cluster_receiver_by_id(self, receiver_id): 504 try: 505 data = self._clustering_client.get( 506 "/receivers/{receiver_id}".format(receiver_id=receiver_id), 507 error_message="Error fetching receiver {name}".format( 508 name=receiver_id)) 509 return self._get_and_munchify('receiver', data) 510 except exc.OpenStackCloudURINotFound as e: 511 self.log.debug(str(e), exc_info=True) 512 return None 513 514 def get_cluster_receiver(self, name_or_id, filters=None): 515 return _utils._get_entity( 516 self, 'cluster_receiver', name_or_id, filters) 517 518 def delete_cluster_receiver(self, name_or_id, wait=False, timeout=3600): 519 receiver = self.get_cluster_receiver(name_or_id) 520 if receiver is None: 521 self.log.debug("Receiver %s not found for deleting", name_or_id) 522 return False 523 524 receiver_id = receiver['id'] 525 526 self._clustering_client.delete( 527 "/receivers/{receiver_id}".format(receiver_id=receiver_id), 528 error_message="Error deleting receiver {name}".format( 529 name=name_or_id)) 530 531 if not wait: 532 return True 533 534 for count in utils.iterate_timeout( 535 timeout, "Timeout waiting for cluster receiver to delete"): 536 537 receiver = self.get_cluster_receiver_by_id(receiver_id) 538 539 if not receiver: 540 break 541 542 return True 543 544 def update_cluster_receiver(self, name_or_id, new_name=None, action=None, 545 params=None): 546 old_receiver = self.get_cluster_receiver(name_or_id) 547 if old_receiver is None: 548 raise exc.OpenStackCloudException( 549 'Invalid receiver {receiver}'.format(receiver=name_or_id)) 550 551 receiver = {} 552 553 if new_name is not None: 554 receiver['name'] = new_name 555 556 if action is not None: 557 receiver['action'] = action 558 559 if params is not None: 560 receiver['params'] = params 561 562 data = self._clustering_client.patch( 563 "/receivers/{receiver_id}".format(receiver_id=old_receiver.id), 564 json={'receiver': receiver}, 565 error_message="Error updating receiver {name}".format( 566 name=name_or_id)) 567 return self._get_and_munchify(key=None, data=data) 568