# Copyright (c) 2016 Mirantis, Inc. # All Rights Reserved. # # 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. import abc import six import traceback from kuryr.lib._i18n import _ from oslo_log import log as logging from oslo_serialization import jsonutils from kuryr_kubernetes import constants as k_const from kuryr_kubernetes import exceptions as k_exc LOG = logging.getLogger(__name__) _CNI_TIMEOUT = 60 class CNIConfig(dict): def __init__(self, cfg): super(CNIConfig, self).__init__(cfg) for k, v in six.iteritems(self): if not k.startswith('_'): setattr(self, k, v) class CNIArgs(object): def __init__(self, value): for item in value.split(';'): k, v = item.split('=', 1) if not k.startswith('_'): setattr(self, k, v) class CNIParameters(object): def __init__(self, env, cfg): for k, v in six.iteritems(env): if k.startswith('CNI_'): setattr(self, k, v) self.config = CNIConfig(cfg) self.args = CNIArgs(self.CNI_ARGS) @six.add_metaclass(abc.ABCMeta) class CNIPlugin(object): @abc.abstractmethod def add(self, params): raise NotImplementedError() @abc.abstractmethod def delete(self, params): raise NotImplementedError() class CNIRunner(object): # TODO(ivc): extend SUPPORTED_VERSIONS and format output based on # requested params.CNI_VERSION and/or params.config.cniVersion VERSION = '0.3.0' SUPPORTED_VERSIONS = ['0.3.0'] def __init__(self, plugin): self._plugin = plugin def run(self, env, fin, fout): try: params = CNIParameters(env, jsonutils.load(fin)) if params.CNI_COMMAND == 'ADD': vif = self._plugin.add(params) self._write_vif(fout, vif) elif params.CNI_COMMAND == 'DEL': self._plugin.delete(params) elif params.CNI_COMMAND == 'VERSION': self._write_version(fout) else: raise k_exc.CNIError(_("unknown CNI_COMMAND: %s") % params.CNI_COMMAND) except Exception as ex: # LOG.exception self._write_exception(fout, str(ex)) return 1 def _write_dict(self, fout, dct): output = {'cniVersion': self.VERSION} output.update(dct) LOG.debug("CNI output: %s", output) jsonutils.dump(output, fout, sort_keys=True) def _write_exception(self, fout, msg): self._write_dict(fout, { 'msg': msg, 'code': k_const.CNI_EXCEPTION_CODE, 'details': traceback.format_exc(), }) def _write_version(self, fout): self._write_dict(fout, {'supportedVersions': self.SUPPORTED_VERSIONS}) def _write_vif(self, fout, vif): result = {} nameservers = [] for subnet in vif.network.subnets.objects: nameservers.extend(subnet.dns) ip = subnet.ips.objects[0].address cni_ip = result.setdefault("ip%s" % ip.version, {}) cni_ip['ip'] = "%s/%s" % (ip, subnet.cidr.prefixlen) if subnet.gateway: cni_ip['gateway'] = str(subnet.gateway) if subnet.routes.objects: cni_ip['routes'] = [ {'dst': str(route.cidr), 'gw': str(route.gateway)} for route in subnet.routes.objects] if nameservers: result['dns'] = {'nameservers': nameservers} self._write_dict(fout, result)