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 7.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  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 kuryr_kubernetes import clients
  18. from kuryr_kubernetes import constants
  19. from kuryr_kubernetes.controller.drivers import base as drivers
  20. from kuryr_kubernetes import exceptions
  21. from kuryr_kubernetes.handlers import k8s_base
  22. from kuryr_kubernetes import utils
  23. from neutronclient.common import exceptions as n_exc
  24. LOG = logging.getLogger(__name__)
  25. namespace_handler_caching_opts = [
  26. oslo_cfg.BoolOpt('caching', default=True),
  27. oslo_cfg.IntOpt('cache_time', default=120),
  28. ]
  29. oslo_cfg.CONF.register_opts(namespace_handler_caching_opts,
  30. "namespace_handler_caching")
  31. cache.configure(oslo_cfg.CONF)
  32. namespace_handler_cache_region = cache.create_region()
  33. MEMOIZE = cache.get_memoization_decorator(
  34. oslo_cfg.CONF, namespace_handler_cache_region, "namespace_handler_caching")
  35. cache.configure_cache_region(oslo_cfg.CONF, namespace_handler_cache_region)
  36. class NamespaceHandler(k8s_base.ResourceEventHandler):
  37. OBJECT_KIND = constants.K8S_OBJ_NAMESPACE
  38. OBJECT_WATCH_PATH = "%s/%s" % (constants.K8S_API_BASE, "namespaces")
  39. def __init__(self):
  40. super(NamespaceHandler, self).__init__()
  41. self._drv_project = drivers.NamespaceProjectDriver.get_instance()
  42. self._drv_subnets = drivers.PodSubnetsDriver.get_instance()
  43. self._drv_sg = drivers.PodSecurityGroupsDriver.get_instance()
  44. self._drv_vif_pool = drivers.VIFPoolDriver.get_instance(
  45. specific_driver='multi_pool')
  46. self._drv_vif_pool.set_vif_driver()
  47. def on_present(self, namespace):
  48. ns_name = namespace['metadata']['name']
  49. project_id = self._drv_project.get_project(namespace)
  50. net_crd_id = self._get_net_crd_id(namespace)
  51. if net_crd_id:
  52. LOG.debug("CRD existing at the new namespace")
  53. return
  54. LOG.debug("Creating network resources for namespace: %s", ns_name)
  55. net_crd_spec = self._drv_subnets.create_namespace_network(ns_name,
  56. project_id)
  57. try:
  58. net_crd_sg = self._drv_sg.create_namespace_sg(ns_name, project_id,
  59. net_crd_spec)
  60. except n_exc.NeutronClientException:
  61. LOG.exception("Error creating security group for the namespace. "
  62. "Rolling back created network resources.")
  63. self._drv_subnets.rollback_network_resources(net_crd_spec, ns_name)
  64. raise
  65. if net_crd_sg:
  66. net_crd_spec.update(net_crd_sg)
  67. else:
  68. LOG.debug("No SG created for the namespace. Namespace isolation "
  69. "will not be enforced.")
  70. # create CRD resource for the network
  71. try:
  72. net_crd = self._add_kuryrnet_crd(ns_name, net_crd_spec)
  73. self._set_net_crd(namespace, net_crd)
  74. except exceptions.K8sClientException:
  75. LOG.exception("Kuryrnet CRD could not be added. Rolling back "
  76. "network resources created for the namespace.")
  77. self._drv_subnets.rollback_network_resources(net_crd_spec, ns_name)
  78. self._drv_sg.delete_sg(net_crd_sg['sgId'])
  79. def on_deleted(self, namespace):
  80. LOG.debug("Deleting namespace: %s", namespace)
  81. net_crd_id = self._get_net_crd_id(namespace)
  82. if not net_crd_id:
  83. LOG.warning("There is no CRD annotated at the namespace %s",
  84. namespace)
  85. return
  86. net_crd = self._get_net_crd(net_crd_id)
  87. self._drv_vif_pool.delete_network_pools(net_crd['spec']['netId'])
  88. self._drv_subnets.delete_namespace_subnet(net_crd)
  89. sg_id = net_crd['spec'].get('sgId')
  90. if sg_id:
  91. self._drv_sg.delete_sg(sg_id)
  92. else:
  93. LOG.debug("There is no security group associated with the "
  94. "namespace to be deleted")
  95. self._del_kuryrnet_crd(net_crd_id)
  96. def is_ready(self, quota):
  97. if not utils.has_kuryr_crd(constants.K8S_API_CRD_KURYRNETS):
  98. return False
  99. return self._check_quota(quota)
  100. @MEMOIZE
  101. def _check_quota(self, quota):
  102. neutron = clients.get_neutron_client()
  103. resources = {'subnet': neutron.list_subnets,
  104. 'network': neutron.list_networks,
  105. 'security_group': neutron.list_security_groups}
  106. for resource, neutron_func in resources.items():
  107. resource_quota = quota[resource]
  108. resource_name = resource + 's'
  109. if utils.has_limit(resource_quota):
  110. if not utils.is_available(resource_name, resource_quota,
  111. neutron_func):
  112. return False
  113. return True
  114. def _get_net_crd_id(self, namespace):
  115. try:
  116. annotations = namespace['metadata']['annotations']
  117. net_crd_id = annotations[constants.K8S_ANNOTATION_NET_CRD]
  118. except KeyError:
  119. return None
  120. return net_crd_id
  121. def _get_net_crd(self, net_crd_id):
  122. k8s = clients.get_kubernetes_client()
  123. try:
  124. kuryrnet_crd = k8s.get('%s/kuryrnets/%s' % (constants.K8S_API_CRD,
  125. net_crd_id))
  126. except exceptions.K8sClientException:
  127. LOG.exception("Kubernetes Client Exception.")
  128. raise
  129. return kuryrnet_crd
  130. def _set_net_crd(self, namespace, net_crd):
  131. LOG.debug("Setting CRD annotations: %s", net_crd)
  132. k8s = clients.get_kubernetes_client()
  133. k8s.annotate(namespace['metadata']['selfLink'],
  134. {constants.K8S_ANNOTATION_NET_CRD:
  135. net_crd['metadata']['name']},
  136. resource_version=namespace['metadata']['resourceVersion'])
  137. def _add_kuryrnet_crd(self, namespace, net_crd_spec):
  138. kubernetes = clients.get_kubernetes_client()
  139. net_crd_name = "ns-" + namespace
  140. spec = {k: v for k, v in net_crd_spec.items()}
  141. net_crd = {
  142. 'apiVersion': 'openstack.org/v1',
  143. 'kind': 'KuryrNet',
  144. 'metadata': {
  145. 'name': net_crd_name,
  146. 'annotations': {
  147. 'namespaceName': namespace,
  148. }
  149. },
  150. 'spec': spec,
  151. }
  152. try:
  153. kubernetes.post('%s/kuryrnets' % constants.K8S_API_CRD, net_crd)
  154. except exceptions.K8sClientException:
  155. LOG.exception("Kubernetes Client Exception creating kuryrnet "
  156. "CRD.")
  157. raise
  158. return net_crd
  159. def _del_kuryrnet_crd(self, net_crd_name):
  160. kubernetes = clients.get_kubernetes_client()
  161. try:
  162. kubernetes.delete('%s/kuryrnets/%s' % (constants.K8S_API_CRD,
  163. net_crd_name))
  164. except exceptions.K8sClientException:
  165. LOG.exception("Kubernetes Client Exception deleting kuryrnet "
  166. "CRD.")
  167. raise