Kubernetes integration with OpenStack networking
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.

namespace.py 9.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. # Copyright 2018 Red Hat, Inc.
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. from oslo_cache import core as cache
  15. from oslo_config import cfg as oslo_cfg
  16. from oslo_log import log as logging
  17. from oslo_serialization import jsonutils
  18. from kuryr_kubernetes import clients
  19. from kuryr_kubernetes import constants
  20. from kuryr_kubernetes.controller.drivers import base as drivers
  21. from kuryr_kubernetes.controller.drivers import utils as drivers_utils
  22. from kuryr_kubernetes import exceptions
  23. from kuryr_kubernetes.handlers import k8s_base
  24. from kuryr_kubernetes import utils
  25. from neutronclient.common import exceptions as n_exc
  26. LOG = logging.getLogger(__name__)
  27. namespace_handler_caching_opts = [
  28. oslo_cfg.BoolOpt('caching', default=True),
  29. oslo_cfg.IntOpt('cache_time', default=120),
  30. ]
  31. oslo_cfg.CONF.register_opts(namespace_handler_caching_opts,
  32. "namespace_handler_caching")
  33. cache.configure(oslo_cfg.CONF)
  34. namespace_handler_cache_region = cache.create_region()
  35. MEMOIZE = cache.get_memoization_decorator(
  36. oslo_cfg.CONF, namespace_handler_cache_region, "namespace_handler_caching")
  37. cache.configure_cache_region(oslo_cfg.CONF, namespace_handler_cache_region)
  38. class NamespaceHandler(k8s_base.ResourceEventHandler):
  39. OBJECT_KIND = constants.K8S_OBJ_NAMESPACE
  40. OBJECT_WATCH_PATH = "%s/%s" % (constants.K8S_API_BASE, "namespaces")
  41. def __init__(self):
  42. super(NamespaceHandler, self).__init__()
  43. self._drv_project = drivers.NamespaceProjectDriver.get_instance()
  44. self._drv_subnets = drivers.PodSubnetsDriver.get_instance()
  45. self._drv_sg = drivers.PodSecurityGroupsDriver.get_instance()
  46. self._drv_vif_pool = drivers.VIFPoolDriver.get_instance(
  47. specific_driver='multi_pool')
  48. self._drv_vif_pool.set_vif_driver()
  49. def on_present(self, namespace):
  50. current_namespace_labels = namespace['metadata'].get('labels')
  51. previous_namespace_labels = drivers_utils.get_annotated_labels(
  52. namespace, constants.K8S_ANNOTATION_NAMESPACE_LABEL)
  53. LOG.debug("Got previous namespace labels from annotation: %r",
  54. previous_namespace_labels)
  55. if (previous_namespace_labels and
  56. current_namespace_labels != previous_namespace_labels):
  57. self._drv_sg.update_namespace_sg_rules(namespace)
  58. self._set_namespace_labels(namespace, current_namespace_labels)
  59. ns_name = namespace['metadata']['name']
  60. project_id = self._drv_project.get_project(namespace)
  61. net_crd_id = self._get_net_crd_id(namespace)
  62. if net_crd_id:
  63. LOG.debug("CRD existing at the new namespace")
  64. return
  65. LOG.debug("Creating network resources for namespace: %s", ns_name)
  66. net_crd_spec = self._drv_subnets.create_namespace_network(ns_name,
  67. project_id)
  68. try:
  69. net_crd_sg = self._drv_sg.create_namespace_sg(ns_name, project_id,
  70. net_crd_spec)
  71. except n_exc.NeutronClientException:
  72. LOG.exception("Error creating security group for the namespace. "
  73. "Rolling back created network resources.")
  74. self._drv_subnets.rollback_network_resources(net_crd_spec, ns_name)
  75. raise
  76. if net_crd_sg:
  77. net_crd_spec.update(net_crd_sg)
  78. else:
  79. LOG.debug("No SG created for the namespace. Namespace isolation "
  80. "will not be enforced.")
  81. # create CRD resource for the network
  82. try:
  83. net_crd = self._add_kuryrnet_crd(ns_name, net_crd_spec)
  84. self._set_net_crd(namespace, net_crd)
  85. self._drv_sg.create_namespace_sg_rules(namespace)
  86. self._set_namespace_labels(namespace, current_namespace_labels)
  87. except exceptions.K8sClientException:
  88. LOG.exception("Kuryrnet CRD could not be added. Rolling back "
  89. "network resources created for the namespace.")
  90. self._drv_subnets.rollback_network_resources(net_crd_spec, ns_name)
  91. self._drv_sg.delete_sg(net_crd_sg['sgId'])
  92. def on_deleted(self, namespace):
  93. LOG.debug("Deleting namespace: %s", namespace)
  94. net_crd_id = self._get_net_crd_id(namespace)
  95. if not net_crd_id:
  96. LOG.warning("There is no CRD annotated at the namespace %s",
  97. namespace)
  98. return
  99. net_crd = self._get_net_crd(net_crd_id)
  100. self._drv_vif_pool.delete_network_pools(net_crd['spec']['netId'])
  101. self._drv_subnets.delete_namespace_subnet(net_crd)
  102. sg_id = net_crd['spec'].get('sgId')
  103. if sg_id:
  104. self._drv_sg.delete_sg(sg_id)
  105. else:
  106. LOG.debug("There is no security group associated with the "
  107. "namespace to be deleted")
  108. self._del_kuryrnet_crd(net_crd_id)
  109. self._drv_sg.delete_namespace_sg_rules(namespace)
  110. def is_ready(self, quota):
  111. if not utils.has_kuryr_crd(constants.K8S_API_CRD_KURYRNETS):
  112. return False
  113. return self._check_quota(quota)
  114. @MEMOIZE
  115. def _check_quota(self, quota):
  116. neutron = clients.get_neutron_client()
  117. resources = {'subnet': neutron.list_subnets,
  118. 'network': neutron.list_networks,
  119. 'security_group': neutron.list_security_groups}
  120. for resource, neutron_func in resources.items():
  121. resource_quota = quota[resource]
  122. resource_name = resource + 's'
  123. if utils.has_limit(resource_quota):
  124. if not utils.is_available(resource_name, resource_quota,
  125. neutron_func):
  126. return False
  127. return True
  128. def _get_net_crd_id(self, namespace):
  129. try:
  130. annotations = namespace['metadata']['annotations']
  131. net_crd_id = annotations[constants.K8S_ANNOTATION_NET_CRD]
  132. except KeyError:
  133. return None
  134. return net_crd_id
  135. def _get_net_crd(self, net_crd_id):
  136. k8s = clients.get_kubernetes_client()
  137. try:
  138. kuryrnet_crd = k8s.get('%s/kuryrnets/%s' % (constants.K8S_API_CRD,
  139. net_crd_id))
  140. except exceptions.K8sClientException:
  141. LOG.exception("Kubernetes Client Exception.")
  142. raise
  143. return kuryrnet_crd
  144. def _set_net_crd(self, namespace, net_crd):
  145. LOG.debug("Setting CRD annotations: %s", net_crd)
  146. k8s = clients.get_kubernetes_client()
  147. k8s.annotate(namespace['metadata']['selfLink'],
  148. {constants.K8S_ANNOTATION_NET_CRD:
  149. net_crd['metadata']['name']},
  150. resource_version=namespace['metadata']['resourceVersion'])
  151. def _add_kuryrnet_crd(self, namespace, net_crd_spec):
  152. kubernetes = clients.get_kubernetes_client()
  153. net_crd_name = "ns-" + namespace
  154. spec = {k: v for k, v in net_crd_spec.items()}
  155. net_crd = {
  156. 'apiVersion': 'openstack.org/v1',
  157. 'kind': 'KuryrNet',
  158. 'metadata': {
  159. 'name': net_crd_name,
  160. 'annotations': {
  161. 'namespaceName': namespace,
  162. }
  163. },
  164. 'spec': spec,
  165. }
  166. try:
  167. kubernetes.post('%s/kuryrnets' % constants.K8S_API_CRD, net_crd)
  168. except exceptions.K8sClientException:
  169. LOG.exception("Kubernetes Client Exception creating kuryrnet "
  170. "CRD.")
  171. raise
  172. return net_crd
  173. def _del_kuryrnet_crd(self, net_crd_name):
  174. kubernetes = clients.get_kubernetes_client()
  175. try:
  176. kubernetes.delete('%s/kuryrnets/%s' % (constants.K8S_API_CRD,
  177. net_crd_name))
  178. except exceptions.K8sClientException:
  179. LOG.exception("Kubernetes Client Exception deleting kuryrnet "
  180. "CRD.")
  181. raise
  182. def _set_namespace_labels(self, namespace, labels):
  183. if not labels:
  184. LOG.debug("Removing Label annotation: %r", labels)
  185. annotation = None
  186. else:
  187. annotation = jsonutils.dumps(labels, sort_keys=True)
  188. LOG.debug("Setting Labels annotation: %r", annotation)
  189. k8s = clients.get_kubernetes_client()
  190. k8s.annotate(namespace['metadata']['selfLink'],
  191. {constants.K8S_ANNOTATION_NAMESPACE_LABEL: annotation},
  192. resource_version=namespace['metadata']['resourceVersion'])