# Copyright 2018 Red Hat, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from oslo_log import log as logging from kuryr_kubernetes import clients from kuryr_kubernetes import constants from kuryr_kubernetes.controller.drivers import base as drivers from kuryr_kubernetes import exceptions from kuryr_kubernetes.handlers import k8s_base from kuryr_kubernetes import utils LOG = logging.getLogger(__name__) class NamespaceHandler(k8s_base.ResourceEventHandler): OBJECT_KIND = constants.K8S_OBJ_NAMESPACE OBJECT_WATCH_PATH = constants.K8S_API_NAMESPACES def __init__(self): super(NamespaceHandler, self).__init__() self._drv_project = drivers.NamespaceProjectDriver.get_instance() self._upgrade_crds() def _upgrade_crds(self): k8s = clients.get_kubernetes_client() try: net_crds = k8s.get(constants.K8S_API_CRD_KURYRNETS) namespaces = k8s.get(constants.K8S_API_NAMESPACES) except exceptions.K8sResourceNotFound: return except exceptions.K8sClientException: LOG.warning("Error retriving namespace information") raise ns_dict = {'ns-' + ns['metadata']['name']: ns for ns in namespaces.get('items')} for net_crd in net_crds.get('items'): try: ns = ns_dict[net_crd['metadata']['name']] except KeyError: # Note(ltomasbo): The CRD does not have an associated # namespace. It must be deleted LOG.debug('No namespace associated, deleting kuryrnet crd: ' '%s', net_crd) else: try: ns_net_annotations = ns['metadata']['annotations'][ constants.K8S_ANNOTATION_NET_CRD] except KeyError: LOG.debug('Namespace associated is not annotated: %s', ns) else: LOG.debug('Removing annotation: %', ns_net_annotations) k8s.remove_annotations(ns['metadata']['selfLink'], constants.K8S_ANNOTATION_NET_CRD) try: k8s.delete(utils.get_res_link(net_crd)) except exceptions.K8sResourceNotFound: LOG.debug('Kuryrnet object already deleted: %s', net_crd) def on_present(self, namespace): ns_labels = namespace['metadata'].get('labels', {}) ns_name = namespace['metadata']['name'] kns_crd = self._get_kns_crd(ns_name) if kns_crd: LOG.debug("Previous CRD existing at the new namespace.") self._update_labels(kns_crd, ns_labels) return try: self._add_kuryrnetwork_crd(ns_name, ns_labels) except exceptions.K8sClientException: LOG.exception("Kuryrnetwork CRD creation failed.") raise exceptions.ResourceNotReady(namespace) def _update_labels(self, kns_crd, ns_labels): kns_status = kns_crd.get('status') if kns_status: kns_crd_labels = kns_crd['status'].get('nsLabels', {}) if kns_crd_labels == ns_labels: # Labels are already up to date, nothing to do return kubernetes = clients.get_kubernetes_client() LOG.debug('Patching KuryrNetwork CRD %s', kns_crd) try: kubernetes.patch_crd('spec', utils.get_res_link(kns_crd), {'nsLabels': ns_labels}) except exceptions.K8sResourceNotFound: LOG.debug('KuryrNetwork CRD not found %s', kns_crd) except exceptions.K8sClientException: LOG.exception('Error updating kuryrnetwork CRD %s', kns_crd) raise def _get_kns_crd(self, namespace): k8s = clients.get_kubernetes_client() try: kuryrnetwork_crd = k8s.get('{}/{}/kuryrnetworks/{}'.format( constants.K8S_API_CRD_NAMESPACES, namespace, namespace)) except exceptions.K8sResourceNotFound: return None except exceptions.K8sClientException: LOG.exception("Kubernetes Client Exception.") raise return kuryrnetwork_crd def _add_kuryrnetwork_crd(self, namespace, ns_labels): project_id = self._drv_project.get_project(namespace) kubernetes = clients.get_kubernetes_client() kns_crd = { 'apiVersion': 'openstack.org/v1', 'kind': 'KuryrNetwork', 'metadata': { 'name': namespace, 'finalizers': [constants.KURYRNETWORK_FINALIZER], }, 'spec': { 'nsName': namespace, 'projectId': project_id, 'nsLabels': ns_labels, } } try: kubernetes.post('{}/{}/kuryrnetworks'.format( constants.K8S_API_CRD_NAMESPACES, namespace), kns_crd) except exceptions.K8sClientException: LOG.exception("Kubernetes Client Exception creating kuryrnetwork " "CRD.") raise def is_ready(self, quota): if not (utils.has_kuryr_crd(constants.K8S_API_CRD_KURYRNETS) and self._check_quota(quota)): LOG.error('Marking NamespaceHandler as not ready.') return False return True def _check_quota(self, quota): resources = ('subnets', 'networks', 'security_groups') for resource in resources: resource_quota = quota[resource] if utils.has_limit(resource_quota): if not utils.is_available(resource, resource_quota): return False return True