Cluster upgrade extension for Fuel
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

handlers.py 7.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. # -*- coding: utf-8 -*-
  2. # Copyright 2015 Mirantis, Inc.
  3. #
  4. # Licensed under the Apache License, Version 2.0 (the "License"); you may
  5. # not use this file except in compliance with the License. You may obtain
  6. # a copy of the License at
  7. #
  8. # http://www.apache.org/licenses/LICENSE-2.0
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  12. # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. # License for the specific language governing permissions and limitations
  14. # under the License.
  15. import copy
  16. import six
  17. from nailgun.api.v1.handlers import base
  18. from nailgun import errors
  19. from nailgun import objects
  20. from nailgun.task import manager
  21. from . import upgrade
  22. from . import validators
  23. from .objects import adapters
  24. class ClusterUpgradeCloneHandler(base.BaseHandler):
  25. single = objects.Cluster
  26. validator = validators.ClusterUpgradeValidator
  27. @base.handle_errors
  28. @base.validate
  29. @base.serialize
  30. def POST(self, cluster_id):
  31. """Initialize the upgrade of the cluster.
  32. Creates a new cluster with specified name and release_id. The
  33. new cluster is created with parameters that are copied from the
  34. cluster with the given cluster_id. The values of the generated
  35. and editable attributes are just copied from one to the other.
  36. :param cluster_id: ID of the cluster from which parameters would
  37. be copied
  38. :returns: JSON representation of the created cluster
  39. :http: * 200 (OK)
  40. * 400 (upgrade parameters are invalid)
  41. * 404 (node or release not found in db)
  42. """
  43. orig_cluster = adapters.NailgunClusterAdapter(
  44. self.get_object_or_404(self.single, cluster_id))
  45. request_data = self.checked_data(cluster=orig_cluster)
  46. new_cluster = upgrade.UpgradeHelper.clone_cluster(orig_cluster,
  47. request_data)
  48. valid = upgrade.UpgradeHelper.validate_network_roles(
  49. orig_cluster, new_cluster,
  50. )
  51. if not valid:
  52. raise errors.InvalidData("Network changes during upgrade"
  53. " is not supported.")
  54. return new_cluster.to_dict()
  55. class NodeReassignHandler(base.BaseHandler):
  56. single = objects.Cluster
  57. validator = validators.NodeReassignValidator
  58. task_manager = manager.ProvisioningTaskManager
  59. def handle_task(self, cluster_id, nodes):
  60. try:
  61. task_manager = self.task_manager(cluster_id=cluster_id)
  62. task = task_manager.execute(nodes)
  63. except Exception as exc:
  64. raise self.http(400, msg=six.text_type(exc))
  65. self.raise_task(task)
  66. @base.handle_errors
  67. @base.validate
  68. def POST(self, cluster_id):
  69. """Reassign nodes to the given cluster.
  70. The given nodes will be assigned from the current cluster to the
  71. given cluster, by default it involves the reprovisioning of the
  72. nodes. If the 'reprovision' flag is set to False, then the nodes
  73. will be just reassigned. If the 'roles' list is specified, then
  74. the given roles will be used as 'pending_roles' in case of
  75. the reprovisioning or otherwise as 'roles'.
  76. :param cluster_id: ID of the cluster nodes should be assigned to.
  77. :returns: None
  78. :http: * 202 (OK)
  79. * 400 (Incorrect node state, problem with task execution,
  80. conflicting or incorrect roles)
  81. * 404 (Cluster or node not found)
  82. """
  83. cluster = adapters.NailgunClusterAdapter(
  84. self.get_object_or_404(self.single, cluster_id))
  85. data = self.checked_data(cluster=cluster)
  86. reprovision = data.get('reprovision', True)
  87. given_roles = data.get('roles', [])
  88. nodes_to_provision = []
  89. for node_id in data['nodes_ids']:
  90. node = adapters.NailgunNodeAdapter(
  91. self.get_object_or_404(objects.Node, node_id))
  92. nodes_to_provision.append(node.node)
  93. roles, pending_roles = upgrade.UpgradeHelper.get_node_roles(
  94. reprovision, node.roles, given_roles)
  95. upgrade.UpgradeHelper.assign_node_to_cluster(
  96. node, cluster, roles, pending_roles)
  97. if reprovision:
  98. self.handle_task(cluster_id, nodes_to_provision)
  99. class CopyVIPsHandler(base.BaseHandler):
  100. single = objects.Cluster
  101. validator = validators.CopyVIPsValidator
  102. @base.handle_errors
  103. @base.validate
  104. @base.serialize
  105. def POST(self, cluster_id):
  106. """Copy VIPs from original cluster to new one
  107. Original cluster object is obtained from existing relation between
  108. clusters that is created on cluster clone operation
  109. :param cluster_id: id of cluster that VIPs must be copied to
  110. :returns: Collection of JSON-serialised VIPs.
  111. :http: * 200 (OK)
  112. * 400 (validation failed)
  113. * 404 (seed cluster is not found)
  114. """
  115. from .objects import relations
  116. cluster = self.get_object_or_404(self.single, cluster_id)
  117. relation = relations.UpgradeRelationObject.get_cluster_relation(
  118. cluster.id)
  119. self.checked_data(cluster=cluster, relation=relation)
  120. # get original cluster object and create adapter with it
  121. orig_cluster_adapter = adapters.NailgunClusterAdapter.get_by_uid(
  122. relation.orig_cluster_id)
  123. seed_cluster_adapter = adapters.NailgunClusterAdapter(cluster)
  124. upgrade.UpgradeHelper.copy_vips(orig_cluster_adapter,
  125. seed_cluster_adapter)
  126. cluster_vips = objects.IPAddrCollection.get_vips_by_cluster_id(
  127. cluster.id)
  128. return objects.IPAddrCollection.to_list(cluster_vips)
  129. class CreateUpgradeReleaseHandler(base.BaseHandler):
  130. @staticmethod
  131. def merge_network_roles(base_nets, orig_nets):
  132. """Create network metadata based on two releases.
  133. Overwrite base default_mapping by orig default_maping values.
  134. """
  135. base_nets = copy.deepcopy(base_nets)
  136. orig_nets = copy.deepcopy(orig_nets)
  137. orig_network_dict = {n['id']: n for n in orig_nets}
  138. for base_net in base_nets:
  139. orig_net = orig_network_dict.get(base_net['id'])
  140. if orig_net is not None:
  141. base_net['default_mapping'] = orig_net['default_mapping']
  142. return base_nets
  143. @base.serialize
  144. def POST(self, cluster_id, release_id):
  145. """Create release for upgrade purposes.
  146. Creates a new release with network_roles_metadata based the given
  147. release and re-use network parameters from the given cluster.
  148. :returns: JSON representation of the created cluster
  149. :http: * 200 (OK)
  150. * 404 (Cluster or release not found.)
  151. """
  152. base_release = self.get_object_or_404(objects.Release, release_id)
  153. orig_cluster = self.get_object_or_404(objects.Cluster, cluster_id)
  154. orig_release = orig_cluster.release
  155. network_metadata = self.merge_network_roles(
  156. base_release.network_roles_metadata,
  157. orig_release.network_roles_metadata)
  158. data = dict(base_release)
  159. data['network_roles_metadata'] = network_metadata
  160. data['name'] = '{0} Upgrade ({1})'.format(
  161. base_release.name, orig_release.id)
  162. deployment_tasks = objects.Release.get_deployment_tasks(base_release)
  163. data['deployment_tasks'] = deployment_tasks
  164. del data['id']
  165. new_release = objects.Release.create(data)
  166. return objects.Release.to_dict(new_release)