From 5a0478117bcff1541fd7d4ab0ab51c70b130c43d Mon Sep 17 00:00:00 2001 From: Ghanshyam Mann Date: Fri, 28 Jun 2024 14:41:52 -0700 Subject: [PATCH] Retire kuryr-kubernetes: remove repo content kuryr-kubernetes repository are retiring and this commit remove the content of this repo. Depends-On: https://review.opendev.org/c/openstack/project-config/+/923072 [1] https://review.opendev.org/c/openstack/governance/+/922507 Change-Id: Ied35a7d48e569e8dcf6708cf0facc847a72d16e6 --- .coveragerc | 17 - .dockerignore | 3 - .gitignore | 77 - .pre-commit-config.yaml | 6 - .stestr.conf | 3 - .zuul.d/base.yaml | 259 -- .zuul.d/k8s-np-e2e.yaml | 144 - .zuul.d/nodesets.yaml | 63 - .zuul.d/project.yaml | 45 - .zuul.d/tempest-jobs.yaml | 239 -- .zuul.d/tempest-multinode-jobs.yaml | 168 -- CONTRIBUTING.rst | 19 - HACKING.rst | 5 - LICENSE | 176 -- README.rst | 41 +- babel.cfg | 2 - cni.Dockerfile | 42 - cni_ds_init | 22 - contrib/devstack-heat/.gitignore | 4 - contrib/devstack-heat/README.rst | 88 - contrib/devstack-heat/devstack_heat.py | 218 -- .../hot/devstack_heat_template.yml | 121 - contrib/devstack-heat/hot/distro_deps.sh | 19 - .../hot/networking_deployment.yaml | 80 - contrib/devstack-heat/hot/node.yaml | 206 -- contrib/devstack-heat/hot/parameters.yml | 10 - contrib/kubectl_plugins/README.rst | 28 - .../kubectl_kuryr_plugin_1080.gif | Bin 1865381 -> 0 bytes contrib/kubectl_plugins/kuryr/kuryr | 186 -- contrib/kubectl_plugins/kuryr/plugin.yaml | 15 - contrib/pools-management/README.rst | 321 -- contrib/pools-management/subports.py | 187 -- contrib/regenerate_controller_pod.sh | 17 - contrib/regenerate_pod_resources_api.sh | 88 - contrib/sctp_client.py | 31 - contrib/testing/container/Dockerfile | 3 - contrib/testing/container/build.sh | 43 - contrib/testing/container/hostname.c | 129 - contrib/vagrant/README.rst | 99 - contrib/vagrant/Vagrantfile | 50 - contrib/vagrant/config/kuryr_rc | 4 - contrib/vagrant/devstack.sh | 62 - contrib/vagrant/vagrant.sh | 25 - controller.Dockerfile | 32 - devstack/files/debs/kuryr-kubernetes | 1 - devstack/files/rpms/kuryr-kubernetes | 1 - devstack/lib/kubernetes | 245 -- devstack/lib/kuryr_kubernetes | 1641 ----------- devstack/local.conf.ovs.sample | 225 -- .../local.conf.pod-in-vm.overcloud.sample | 71 - ...local.conf.pod-in-vm.undercloud.ovn.sample | 42 - .../local.conf.pod-in-vm.undercloud.sample | 56 - devstack/local.conf.sample | 210 -- devstack/local.conf.worker.sample | 60 - devstack/plugin.sh | 174 -- devstack/settings | 101 - doc/images/controller_pipeline.png | Bin 156783 -> 0 bytes doc/images/create_network_policy_flow.svg | 2 - doc/images/external_traffic_to_l7_router.svg | 4 - doc/images/fuxi_k8s_components.png | Bin 28274 -> 0 bytes doc/images/kuryr_k8s_components.png | Bin 64078 -> 0 bytes doc/images/kuryr_k8s_components.svg | 367 --- .../kuryr_k8s_ingress_ctrl_flow_diagram.svg | 4 - .../kuryr_k8s_ingress_sw_components.svg | 4 - doc/images/kuryr_k8s_ocp_route_ctrl_sw.svg | 4 - ...7_routing_and_user_lb_neutron_entities.svg | 4 - doc/images/lbaas_translation.svg | 2569 ----------------- doc/images/net-policy.svg | 687 ----- doc/images/pod_creation_flow_daemon.png | Bin 220449 -> 0 bytes doc/images/pod_creation_flow_daemon.svg | 1496 ---------- doc/images/service_creation_diagram.png | Bin 92990 -> 0 bytes doc/images/service_creation_diagram.svg | 1 - .../update_network_policy_on_pod_creation.svg | 2 - doc/images/vif_handler_drivers_design.png | Bin 36189 -> 0 bytes doc/requirements.txt | 6 - doc/source/conf.py | 82 - doc/source/contributor/contributing.rst | 82 - doc/source/contributor/index.rst | 8 - .../devref/annotation_project_driver.rst | 98 - doc/source/devref/health_manager.rst | 84 - doc/source/devref/high_availability.rst | 143 - doc/source/devref/index.rst | 56 - doc/source/devref/kuryr_kubernetes_design.rst | 328 --- .../devref/kuryr_kubernetes_versions.rst | 43 - doc/source/devref/network_policy.rst | 521 ---- doc/source/devref/port_manager.rst | 195 -- doc/source/devref/service_support.rst | 157 - .../devref/updating_pod_resources_api.rst | 137 - .../devref/vif_handler_drivers_design.rst | 157 - doc/source/index.rst | 48 - doc/source/installation/containerized.rst | 187 -- .../installation/default_configuration.rst | 91 - doc/source/installation/devstack/basic.rst | 186 -- .../installation/devstack/containerized.rst | 83 - doc/source/installation/devstack/index.rst | 42 - .../installation/devstack/nested-dpdk.rst | 222 -- .../installation/devstack/nested-macvlan.rst | 53 - .../installation/devstack/nested-vlan.rst | 94 - .../installation/devstack/ovn-octavia.rst | 105 - .../installation/devstack/ovn_support.rst | 190 -- .../installation/devstack/ports-pool.rst | 40 - doc/source/installation/https_kubernetes.rst | 29 - doc/source/installation/index.rst | 49 - doc/source/installation/ipv6.rst | 265 -- doc/source/installation/listener_timeouts.rst | 68 - doc/source/installation/manual.rst | 332 --- .../installation/multi_vif_with_npwg_spec.rst | 95 - doc/source/installation/multiple_tenants.rst | 98 - doc/source/installation/network_namespace.rst | 164 -- doc/source/installation/network_policy.rst | 350 --- doc/source/installation/ports-pool.rst | 177 -- doc/source/installation/services.rst | 787 ----- .../installation/testing_connectivity.rst | 240 -- .../testing_nested_connectivity.rst | 57 - .../installation/testing_sctp_services.rst | 209 -- .../installation/testing_udp_services.rst | 177 -- doc/source/installation/trunk_ports.rst | 53 - doc/source/installation/upgrades.rst | 91 - doc/source/nested_vlan_mode.rst | 66 - doc/source/readme.rst | 1 - doc/source/specs/pike/contrail_support.rst | 80 - doc/source/specs/pike/fuxi_kubernetes.rst | 176 -- doc/source/specs/queens/network_policy.rst | 613 ---- doc/source/specs/rocky/npwg_spec_support.rst | 190 -- doc/source/specs/stein/vhostuser.rst | 197 -- doc/source/usage.rst | 7 - etc/cni/net.d/10-kuryr.conflist | 11 - etc/cni/net.d/kuryr.conflist.template | 11 - etc/oslo-config-generator/kuryr.conf | 4 - .../kuryr_crds/kuryrloadbalancer.yaml | 241 -- kubernetes_crds/kuryr_crds/kuryrnetwork.yaml | 59 - .../kuryr_crds/kuryrnetworkpolicy.yaml | 158 - kubernetes_crds/kuryr_crds/kuryrport.yaml | 54 - .../network_attachment_definition_crd.yaml | 26 - kuryr_cni/README | 2 - kuryr_cni/go.mod | 9 - kuryr_cni/go.sum | 89 - kuryr_cni/hack/build-go.sh | 9 - kuryr_cni/hack/update-deps.sh | 3 - kuryr_cni/pkg/main.go | 235 -- kuryr_cni/pkg/ovo.go | 120 - .../containernetworking/cni/LICENSE | 202 -- .../containernetworking/cni/pkg/skel/skel.go | 296 -- .../cni/pkg/types/020/types.go | 126 - .../containernetworking/cni/pkg/types/args.go | 112 - .../cni/pkg/types/current/types.go | 276 -- .../cni/pkg/types/types.go | 207 -- .../cni/pkg/utils/utils.go | 84 - .../cni/pkg/version/conf.go | 37 - .../cni/pkg/version/plugin.go | 144 - .../cni/pkg/version/reconcile.go | 49 - .../cni/pkg/version/version.go | 83 - kuryr_cni/vendor/modules.txt | 12 - kuryr_kubernetes/__init__.py | 0 kuryr_kubernetes/clients.py | 116 - kuryr_kubernetes/cmd/__init__.py | 28 - kuryr_kubernetes/cmd/cni.py | 22 - kuryr_kubernetes/cmd/daemon.py | 22 - kuryr_kubernetes/cmd/eventlet/__init__.py | 18 - kuryr_kubernetes/cmd/eventlet/controller.py | 22 - kuryr_kubernetes/cmd/sanity/__init__.py | 0 kuryr_kubernetes/cmd/sanity/checks.py | 85 - kuryr_kubernetes/cmd/sanity_checks.py | 105 - kuryr_kubernetes/cmd/status.py | 204 -- kuryr_kubernetes/cni/__init__.py | 0 kuryr_kubernetes/cni/api.py | 169 -- kuryr_kubernetes/cni/binding/__init__.py | 0 kuryr_kubernetes/cni/binding/base.py | 166 -- kuryr_kubernetes/cni/binding/bridge.py | 115 - kuryr_kubernetes/cni/binding/dpdk.py | 196 -- kuryr_kubernetes/cni/binding/nested.py | 239 -- kuryr_kubernetes/cni/binding/vhostuser.py | 131 - kuryr_kubernetes/cni/daemon/__init__.py | 0 kuryr_kubernetes/cni/daemon/service.py | 375 --- .../cni/daemon/watcher_service.py | 94 - kuryr_kubernetes/cni/handlers.py | 116 - kuryr_kubernetes/cni/health.py | 140 - kuryr_kubernetes/cni/main.py | 69 - kuryr_kubernetes/cni/plugins/__init__.py | 0 kuryr_kubernetes/cni/plugins/base.py | 28 - .../cni/plugins/k8s_cni_registry.py | 246 -- kuryr_kubernetes/cni/prometheus_exporter.py | 71 - kuryr_kubernetes/cni/utils.py | 111 - kuryr_kubernetes/config.py | 352 --- kuryr_kubernetes/constants.py | 113 - kuryr_kubernetes/controller/__init__.py | 0 .../controller/drivers/__init__.py | 0 .../controller/drivers/annotation_project.py | 69 - kuryr_kubernetes/controller/drivers/base.py | 781 ----- .../controller/drivers/default_project.py | 77 - .../drivers/default_security_groups.py | 81 - .../controller/drivers/default_subnet.py | 54 - .../controller/drivers/lb_public_ip.py | 113 - .../controller/drivers/lbaasv2.py | 940 ------ .../controller/drivers/multi_vif.py | 117 - .../controller/drivers/namespace_subnet.py | 214 -- .../controller/drivers/nested_dpdk_vif.py | 75 - .../controller/drivers/nested_macvlan_vif.py | 179 -- .../controller/drivers/nested_vif.py | 75 - .../controller/drivers/nested_vlan_vif.py | 311 -- .../controller/drivers/network_policy.py | 792 ----- .../drivers/network_policy_security_groups.py | 437 --- .../controller/drivers/neutron_vif.py | 161 -- .../controller/drivers/node_subnets.py | 179 -- .../controller/drivers/public_ip.py | 178 -- kuryr_kubernetes/controller/drivers/utils.py | 770 ----- .../controller/drivers/vif_pool.py | 1289 --------- .../controller/handlers/__init__.py | 0 .../controller/handlers/kuryrnetwork.py | 213 -- .../handlers/kuryrnetwork_population.py | 97 - .../controller/handlers/kuryrnetworkpolicy.py | 319 -- .../controller/handlers/kuryrport.py | 403 --- kuryr_kubernetes/controller/handlers/lbaas.py | 447 --- .../controller/handlers/loadbalancer.py | 916 ------ .../controller/handlers/machine.py | 67 - .../controller/handlers/namespace.py | 147 - .../controller/handlers/pipeline.py | 72 - .../controller/handlers/pod_label.py | 124 - .../controller/handlers/policy.py | 61 - kuryr_kubernetes/controller/handlers/vif.py | 217 -- .../controller/managers/__init__.py | 0 .../controller/managers/health.py | 114 - kuryr_kubernetes/controller/managers/pool.py | 255 -- .../managers/prometheus_exporter.py | 192 -- kuryr_kubernetes/controller/service.py | 188 -- kuryr_kubernetes/exceptions.py | 197 -- kuryr_kubernetes/handlers/__init__.py | 0 kuryr_kubernetes/handlers/asynchronous.py | 123 - kuryr_kubernetes/handlers/base.py | 28 - kuryr_kubernetes/handlers/dispatch.py | 126 - kuryr_kubernetes/handlers/health.py | 57 - kuryr_kubernetes/handlers/k8s_base.py | 121 - kuryr_kubernetes/handlers/logging.py | 49 - kuryr_kubernetes/handlers/retry.py | 137 - kuryr_kubernetes/health.py | 76 - kuryr_kubernetes/k8s_client.py | 466 --- kuryr_kubernetes/linux_net_utils.py | 57 - kuryr_kubernetes/objects/__init__.py | 2 - kuryr_kubernetes/objects/base.py | 30 - kuryr_kubernetes/objects/fields.py | 26 - kuryr_kubernetes/objects/lbaas.py | 164 -- kuryr_kubernetes/objects/vif.py | 82 - kuryr_kubernetes/opts.py | 61 - kuryr_kubernetes/os_vif_plug_noop.py | 44 - kuryr_kubernetes/os_vif_util.py | 432 --- kuryr_kubernetes/tests/__init__.py | 0 kuryr_kubernetes/tests/base.py | 24 - kuryr_kubernetes/tests/fake.py | 182 -- kuryr_kubernetes/tests/unit/__init__.py | 0 kuryr_kubernetes/tests/unit/cmd/__init__.py | 0 .../tests/unit/cmd/eventlet/__init__.py | 0 .../unit/cmd/eventlet/test_controller.py | 34 - .../tests/unit/cmd/test_daemon.py | 27 - .../tests/unit/cmd/test_status.py | 148 - kuryr_kubernetes/tests/unit/cni/__init__.py | 0 .../tests/unit/cni/plugins/__init__.py | 0 .../unit/cni/plugins/test_k8s_cni_registry.py | 258 -- kuryr_kubernetes/tests/unit/cni/test_api.py | 102 - .../tests/unit/cni/test_binding.py | 383 --- .../tests/unit/cni/test_handlers.py | 68 - .../tests/unit/cni/test_health.py | 127 - kuryr_kubernetes/tests/unit/cni/test_main.py | 43 - .../tests/unit/cni/test_service.py | 109 - kuryr_kubernetes/tests/unit/cni/test_utils.py | 34 - .../tests/unit/controller/__init__.py | 0 .../tests/unit/controller/drivers/__init__.py | 0 .../drivers/test_annotation_project.py | 122 - .../unit/controller/drivers/test_base.py | 114 - .../drivers/test_default_project.py | 89 - .../drivers/test_default_security_groups.py | 69 - .../controller/drivers/test_default_subnet.py | 78 - .../controller/drivers/test_lb_public_ip.py | 390 --- .../unit/controller/drivers/test_lbaasv2.py | 1602 ---------- .../unit/controller/drivers/test_multi_vif.py | 206 -- .../drivers/test_namespace_subnet.py | 296 -- .../controller/drivers/test_nested_dpdk.py | 227 -- .../drivers/test_nested_macvlan_vif.py | 510 ---- .../controller/drivers/test_nested_vif.py | 129 - .../drivers/test_nested_vlan_vif.py | 651 ----- .../controller/drivers/test_network_policy.py | 644 ----- .../test_network_policy_security_groups.py | 616 ---- .../controller/drivers/test_neutron_vif.py | 346 --- .../controller/drivers/test_node_subnets.py | 418 --- .../unit/controller/drivers/test_public_ip.py | 170 -- .../unit/controller/drivers/test_utils.py | 172 -- .../unit/controller/drivers/test_vif_pool.py | 1878 ------------ .../unit/controller/handlers/__init__.py | 0 .../controller/handlers/test_fake_handler.py | 24 - .../controller/handlers/test_kuryrnetwork.py | 395 --- .../handlers/test_kuryrnetwork_population.py | 112 - .../handlers/test_kuryrnetworkpolicy.py | 97 - .../controller/handlers/test_kuryrport.py | 681 ----- .../unit/controller/handlers/test_lbaas.py | 448 --- .../controller/handlers/test_loadbalancer.py | 696 ----- .../unit/controller/handlers/test_machine.py | 84 - .../controller/handlers/test_namespace.py | 139 - .../unit/controller/handlers/test_pipeline.py | 61 - .../controller/handlers/test_pod_label.py | 126 - .../unit/controller/handlers/test_policy.py | 80 - .../unit/controller/handlers/test_vif.py | 282 -- .../unit/controller/managers/__init__.py | 0 .../unit/controller/managers/test_health.py | 225 -- .../unit/controller/managers/test_pool.py | 355 --- .../managers/test_prometheus_exporter.py | 186 -- .../tests/unit/controller/test_service.py | 59 - .../tests/unit/handlers/__init__.py | 0 .../tests/unit/handlers/test_asynchronous.py | 161 -- .../tests/unit/handlers/test_dispatch.py | 123 - .../tests/unit/handlers/test_health.py | 48 - .../tests/unit/handlers/test_k8s_base.py | 64 - .../tests/unit/handlers/test_logging.py | 65 - .../tests/unit/handlers/test_retry.py | 163 -- kuryr_kubernetes/tests/unit/kuryr_fixtures.py | 52 - kuryr_kubernetes/tests/unit/test_clients.py | 49 - .../tests/unit/test_k8s_client.py | 525 ---- .../tests/unit/test_linux_net_utils.py | 62 - kuryr_kubernetes/tests/unit/test_object.py | 73 - .../tests/unit/test_os_vif_plug_noop.py | 93 - .../tests/unit/test_os_vif_util.py | 658 ----- kuryr_kubernetes/tests/unit/test_utils.py | 577 ---- kuryr_kubernetes/tests/unit/test_watcher.py | 347 --- kuryr_kubernetes/utils.py | 846 ------ kuryr_kubernetes/version.py | 15 - kuryr_kubernetes/watcher.py | 239 -- playbooks/copy-crio-logs.yaml | 14 - playbooks/copy-k8s-logs.yaml | 14 - playbooks/e2e-tests.patch | 58 - playbooks/get_amphora_tarball.yaml | 6 - playbooks/run_k8s_e2e_tests.yaml | 106 - .../active-passive-ha-cfbda8e6b527b48e.yaml | 9 - .../notes/add-tagging-ce56231f58bf7ad0.yaml | 11 - ...shift-router-support-5f28108b39a2826f.yaml | 15 - ...-daemon-default-port-e968a83fa1bf30b5.yaml | 8 - ...ault-url-for-k8s-api-42c3b90183783291.yaml | 16 - .../cni-health-checks-d2b70f2f2551a9fc.yaml | 16 - .../containerization-2fba4dac5c097b19.yaml | 6 - .../notes/cri-o-support-ab7e810775754ea7.yaml | 5 - ...ate-handlers-caching-9cdfd772aba9a7ce.yaml | 9 - ...ecate-non-daemonized-6dd2154238b1628c.yaml | 18 - ...ecate-sg-mode-option-96824c33335cd74b.yaml | 10 - ...-worker-nodes-subnet-e452c84df5b5ed5c.yaml | 12 - .../deprecate_lbaasv2-a524aedf5d3a36bc.yaml | 15 - .../notes/drop-ingress-d78a7a9be8f20da1.yaml | 9 - .../notes/drop-py27-60f55b6bc1d082bc.yaml | 6 - ...lt-tolerable-watcher-24c51dbccabf5f17.yaml | 21 - .../golang-kuryr-cni-aab144831d4dc9dd.yaml | 14 - ...client-token-default-882ec49d1faffc29.yaml | 11 - .../notes/kuryr-daemon-f09075b9eb60388f.yaml | 10 - ...-ext-subnet-optional-99e73bfcbde96c22.yaml | 24 - ...e-handlers-pluggable-844475484771ffd6.yaml | 23 - ...timeout-configurable-8624ea32971c0cbe.yaml | 7 - ...imeouts-configurable-f563d85eg6c6fe6d.yaml | 10 - .../multivif-pools-1cec757c77a8c4f8.yaml | 34 - ...k-device-mtu-default-90faa7d685d4d0ec.yaml | 7 - .../network-namespace-2353f8013be398cd.yaml | 16 - ...es-without-selectors-fea06ab71a8a6f2a.yaml | 7 - .../notes/octavia-acls-7452d3406d75ea15.yaml | 9 - .../notes/oslo-caching-b11881cfb9dc306c.yaml | 22 - .../remove-non-daemon-836e4825384b1b88.yaml | 5 - ...sical-device-mapping-15d614b70c68fc73.yaml | 8 - .../remove-run-server-aca25a2d9d723dc7.yaml | 5 - .../notes/remove-sriov-5f44deb951264510.yaml | 8 - ...-worker_nodes_subnet-3e64510c0342e739.yaml | 5 - ...eusable-pool-drivers-00e7fdc1f4738441.yaml | 31 - .../started-using-reno-90dbe9da108ec5c4.yaml | 2 - .../notes/stein-upgrade-226c8e7b735701ee.yaml | 6 - ...namespace-annotation-18bc6eca729bff5e.yaml | 13 - ...vc-without-selectors-f36bab0883459b80.yaml | 8 - ...zuul-v3-native-gates-4ed7698667a7b92e.yaml | 7 - releasenotes/source/2023.1.rst | 6 - releasenotes/source/2023.2.rst | 6 - releasenotes/source/2024.1.rst | 6 - releasenotes/source/README.rst | 14 - releasenotes/source/_static/.placeholder | 0 releasenotes/source/_templates/.placeholder | 0 releasenotes/source/conf.py | 145 - releasenotes/source/index.rst | 25 - releasenotes/source/queens.rst | 6 - releasenotes/source/rocky.rst | 6 - releasenotes/source/stein.rst | 6 - releasenotes/source/train.rst | 6 - releasenotes/source/unreleased.rst | 5 - releasenotes/source/ussuri.rst | 6 - releasenotes/source/victoria.rst | 6 - releasenotes/source/wallaby.rst | 6 - releasenotes/source/xena.rst | 6 - releasenotes/source/yoga.rst | 6 - releasenotes/source/zed.rst | 6 - requirements.txt | 29 - setup.cfg | 140 - setup.py | 20 - test-requirements.txt | 15 - tools/gate/copy_crio_logs.sh | 29 - tools/gate/copy_k8s_logs.sh | 61 - tools/generate_config_file_samples.sh | 54 - tools/generate_k8s_resource_definitions.sh | 103 - tox.ini | 80 - 397 files changed, 8 insertions(+), 58333 deletions(-) delete mode 100644 .coveragerc delete mode 100644 .dockerignore delete mode 100644 .gitignore delete mode 100644 .pre-commit-config.yaml delete mode 100644 .stestr.conf delete mode 100644 .zuul.d/base.yaml delete mode 100644 .zuul.d/k8s-np-e2e.yaml delete mode 100644 .zuul.d/nodesets.yaml delete mode 100644 .zuul.d/project.yaml delete mode 100644 .zuul.d/tempest-jobs.yaml delete mode 100644 .zuul.d/tempest-multinode-jobs.yaml delete mode 100644 CONTRIBUTING.rst delete mode 100644 HACKING.rst delete mode 100644 LICENSE delete mode 100644 babel.cfg delete mode 100644 cni.Dockerfile delete mode 100755 cni_ds_init delete mode 100644 contrib/devstack-heat/.gitignore delete mode 100644 contrib/devstack-heat/README.rst delete mode 100755 contrib/devstack-heat/devstack_heat.py delete mode 100644 contrib/devstack-heat/hot/devstack_heat_template.yml delete mode 100644 contrib/devstack-heat/hot/distro_deps.sh delete mode 100644 contrib/devstack-heat/hot/networking_deployment.yaml delete mode 100644 contrib/devstack-heat/hot/node.yaml delete mode 100644 contrib/devstack-heat/hot/parameters.yml delete mode 100644 contrib/kubectl_plugins/README.rst delete mode 100644 contrib/kubectl_plugins/kubectl_kuryr_plugin_1080.gif delete mode 100755 contrib/kubectl_plugins/kuryr/kuryr delete mode 100644 contrib/kubectl_plugins/kuryr/plugin.yaml delete mode 100644 contrib/pools-management/README.rst delete mode 100644 contrib/pools-management/subports.py delete mode 100755 contrib/regenerate_controller_pod.sh delete mode 100755 contrib/regenerate_pod_resources_api.sh delete mode 100644 contrib/sctp_client.py delete mode 100644 contrib/testing/container/Dockerfile delete mode 100755 contrib/testing/container/build.sh delete mode 100644 contrib/testing/container/hostname.c delete mode 100644 contrib/vagrant/README.rst delete mode 100644 contrib/vagrant/Vagrantfile delete mode 100644 contrib/vagrant/config/kuryr_rc delete mode 100755 contrib/vagrant/devstack.sh delete mode 100755 contrib/vagrant/vagrant.sh delete mode 100644 controller.Dockerfile delete mode 100644 devstack/files/debs/kuryr-kubernetes delete mode 100644 devstack/files/rpms/kuryr-kubernetes delete mode 100644 devstack/lib/kubernetes delete mode 100644 devstack/lib/kuryr_kubernetes delete mode 100644 devstack/local.conf.ovs.sample delete mode 100644 devstack/local.conf.pod-in-vm.overcloud.sample delete mode 100644 devstack/local.conf.pod-in-vm.undercloud.ovn.sample delete mode 100644 devstack/local.conf.pod-in-vm.undercloud.sample delete mode 100644 devstack/local.conf.sample delete mode 100644 devstack/local.conf.worker.sample delete mode 100644 devstack/plugin.sh delete mode 100644 devstack/settings delete mode 100644 doc/images/controller_pipeline.png delete mode 100644 doc/images/create_network_policy_flow.svg delete mode 100644 doc/images/external_traffic_to_l7_router.svg delete mode 100644 doc/images/fuxi_k8s_components.png delete mode 100644 doc/images/kuryr_k8s_components.png delete mode 100644 doc/images/kuryr_k8s_components.svg delete mode 100644 doc/images/kuryr_k8s_ingress_ctrl_flow_diagram.svg delete mode 100644 doc/images/kuryr_k8s_ingress_sw_components.svg delete mode 100644 doc/images/kuryr_k8s_ocp_route_ctrl_sw.svg delete mode 100644 doc/images/l7_routing_and_user_lb_neutron_entities.svg delete mode 100644 doc/images/lbaas_translation.svg delete mode 100644 doc/images/net-policy.svg delete mode 100644 doc/images/pod_creation_flow_daemon.png delete mode 100644 doc/images/pod_creation_flow_daemon.svg delete mode 100644 doc/images/service_creation_diagram.png delete mode 100644 doc/images/service_creation_diagram.svg delete mode 100644 doc/images/update_network_policy_on_pod_creation.svg delete mode 100644 doc/images/vif_handler_drivers_design.png delete mode 100644 doc/requirements.txt delete mode 100755 doc/source/conf.py delete mode 100644 doc/source/contributor/contributing.rst delete mode 100644 doc/source/contributor/index.rst delete mode 100644 doc/source/devref/annotation_project_driver.rst delete mode 100644 doc/source/devref/health_manager.rst delete mode 100644 doc/source/devref/high_availability.rst delete mode 100644 doc/source/devref/index.rst delete mode 100644 doc/source/devref/kuryr_kubernetes_design.rst delete mode 100644 doc/source/devref/kuryr_kubernetes_versions.rst delete mode 100644 doc/source/devref/network_policy.rst delete mode 100644 doc/source/devref/port_manager.rst delete mode 100644 doc/source/devref/service_support.rst delete mode 100644 doc/source/devref/updating_pod_resources_api.rst delete mode 100644 doc/source/devref/vif_handler_drivers_design.rst delete mode 100644 doc/source/index.rst delete mode 100644 doc/source/installation/containerized.rst delete mode 100644 doc/source/installation/default_configuration.rst delete mode 100644 doc/source/installation/devstack/basic.rst delete mode 100644 doc/source/installation/devstack/containerized.rst delete mode 100644 doc/source/installation/devstack/index.rst delete mode 100644 doc/source/installation/devstack/nested-dpdk.rst delete mode 100644 doc/source/installation/devstack/nested-macvlan.rst delete mode 100644 doc/source/installation/devstack/nested-vlan.rst delete mode 100644 doc/source/installation/devstack/ovn-octavia.rst delete mode 100644 doc/source/installation/devstack/ovn_support.rst delete mode 100644 doc/source/installation/devstack/ports-pool.rst delete mode 100644 doc/source/installation/https_kubernetes.rst delete mode 100644 doc/source/installation/index.rst delete mode 100644 doc/source/installation/ipv6.rst delete mode 100644 doc/source/installation/listener_timeouts.rst delete mode 100644 doc/source/installation/manual.rst delete mode 100644 doc/source/installation/multi_vif_with_npwg_spec.rst delete mode 100644 doc/source/installation/multiple_tenants.rst delete mode 100644 doc/source/installation/network_namespace.rst delete mode 100644 doc/source/installation/network_policy.rst delete mode 100644 doc/source/installation/ports-pool.rst delete mode 100644 doc/source/installation/services.rst delete mode 100644 doc/source/installation/testing_connectivity.rst delete mode 100644 doc/source/installation/testing_nested_connectivity.rst delete mode 100644 doc/source/installation/testing_sctp_services.rst delete mode 100644 doc/source/installation/testing_udp_services.rst delete mode 100644 doc/source/installation/trunk_ports.rst delete mode 100644 doc/source/installation/upgrades.rst delete mode 100644 doc/source/nested_vlan_mode.rst delete mode 100644 doc/source/readme.rst delete mode 100644 doc/source/specs/pike/contrail_support.rst delete mode 100644 doc/source/specs/pike/fuxi_kubernetes.rst delete mode 100644 doc/source/specs/queens/network_policy.rst delete mode 100644 doc/source/specs/rocky/npwg_spec_support.rst delete mode 100644 doc/source/specs/stein/vhostuser.rst delete mode 100644 doc/source/usage.rst delete mode 100644 etc/cni/net.d/10-kuryr.conflist delete mode 100644 etc/cni/net.d/kuryr.conflist.template delete mode 100644 etc/oslo-config-generator/kuryr.conf delete mode 100644 kubernetes_crds/kuryr_crds/kuryrloadbalancer.yaml delete mode 100644 kubernetes_crds/kuryr_crds/kuryrnetwork.yaml delete mode 100644 kubernetes_crds/kuryr_crds/kuryrnetworkpolicy.yaml delete mode 100644 kubernetes_crds/kuryr_crds/kuryrport.yaml delete mode 100644 kubernetes_crds/network_attachment_definition_crd.yaml delete mode 100644 kuryr_cni/README delete mode 100644 kuryr_cni/go.mod delete mode 100644 kuryr_cni/go.sum delete mode 100755 kuryr_cni/hack/build-go.sh delete mode 100755 kuryr_cni/hack/update-deps.sh delete mode 100644 kuryr_cni/pkg/main.go delete mode 100644 kuryr_cni/pkg/ovo.go delete mode 100644 kuryr_cni/vendor/github.com/containernetworking/cni/LICENSE delete mode 100644 kuryr_cni/vendor/github.com/containernetworking/cni/pkg/skel/skel.go delete mode 100644 kuryr_cni/vendor/github.com/containernetworking/cni/pkg/types/020/types.go delete mode 100644 kuryr_cni/vendor/github.com/containernetworking/cni/pkg/types/args.go delete mode 100644 kuryr_cni/vendor/github.com/containernetworking/cni/pkg/types/current/types.go delete mode 100644 kuryr_cni/vendor/github.com/containernetworking/cni/pkg/types/types.go delete mode 100644 kuryr_cni/vendor/github.com/containernetworking/cni/pkg/utils/utils.go delete mode 100644 kuryr_cni/vendor/github.com/containernetworking/cni/pkg/version/conf.go delete mode 100644 kuryr_cni/vendor/github.com/containernetworking/cni/pkg/version/plugin.go delete mode 100644 kuryr_cni/vendor/github.com/containernetworking/cni/pkg/version/reconcile.go delete mode 100644 kuryr_cni/vendor/github.com/containernetworking/cni/pkg/version/version.go delete mode 100644 kuryr_cni/vendor/modules.txt delete mode 100644 kuryr_kubernetes/__init__.py delete mode 100644 kuryr_kubernetes/clients.py delete mode 100644 kuryr_kubernetes/cmd/__init__.py delete mode 100644 kuryr_kubernetes/cmd/cni.py delete mode 100644 kuryr_kubernetes/cmd/daemon.py delete mode 100644 kuryr_kubernetes/cmd/eventlet/__init__.py delete mode 100644 kuryr_kubernetes/cmd/eventlet/controller.py delete mode 100644 kuryr_kubernetes/cmd/sanity/__init__.py delete mode 100644 kuryr_kubernetes/cmd/sanity/checks.py delete mode 100644 kuryr_kubernetes/cmd/sanity_checks.py delete mode 100644 kuryr_kubernetes/cmd/status.py delete mode 100644 kuryr_kubernetes/cni/__init__.py delete mode 100644 kuryr_kubernetes/cni/api.py delete mode 100644 kuryr_kubernetes/cni/binding/__init__.py delete mode 100644 kuryr_kubernetes/cni/binding/base.py delete mode 100644 kuryr_kubernetes/cni/binding/bridge.py delete mode 100644 kuryr_kubernetes/cni/binding/dpdk.py delete mode 100644 kuryr_kubernetes/cni/binding/nested.py delete mode 100644 kuryr_kubernetes/cni/binding/vhostuser.py delete mode 100644 kuryr_kubernetes/cni/daemon/__init__.py delete mode 100644 kuryr_kubernetes/cni/daemon/service.py delete mode 100644 kuryr_kubernetes/cni/daemon/watcher_service.py delete mode 100644 kuryr_kubernetes/cni/handlers.py delete mode 100644 kuryr_kubernetes/cni/health.py delete mode 100644 kuryr_kubernetes/cni/main.py delete mode 100644 kuryr_kubernetes/cni/plugins/__init__.py delete mode 100644 kuryr_kubernetes/cni/plugins/base.py delete mode 100644 kuryr_kubernetes/cni/plugins/k8s_cni_registry.py delete mode 100644 kuryr_kubernetes/cni/prometheus_exporter.py delete mode 100644 kuryr_kubernetes/cni/utils.py delete mode 100644 kuryr_kubernetes/config.py delete mode 100644 kuryr_kubernetes/constants.py delete mode 100644 kuryr_kubernetes/controller/__init__.py delete mode 100644 kuryr_kubernetes/controller/drivers/__init__.py delete mode 100644 kuryr_kubernetes/controller/drivers/annotation_project.py delete mode 100644 kuryr_kubernetes/controller/drivers/base.py delete mode 100644 kuryr_kubernetes/controller/drivers/default_project.py delete mode 100644 kuryr_kubernetes/controller/drivers/default_security_groups.py delete mode 100644 kuryr_kubernetes/controller/drivers/default_subnet.py delete mode 100644 kuryr_kubernetes/controller/drivers/lb_public_ip.py delete mode 100644 kuryr_kubernetes/controller/drivers/lbaasv2.py delete mode 100644 kuryr_kubernetes/controller/drivers/multi_vif.py delete mode 100644 kuryr_kubernetes/controller/drivers/namespace_subnet.py delete mode 100644 kuryr_kubernetes/controller/drivers/nested_dpdk_vif.py delete mode 100755 kuryr_kubernetes/controller/drivers/nested_macvlan_vif.py delete mode 100644 kuryr_kubernetes/controller/drivers/nested_vif.py delete mode 100644 kuryr_kubernetes/controller/drivers/nested_vlan_vif.py delete mode 100644 kuryr_kubernetes/controller/drivers/network_policy.py delete mode 100644 kuryr_kubernetes/controller/drivers/network_policy_security_groups.py delete mode 100644 kuryr_kubernetes/controller/drivers/neutron_vif.py delete mode 100644 kuryr_kubernetes/controller/drivers/node_subnets.py delete mode 100644 kuryr_kubernetes/controller/drivers/public_ip.py delete mode 100644 kuryr_kubernetes/controller/drivers/utils.py delete mode 100644 kuryr_kubernetes/controller/drivers/vif_pool.py delete mode 100644 kuryr_kubernetes/controller/handlers/__init__.py delete mode 100644 kuryr_kubernetes/controller/handlers/kuryrnetwork.py delete mode 100644 kuryr_kubernetes/controller/handlers/kuryrnetwork_population.py delete mode 100644 kuryr_kubernetes/controller/handlers/kuryrnetworkpolicy.py delete mode 100644 kuryr_kubernetes/controller/handlers/kuryrport.py delete mode 100644 kuryr_kubernetes/controller/handlers/lbaas.py delete mode 100755 kuryr_kubernetes/controller/handlers/loadbalancer.py delete mode 100644 kuryr_kubernetes/controller/handlers/machine.py delete mode 100644 kuryr_kubernetes/controller/handlers/namespace.py delete mode 100644 kuryr_kubernetes/controller/handlers/pipeline.py delete mode 100644 kuryr_kubernetes/controller/handlers/pod_label.py delete mode 100644 kuryr_kubernetes/controller/handlers/policy.py delete mode 100644 kuryr_kubernetes/controller/handlers/vif.py delete mode 100644 kuryr_kubernetes/controller/managers/__init__.py delete mode 100644 kuryr_kubernetes/controller/managers/health.py delete mode 100644 kuryr_kubernetes/controller/managers/pool.py delete mode 100644 kuryr_kubernetes/controller/managers/prometheus_exporter.py delete mode 100644 kuryr_kubernetes/controller/service.py delete mode 100644 kuryr_kubernetes/exceptions.py delete mode 100644 kuryr_kubernetes/handlers/__init__.py delete mode 100755 kuryr_kubernetes/handlers/asynchronous.py delete mode 100644 kuryr_kubernetes/handlers/base.py delete mode 100644 kuryr_kubernetes/handlers/dispatch.py delete mode 100644 kuryr_kubernetes/handlers/health.py delete mode 100755 kuryr_kubernetes/handlers/k8s_base.py delete mode 100644 kuryr_kubernetes/handlers/logging.py delete mode 100644 kuryr_kubernetes/handlers/retry.py delete mode 100644 kuryr_kubernetes/health.py delete mode 100644 kuryr_kubernetes/k8s_client.py delete mode 100644 kuryr_kubernetes/linux_net_utils.py delete mode 100644 kuryr_kubernetes/objects/__init__.py delete mode 100644 kuryr_kubernetes/objects/base.py delete mode 100644 kuryr_kubernetes/objects/fields.py delete mode 100644 kuryr_kubernetes/objects/lbaas.py delete mode 100644 kuryr_kubernetes/objects/vif.py delete mode 100644 kuryr_kubernetes/opts.py delete mode 100644 kuryr_kubernetes/os_vif_plug_noop.py delete mode 100644 kuryr_kubernetes/os_vif_util.py delete mode 100644 kuryr_kubernetes/tests/__init__.py delete mode 100644 kuryr_kubernetes/tests/base.py delete mode 100644 kuryr_kubernetes/tests/fake.py delete mode 100644 kuryr_kubernetes/tests/unit/__init__.py delete mode 100644 kuryr_kubernetes/tests/unit/cmd/__init__.py delete mode 100644 kuryr_kubernetes/tests/unit/cmd/eventlet/__init__.py delete mode 100644 kuryr_kubernetes/tests/unit/cmd/eventlet/test_controller.py delete mode 100644 kuryr_kubernetes/tests/unit/cmd/test_daemon.py delete mode 100644 kuryr_kubernetes/tests/unit/cmd/test_status.py delete mode 100644 kuryr_kubernetes/tests/unit/cni/__init__.py delete mode 100644 kuryr_kubernetes/tests/unit/cni/plugins/__init__.py delete mode 100644 kuryr_kubernetes/tests/unit/cni/plugins/test_k8s_cni_registry.py delete mode 100644 kuryr_kubernetes/tests/unit/cni/test_api.py delete mode 100644 kuryr_kubernetes/tests/unit/cni/test_binding.py delete mode 100644 kuryr_kubernetes/tests/unit/cni/test_handlers.py delete mode 100644 kuryr_kubernetes/tests/unit/cni/test_health.py delete mode 100644 kuryr_kubernetes/tests/unit/cni/test_main.py delete mode 100644 kuryr_kubernetes/tests/unit/cni/test_service.py delete mode 100644 kuryr_kubernetes/tests/unit/cni/test_utils.py delete mode 100644 kuryr_kubernetes/tests/unit/controller/__init__.py delete mode 100644 kuryr_kubernetes/tests/unit/controller/drivers/__init__.py delete mode 100644 kuryr_kubernetes/tests/unit/controller/drivers/test_annotation_project.py delete mode 100644 kuryr_kubernetes/tests/unit/controller/drivers/test_base.py delete mode 100644 kuryr_kubernetes/tests/unit/controller/drivers/test_default_project.py delete mode 100644 kuryr_kubernetes/tests/unit/controller/drivers/test_default_security_groups.py delete mode 100644 kuryr_kubernetes/tests/unit/controller/drivers/test_default_subnet.py delete mode 100644 kuryr_kubernetes/tests/unit/controller/drivers/test_lb_public_ip.py delete mode 100644 kuryr_kubernetes/tests/unit/controller/drivers/test_lbaasv2.py delete mode 100644 kuryr_kubernetes/tests/unit/controller/drivers/test_multi_vif.py delete mode 100644 kuryr_kubernetes/tests/unit/controller/drivers/test_namespace_subnet.py delete mode 100644 kuryr_kubernetes/tests/unit/controller/drivers/test_nested_dpdk.py delete mode 100644 kuryr_kubernetes/tests/unit/controller/drivers/test_nested_macvlan_vif.py delete mode 100644 kuryr_kubernetes/tests/unit/controller/drivers/test_nested_vif.py delete mode 100644 kuryr_kubernetes/tests/unit/controller/drivers/test_nested_vlan_vif.py delete mode 100644 kuryr_kubernetes/tests/unit/controller/drivers/test_network_policy.py delete mode 100644 kuryr_kubernetes/tests/unit/controller/drivers/test_network_policy_security_groups.py delete mode 100644 kuryr_kubernetes/tests/unit/controller/drivers/test_neutron_vif.py delete mode 100644 kuryr_kubernetes/tests/unit/controller/drivers/test_node_subnets.py delete mode 100644 kuryr_kubernetes/tests/unit/controller/drivers/test_public_ip.py delete mode 100644 kuryr_kubernetes/tests/unit/controller/drivers/test_utils.py delete mode 100644 kuryr_kubernetes/tests/unit/controller/drivers/test_vif_pool.py delete mode 100644 kuryr_kubernetes/tests/unit/controller/handlers/__init__.py delete mode 100644 kuryr_kubernetes/tests/unit/controller/handlers/test_fake_handler.py delete mode 100644 kuryr_kubernetes/tests/unit/controller/handlers/test_kuryrnetwork.py delete mode 100644 kuryr_kubernetes/tests/unit/controller/handlers/test_kuryrnetwork_population.py delete mode 100644 kuryr_kubernetes/tests/unit/controller/handlers/test_kuryrnetworkpolicy.py delete mode 100644 kuryr_kubernetes/tests/unit/controller/handlers/test_kuryrport.py delete mode 100644 kuryr_kubernetes/tests/unit/controller/handlers/test_lbaas.py delete mode 100755 kuryr_kubernetes/tests/unit/controller/handlers/test_loadbalancer.py delete mode 100644 kuryr_kubernetes/tests/unit/controller/handlers/test_machine.py delete mode 100644 kuryr_kubernetes/tests/unit/controller/handlers/test_namespace.py delete mode 100644 kuryr_kubernetes/tests/unit/controller/handlers/test_pipeline.py delete mode 100644 kuryr_kubernetes/tests/unit/controller/handlers/test_pod_label.py delete mode 100644 kuryr_kubernetes/tests/unit/controller/handlers/test_policy.py delete mode 100644 kuryr_kubernetes/tests/unit/controller/handlers/test_vif.py delete mode 100644 kuryr_kubernetes/tests/unit/controller/managers/__init__.py delete mode 100644 kuryr_kubernetes/tests/unit/controller/managers/test_health.py delete mode 100644 kuryr_kubernetes/tests/unit/controller/managers/test_pool.py delete mode 100644 kuryr_kubernetes/tests/unit/controller/managers/test_prometheus_exporter.py delete mode 100644 kuryr_kubernetes/tests/unit/controller/test_service.py delete mode 100644 kuryr_kubernetes/tests/unit/handlers/__init__.py delete mode 100644 kuryr_kubernetes/tests/unit/handlers/test_asynchronous.py delete mode 100644 kuryr_kubernetes/tests/unit/handlers/test_dispatch.py delete mode 100644 kuryr_kubernetes/tests/unit/handlers/test_health.py delete mode 100644 kuryr_kubernetes/tests/unit/handlers/test_k8s_base.py delete mode 100644 kuryr_kubernetes/tests/unit/handlers/test_logging.py delete mode 100644 kuryr_kubernetes/tests/unit/handlers/test_retry.py delete mode 100644 kuryr_kubernetes/tests/unit/kuryr_fixtures.py delete mode 100644 kuryr_kubernetes/tests/unit/test_clients.py delete mode 100644 kuryr_kubernetes/tests/unit/test_k8s_client.py delete mode 100644 kuryr_kubernetes/tests/unit/test_linux_net_utils.py delete mode 100644 kuryr_kubernetes/tests/unit/test_object.py delete mode 100644 kuryr_kubernetes/tests/unit/test_os_vif_plug_noop.py delete mode 100644 kuryr_kubernetes/tests/unit/test_os_vif_util.py delete mode 100644 kuryr_kubernetes/tests/unit/test_utils.py delete mode 100644 kuryr_kubernetes/tests/unit/test_watcher.py delete mode 100644 kuryr_kubernetes/utils.py delete mode 100644 kuryr_kubernetes/version.py delete mode 100644 kuryr_kubernetes/watcher.py delete mode 100644 playbooks/copy-crio-logs.yaml delete mode 100644 playbooks/copy-k8s-logs.yaml delete mode 100644 playbooks/e2e-tests.patch delete mode 100644 playbooks/get_amphora_tarball.yaml delete mode 100644 playbooks/run_k8s_e2e_tests.yaml delete mode 100644 releasenotes/notes/active-passive-ha-cfbda8e6b527b48e.yaml delete mode 100644 releasenotes/notes/add-tagging-ce56231f58bf7ad0.yaml delete mode 100644 releasenotes/notes/bp-openshift-router-support-5f28108b39a2826f.yaml delete mode 100644 releasenotes/notes/change-cni-daemon-default-port-e968a83fa1bf30b5.yaml delete mode 100644 releasenotes/notes/changing-default-url-for-k8s-api-42c3b90183783291.yaml delete mode 100644 releasenotes/notes/cni-health-checks-d2b70f2f2551a9fc.yaml delete mode 100644 releasenotes/notes/containerization-2fba4dac5c097b19.yaml delete mode 100644 releasenotes/notes/cri-o-support-ab7e810775754ea7.yaml delete mode 100644 releasenotes/notes/deprecate-handlers-caching-9cdfd772aba9a7ce.yaml delete mode 100644 releasenotes/notes/deprecate-non-daemonized-6dd2154238b1628c.yaml delete mode 100644 releasenotes/notes/deprecate-sg-mode-option-96824c33335cd74b.yaml delete mode 100644 releasenotes/notes/deprecate-worker-nodes-subnet-e452c84df5b5ed5c.yaml delete mode 100644 releasenotes/notes/deprecate_lbaasv2-a524aedf5d3a36bc.yaml delete mode 100644 releasenotes/notes/drop-ingress-d78a7a9be8f20da1.yaml delete mode 100644 releasenotes/notes/drop-py27-60f55b6bc1d082bc.yaml delete mode 100644 releasenotes/notes/fault-tolerable-watcher-24c51dbccabf5f17.yaml delete mode 100644 releasenotes/notes/golang-kuryr-cni-aab144831d4dc9dd.yaml delete mode 100644 releasenotes/notes/k8s-client-token-default-882ec49d1faffc29.yaml delete mode 100644 releasenotes/notes/kuryr-daemon-f09075b9eb60388f.yaml delete mode 100644 releasenotes/notes/make-ext-subnet-optional-99e73bfcbde96c22.yaml delete mode 100644 releasenotes/notes/make-handlers-pluggable-844475484771ffd6.yaml delete mode 100644 releasenotes/notes/make-lbaas-timeout-configurable-8624ea32971c0cbe.yaml delete mode 100644 releasenotes/notes/make-listener-timeouts-configurable-f563d85eg6c6fe6d.yaml delete mode 100644 releasenotes/notes/multivif-pools-1cec757c77a8c4f8.yaml delete mode 100644 releasenotes/notes/network-device-mtu-default-90faa7d685d4d0ec.yaml delete mode 100644 releasenotes/notes/network-namespace-2353f8013be398cd.yaml delete mode 100644 releasenotes/notes/network-policy-support-on-services-without-selectors-fea06ab71a8a6f2a.yaml delete mode 100644 releasenotes/notes/octavia-acls-7452d3406d75ea15.yaml delete mode 100644 releasenotes/notes/oslo-caching-b11881cfb9dc306c.yaml delete mode 100644 releasenotes/notes/remove-non-daemon-836e4825384b1b88.yaml delete mode 100644 releasenotes/notes/remove-physical-device-mapping-15d614b70c68fc73.yaml delete mode 100644 releasenotes/notes/remove-run-server-aca25a2d9d723dc7.yaml delete mode 100644 releasenotes/notes/remove-sriov-5f44deb951264510.yaml delete mode 100644 releasenotes/notes/remove-worker_nodes_subnet-3e64510c0342e739.yaml delete mode 100644 releasenotes/notes/reusable-pool-drivers-00e7fdc1f4738441.yaml delete mode 100644 releasenotes/notes/started-using-reno-90dbe9da108ec5c4.yaml delete mode 100644 releasenotes/notes/stein-upgrade-226c8e7b735701ee.yaml delete mode 100644 releasenotes/notes/support-specify-project-by-namespace-annotation-18bc6eca729bff5e.yaml delete mode 100644 releasenotes/notes/svc-without-selectors-f36bab0883459b80.yaml delete mode 100644 releasenotes/notes/zuul-v3-native-gates-4ed7698667a7b92e.yaml delete mode 100644 releasenotes/source/2023.1.rst delete mode 100644 releasenotes/source/2023.2.rst delete mode 100644 releasenotes/source/2024.1.rst delete mode 100644 releasenotes/source/README.rst delete mode 100644 releasenotes/source/_static/.placeholder delete mode 100644 releasenotes/source/_templates/.placeholder delete mode 100644 releasenotes/source/conf.py delete mode 100644 releasenotes/source/index.rst delete mode 100644 releasenotes/source/queens.rst delete mode 100644 releasenotes/source/rocky.rst delete mode 100644 releasenotes/source/stein.rst delete mode 100644 releasenotes/source/train.rst delete mode 100644 releasenotes/source/unreleased.rst delete mode 100644 releasenotes/source/ussuri.rst delete mode 100644 releasenotes/source/victoria.rst delete mode 100644 releasenotes/source/wallaby.rst delete mode 100644 releasenotes/source/xena.rst delete mode 100644 releasenotes/source/yoga.rst delete mode 100644 releasenotes/source/zed.rst delete mode 100644 requirements.txt delete mode 100644 setup.cfg delete mode 100644 setup.py delete mode 100644 test-requirements.txt delete mode 100755 tools/gate/copy_crio_logs.sh delete mode 100755 tools/gate/copy_k8s_logs.sh delete mode 100755 tools/generate_config_file_samples.sh delete mode 100755 tools/generate_k8s_resource_definitions.sh delete mode 100644 tox.ini diff --git a/.coveragerc b/.coveragerc deleted file mode 100644 index 01ea475ae..000000000 --- a/.coveragerc +++ /dev/null @@ -1,17 +0,0 @@ -[run] -branch = True -source = kuryr_kubernetes -omit = kuryr_kubernetes/tests/* - - -[report] -ignore_errors = True -exclude_lines = - # Have to re-enable the standard pragma - pragma: no cover - - # Don't complain if tests don't hit defensive assertion code: - raise NotImplementedError - - # Don't complain if non-runnable code isn't run: - if __name__ == .__main__.: diff --git a/.dockerignore b/.dockerignore deleted file mode 100644 index 8a0f61274..000000000 --- a/.dockerignore +++ /dev/null @@ -1,3 +0,0 @@ -.tox -.dockerignore -*.Dockerfile diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 948f2fc65..000000000 --- a/.gitignore +++ /dev/null @@ -1,77 +0,0 @@ -*.py[cod] - -# C extensions -*.so - -# Packages -*.egg -*.egg-info -dist -build -eggs -parts -bin -var -sdist -develop-eggs -lib -lib64 - -# Installer logs -pip-log.txt - -# Unit test / coverage reports -nosetests.xml -cover - -# Translations -*.mo - -# Complexity -output/*.html -output/*/index.html - -# Sphinx -doc/build - -# Files created by releasenotes build -releasenotes/build - -# pbr generates these -AUTHORS -ChangeLog - -# Editors -*~ -*.sw? - -# Hidden directories -!/.coveragerc -!/.gitignore -!/.gitreview -!/.mailmap -!/.pylintrc -!/.testr.conf -!/.stestr.conf -.stestr - -contrib/vagrant/.vagrant - -# Configuration files -etc/kuryr.conf.sample - -# Ignore user specific local.conf settings for vagrant -contrib/vagrant/user_local.conf - -# Log files -*.log - -# devstack-heat -*.pem - -# Binaries from docker images builds -kuryr-cni-bin -kuryr-cni - -# editor tags dir -tags diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml deleted file mode 100644 index 6c7a4dccf..000000000 --- a/.pre-commit-config.yaml +++ /dev/null @@ -1,6 +0,0 @@ -repos: - - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v1.4.0 - hooks: - - id: flake8 - diff --git a/.stestr.conf b/.stestr.conf deleted file mode 100644 index 117657f2e..000000000 --- a/.stestr.conf +++ /dev/null @@ -1,3 +0,0 @@ -[DEFAULT] -test_path=${OS_TEST_PATH:-./kuryr_kubernetes/tests/} -top_dir=./ diff --git a/.zuul.d/base.yaml b/.zuul.d/base.yaml deleted file mode 100644 index 85d8490a8..000000000 --- a/.zuul.d/base.yaml +++ /dev/null @@ -1,259 +0,0 @@ -# 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. - -- job: - name: kuryr-kubernetes-base - parent: devstack-tempest - description: | - Base Kuryr Kubernetes tempest job. There are neither Neutron nor Octavia - services, its meant to be extended. - required-projects: - - openstack/devstack-plugin-container - - openstack/kuryr-kubernetes - - openstack/kuryr-tempest-plugin - - openstack/tempest - timeout: 10800 - post-run: - - playbooks/copy-k8s-logs.yaml - - playbooks/copy-crio-logs.yaml - host-vars: - controller: - devstack_plugins: - kuryr-kubernetes: https://opendev.org/openstack/kuryr-kubernetes - devstack-plugin-container: https://opendev.org/openstack/devstack-plugin-container - kuryr-tempest-plugin: https://opendev.org/openstack/kuryr-tempest-plugin - vars: - # Default swap size got shrinked to 1 GB, it's way too small for us. - configure_swap_size: 8192 - tempest_test_regex: '^(kuryr_tempest_plugin.tests.)' - # Since we switched the amphora image to focal, the tests started - # requiring more time. - tempest_test_timeout: 2400 - tox_envlist: 'all' - tempest_plugins: - - kuryr-tempest-plugin - devstack_localrc: - CONTAINER_ENGINE: crio - CRIO_VERSION: "1.28" - ENABLE_TLS: true - ETCD_USE_RAMDISK: true - KURYR_ENABLED_HANDLERS: vif,endpoints,service,namespace,pod_label,policy,kuryrnetworkpolicy,kuryrnetwork,kuryrport,kuryrloadbalancer - KURYR_SG_DRIVER: policy - KURYR_SUBNET_DRIVER: namespace - KURYR_SUPPORT_POD_SECURITY: true - devstack_services: - c-api: false - c-bak: false - c-sch: false - c-vol: false - cinder: false - coredns: false - # Need to disable dstat due to bug https://github.com/dstat-real/dstat/pull/162 - dstat: false - etcd3: true - g-api: true - g-reg: true - key: true - kubernetes-master: true - kuryr-daemon: true - kuryr-kubernetes: true - mysql: true - n-api-meta: true - n-api: true - n-cond: true - n-cpu: true - n-sch: true - placement-api: true - placement-client: true - rabbit: true - s-account: false - s-container: false - s-object: false - s-proxy: false - tempest: true - zuul_copy_output: - '{{ devstack_log_dir }}/kubernetes': 'logs' - '{{ devstack_log_dir }}/crio': 'logs' - irrelevant-files: - - ^.*\.rst$ - - ^doc/.*$ - - ^releasenotes/.*$ - - ^contrib/.*$ - -- job: - name: kuryr-kubernetes-base-ovn - parent: kuryr-kubernetes-base - description: Base kuryr-kubernetes-job with OVN - required-projects: - - openstack/neutron - timeout: 10800 - post-run: playbooks/copy-k8s-logs.yaml - host-vars: - controller: - devstack_plugins: - neutron: https://opendev.org/openstack/neutron - vars: - network_api_extensions_common: - - tag-ports-during-bulk-creation - devstack_localrc: - KURYR_NEUTRON_DEFAULT_ROUTER: kuryr-router - ML2_L3_PLUGIN: ovn-router,trunk,qos - OVN_BRANCH: v21.06.0 - OVS_BRANCH: "a4b04276ab5934d087669ff2d191a23931335c87" - OVN_BUILD_FROM_SOURCE: true - OVN_L3_CREATE_PUBLIC_NETWORK: true - VAR_RUN_PATH: /usr/local/var/run - devstack_services: - neutron-tag-ports-during-bulk-creation: true - neutron: true - q-qos: true - q-trunk: true - zuul_copy_output: - '{{ devstack_base_dir }}/data/ovn': 'logs' - '{{ devstack_log_dir }}/ovsdb-server-nb.log': 'logs' - '{{ devstack_log_dir }}/ovsdb-server-sb.log': 'logs' - '/home/zuul/np_sctp_kubetest.log': 'logs' - -- job: - name: kuryr-kubernetes-base-ovs - parent: kuryr-kubernetes-base - description: Base kuryr-kubernetes-job with OVS - required-projects: - - openstack/devstack-plugin-container - - openstack/kuryr-kubernetes - - openstack/kuryr-tempest-plugin - - openstack/tempest - - openstack/neutron - timeout: 10800 - post-run: playbooks/copy-k8s-logs.yaml - host-vars: - controller: - devstack_plugins: - neutron: https://opendev.org/openstack/neutron - vars: - network_api_extensions_common: - - tag-ports-during-bulk-creation - devstack_services: - neutron-tag-ports-during-bulk-creation: true - neutron: true - ovn-controller: false - ovn-northd: false - ovs-vswitchd: false - ovsdb-server: false - q-agt: true - q-dhcp: true - q-l3: true - q-meta: true - q-ovn-metadata-agent: false - q-svc: true - q-trunk: true - devstack_localrc: - KURYR_ENFORCE_SG_RULES: true - ML2_L3_PLUGIN: router - Q_AGENT: openvswitch - Q_ML2_PLUGIN_MECHANISM_DRIVERS: openvswitch - Q_ML2_TENANT_NETWORK_TYPE: vxlan - zuul_copy_output: - '{{ devstack_log_dir }}/ovsdb-server-nb.log': 'logs' - '{{ devstack_log_dir }}/ovsdb-server-sb.log': 'logs' - -- job: - name: kuryr-kubernetes-octavia-base - parent: kuryr-kubernetes-base-ovn - description: | - Kuryr-Kubernetes tempest job using OVN and ovn-octavia driver for Kuryr - required-projects: - - openstack/octavia - - openstack/python-octaviaclient - - openstack/ovn-octavia-provider - - openstack/octavia-tempest-plugin - pre-run: playbooks/get_amphora_tarball.yaml - host-vars: - controller: - devstack_plugins: - octavia: https://opendev.org/openstack/octavia - ovn-octavia-provider: https://opendev.org/openstack/ovn-octavia-provider - octavia-tempest-plugin: https://opendev.org/openstack/octavia-tempest-plugin - vars: - tempest_plugins: - - kuryr-tempest-plugin - - octavia-tempest-plugin - devstack_localrc: - KURYR_EP_DRIVER_OCTAVIA_PROVIDER: ovn - KURYR_ENFORCE_SG_RULES: false - KURYR_K8S_OCTAVIA_MEMBER_MODE: L2 - KURYR_LB_ALGORITHM: SOURCE_IP_PORT - OCTAVIA_AMP_IMAGE_FILE: /tmp/test-only-amphora-x64-haproxy-ubuntu-focal.qcow2 - OCTAVIA_AMP_IMAGE_NAME: test-only-amphora-x64-haproxy-ubuntu-focal - OCTAVIA_AMP_IMAGE_SIZE: 3 - devstack_local_conf: - post-config: - $OCTAVIA_CONF: - controller_worker: - amp_active_retries: 9999 - api_settings: - enabled_provider_drivers: amphora:'Octavia Amphora driver',ovn:'Octavia OVN driver' - health_manager: - failover_threads: 2 - health_update_threads: 2 - stats_update_threads: 2 - devstack_services: - octavia: true - o-api: true - o-cw: true - o-da: true - o-hk: true - o-hm: true - -- job: - name: kuryr-kubernetes-octavia-base-ovs - parent: kuryr-kubernetes-base-ovs - nodeset: kuryr-nested-virt-ubuntu-jammy - description: | - Kuryr-Kubernetes tempest job using OVS and amphora driver for Octavia - required-projects: - - openstack/octavia - - openstack/python-octaviaclient - - openstack/octavia-tempest-plugin - pre-run: playbooks/get_amphora_tarball.yaml - host-vars: - controller: - devstack_plugins: - octavia: https://opendev.org/openstack/octavia - octavia-tempest-plugin: https://opendev.org/openstack/octavia-tempest-plugin - vars: - tempest_plugins: - - kuryr-tempest-plugin - - octavia-tempest-plugin - devstack_localrc: - OCTAVIA_AMP_IMAGE_FILE: /tmp/test-only-amphora-x64-haproxy-ubuntu-focal.qcow2 - OCTAVIA_AMP_IMAGE_NAME: test-only-amphora-x64-haproxy-ubuntu-focal - OCTAVIA_AMP_IMAGE_SIZE: 3 - LIBVIRT_TYPE: kvm - LIBVIRT_CPU_MODE: host-passthrough - devstack_local_conf: - post-config: - $OCTAVIA_CONF: - controller_worker: - amp_active_retries: 9999 - health_manager: - failover_threads: 2 - health_update_threads: 2 - stats_update_threads: 2 - devstack_services: - octavia: true - o-api: true - o-cw: true - o-hk: true - o-hm: true diff --git a/.zuul.d/k8s-np-e2e.yaml b/.zuul.d/k8s-np-e2e.yaml deleted file mode 100644 index 63fc3bfd4..000000000 --- a/.zuul.d/k8s-np-e2e.yaml +++ /dev/null @@ -1,144 +0,0 @@ -# Copyright 2021 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. - -- job: - name: kuryr-kubernetes-e2e-np - parent: devstack - description: | - Kuryr-Kubernetes job with OVN and Octavia provider OVN running k8s network policy e2e tests - required-projects: - - openstack/devstack-plugin-container - - openstack/kuryr-kubernetes - - openstack/neutron - - openstack/octavia - - openstack/ovn-octavia-provider - - openstack/python-octaviaclient - pre-run: playbooks/get_amphora_tarball.yaml - post-run: - - playbooks/run_k8s_e2e_tests.yaml - - playbooks/copy-k8s-logs.yaml - - playbooks/copy-crio-logs.yaml - post-timeout: 7200 - host-vars: - controller: - devstack_plugins: - devstack-plugin-container: https://opendev.org/openstack/devstack-plugin-container - kuryr-kubernetes: https://opendev.org/openstack/kuryr-kubernetes - neutron: https://opendev.org/openstack/neutron - octavia: https://opendev.org/openstack/octavia - ovn-octavia-provider: https://opendev.org/openstack/ovn-octavia-provider - vars: - network_api_extensions_common: - - tag-ports-during-bulk-creation - devstack_localrc: - CONTAINER_ENGINE: crio - CRIO_VERSION: "1.28" - ETCD_USE_RAMDISK: true - KURYR_ENABLED_HANDLERS: vif,endpoints,service,namespace,pod_label,policy,kuryrnetworkpolicy,kuryrnetwork,kuryrport,kuryrloadbalancer - KURYR_ENFORCE_SG_RULES: false - KURYR_EP_DRIVER_OCTAVIA_PROVIDER: ovn - KURYR_K8S_API_PORT: 6443 - KURYR_K8S_CLOUD_PROVIDER: false - KURYR_K8S_OCTAVIA_MEMBER_MODE: L2 - KURYR_LB_ALGORITHM: SOURCE_IP_PORT - KURYR_NEUTRON_DEFAULT_ROUTER: kuryr-router - KURYR_SG_DRIVER: policy - KURYR_SUBNET_DRIVER: namespace - ML2_L3_PLUGIN: ovn-router,trunk,qos - OCTAVIA_AMP_IMAGE_FILE: "/tmp/test-only-amphora-x64-haproxy-ubuntu-focal.qcow2" - OCTAVIA_AMP_IMAGE_NAME: "test-only-amphora-x64-haproxy-ubuntu-focal" - OCTAVIA_AMP_IMAGE_SIZE: 3 - OVN_BRANCH: v21.06.0 - OVS_BRANCH: "a4b04276ab5934d087669ff2d191a23931335c87" - OVN_BUILD_FROM_SOURCE: true - OVN_L3_CREATE_PUBLIC_NETWORK: true - PHYSICAL_NETWORK: public - Q_AGENT: ovn - Q_BUILD_OVS_FROM_GIT: true - Q_ML2_PLUGIN_MECHANISM_DRIVERS: ovn,logger - Q_ML2_PLUGIN_TYPE_DRIVERS: local,flat,vlan,geneve - Q_ML2_TENANT_NETWORK_TYPE: geneve - Q_USE_PROVIDERNET_FOR_PUBLIC: true - VAR_RUN_PATH: /usr/local/var/run - devstack_services: - # TODO(dmellado):Temporary workaround until proper fix - base: false - c-api: false - c-bak: false - c-sch: false - c-vol: false - cinder: false - coredns: false - # Need to disable dstat due to bug https://github.com/dstat-real/dstat/pull/162 - dstat: false - etcd3: true - g-api: true - g-reg: true - key: true - kubernetes-master: true - kuryr-daemon: true - kuryr-kubernetes: true - mysql: true - n-api-meta: true - n-api: true - n-cond: true - n-cpu: true - n-sch: true - neutron-tag-ports-during-bulk-creation: true - neutron: true - o-api: true - o-cw: true - o-da: true - o-hk: true - o-hm: true - octavia: true - ovn-controller: true - ovn-northd: true - placement-api: true - placement-client: true - q-agt: false - q-dhcp: false - q-l3: false - q-meta: false - q-ovn-metadata-agent: true - q-qos: true - q-svc: true - q-trunk: true - rabbit: true - s-account: false - s-container: false - s-object: false - s-proxy: false - devstack_local_conf: - post-config: - $OCTAVIA_CONF: - controller_worker: - amp_active_retries: 9999 - api_settings: - enabled_provider_drivers: amphora:'Octavia Amphora driver',ovn:'Octavia OVN driver' - kubetest_version: v1.22.5 - np_parallel_number: 2 - gopkg: go1.16.12.linux-amd64.tar.gz - np_sleep: 30 - zuul_copy_output: - '/home/zuul/np_kubetest.log': 'logs' - '/home/zuul/np_sctp_kubetest.log': 'logs' - '{{ devstack_log_dir }}/kubernetes': 'logs' - '{{ devstack_log_dir }}/crio': 'logs' - irrelevant-files: - - ^.*\.rst$ - - ^doc/.*$ - - ^releasenotes/.*$ - - ^contrib/.*$ - voting: false diff --git a/.zuul.d/nodesets.yaml b/.zuul.d/nodesets.yaml deleted file mode 100644 index 10381df7a..000000000 --- a/.zuul.d/nodesets.yaml +++ /dev/null @@ -1,63 +0,0 @@ -# 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. - -- nodeset: - name: openstack-centos-7-single-node - nodes: - - name: controller - label: centos-7 - groups: - - name: tempest - nodes: - - controller - -- nodeset: - name: kuryr-nested-virt-ubuntu-jammy - nodes: - - name: controller - label: nested-virt-ubuntu-jammy - groups: - - name: tempest - nodes: - - controller - -- nodeset: - name: kuryr-nested-virt-two-node-jammy - nodes: - - name: controller - label: nested-virt-ubuntu-jammy - - name: compute1 - label: nested-virt-ubuntu-jammy - groups: - # Node where tests are executed and test results collected - - name: tempest - nodes: - - controller - # Nodes running the compute service - - name: compute - nodes: - - controller - - compute1 - # Nodes that are not the controller - - name: subnode - nodes: - - compute1 - # Switch node for multinode networking setup - - name: switch - nodes: - - controller - # Peer nodes for multinode networking setup - - name: peers - nodes: - - compute1 diff --git a/.zuul.d/project.yaml b/.zuul.d/project.yaml deleted file mode 100644 index 3d1baa370..000000000 --- a/.zuul.d/project.yaml +++ /dev/null @@ -1,45 +0,0 @@ -# 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. - -- project-template: - name: kuryr-kubernetes-tempest-jobs - check: - jobs: - - kuryr-kubernetes-tempest - - kuryr-kubernetes-tempest-defaults - - kuryr-kubernetes-tempest-systemd - - kuryr-kubernetes-tempest-multinode - - kuryr-kubernetes-tempest-multinode-ovs - - kuryr-kubernetes-tempest-ipv6 - - kuryr-kubernetes-tempest-ipv6-ovs - - kuryr-kubernetes-tempest-amphora - - kuryr-kubernetes-tempest-amphora-ovs - - kuryr-kubernetes-tempest-annotation-project-driver - gate: - jobs: - - kuryr-kubernetes-tempest - - kuryr-kubernetes-tempest-systemd - experimental: - jobs: - - kuryr-kubernetes-tempest-pools-namespace - - kuryr-kubernetes-tempest-multinode-ha - - kuryr-kubernetes-tempest-dual-stack - -- project: - templates: - - openstack-python3-jobs - - publish-openstack-docs-pti - - release-notes-jobs-python3 - - check-requirements - - kuryr-kubernetes-tempest-jobs diff --git a/.zuul.d/tempest-jobs.yaml b/.zuul.d/tempest-jobs.yaml deleted file mode 100644 index 8fff68a74..000000000 --- a/.zuul.d/tempest-jobs.yaml +++ /dev/null @@ -1,239 +0,0 @@ -# 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. - -- job: - name: kuryr-kubernetes-tempest - parent: kuryr-kubernetes-octavia-base - description: | - Kuryr-Kubernetes tempest job running kuryr containerized - -- job: - name: kuryr-kubernetes-tempest-ovn-provider-ovn - parent: kuryr-kubernetes-octavia-base - description: | - Kuryr-Kubernetes alias for kuryr kubernetes tempest test. - Because of the change we introduced in switching over to Neutron OVN - and Octavia OVN provider, this can be removed after updating - ovn-octavia-provider zuul project. - -- job: - name: kuryr-kubernetes-tempest-systemd - parent: kuryr-kubernetes-octavia-base - description: | - Kuryr-Kubernetes tempest job using octavia and running kuryr as systemd - services - vars: - devstack_localrc: - KURYR_K8S_CONTAINERIZED_DEPLOYMENT: false - -- job: - name: kuryr-kubernetes-tempest-centos-7 - parent: kuryr-kubernetes-tempest-systemd - nodeset: openstack-centos-7-single-node - voting: false - -- job: - name: kuryr-kubernetes-tempest-defaults - parent: kuryr-kubernetes-octavia-base - nodeset: kuryr-nested-virt-ubuntu-jammy - description: | - Kuryr-Kubernetes tempest job running kuryr containerized with OVN, - Octavias amphora, default set of handlers, default SG driver and default - subnet driver. - host-vars: - controller: - devstack_plugins: - octavia: https://opendev.org/openstack/octavia - octavia-tempest-plugin: https://opendev.org/openstack/octavia-tempest-plugin - vars: - devstack_localrc: - KURYR_ENABLED_HANDLERS: '' - KURYR_ENFORCE_SG_RULES: true - KURYR_EP_DRIVER_OCTAVIA_PROVIDER: default - KURYR_K8S_OCTAVIA_MEMBER_MODE: L3 - KURYR_LB_ALGORITHM: ROUND_ROBIN - KURYR_SG_DRIVER: default - KURYR_SUBNET_DRIVER: default - LIBVIRT_TYPE: kvm - LIBVIRT_CPU_MODE: host-passthrough - devstack_local_conf: - post-config: - $OCTAVIA_CONF: - controller_worker: - amp_active_retries: 9999 - api_settings: - enabled_provider_drivers: amphora:'Octavia Amphora driver' - health_manager: - failover_threads: 2 - health_update_threads: 2 - stats_update_threads: 2 - devstack_services: - q-trunk: true - o-da: false - voting: false - -- job: - name: kuryr-kubernetes-tempest-ipv6 - nodeset: kuryr-nested-virt-ubuntu-jammy - parent: kuryr-kubernetes-octavia-base - description: | - Kuryr-Kubernetes tempest job running kuryr containerized with IPv6 pod - and service networks using OVN and Octavia Amphora - # TODO(gryf): investigate why NP does not work with IPv6 - host-vars: - controller: - devstack_plugins: - octavia: https://opendev.org/openstack/octavia - octavia-tempest-plugin: https://opendev.org/openstack/octavia-tempest-plugin - vars: - devstack_localrc: - KURYR_ENABLED_HANDLERS: '' - KURYR_ENFORCE_SG_RULES: true - KURYR_EP_DRIVER_OCTAVIA_PROVIDER: default - KURYR_IPV6: true - KURYR_K8S_OCTAVIA_MEMBER_MODE: L3 - KURYR_LB_ALGORITHM: ROUND_ROBIN - KURYR_SG_DRIVER: default - KURYR_SUBNET_DRIVER: default - LIBVIRT_TYPE: kvm - LIBVIRT_CPU_MODE: host-passthrough - devstack_local_conf: - post-config: - $OCTAVIA_CONF: - controller_worker: - amp_active_retries: 9999 - api_settings: - enabled_provider_drivers: amphora:'Octavia Amphora driver' - health_manager: - failover_threads: 2 - health_update_threads: 2 - stats_update_threads: 2 - devstack_services: - q-trunk: true - o-da: false - voting: false - -- job: - name: kuryr-kubernetes-tempest-ipv6-ovs - parent: kuryr-kubernetes-octavia-base-ovs - description: | - Kuryr-Kubernetes tempest job running kuryr containerized with IPv6 pod - and service networks based on OVS - # TODO(gryf): investigate why NP does not work with IPv6 - vars: - devstack_localrc: - KURYR_ENABLED_HANDLERS: '' - KURYR_IPV6: true - KURYR_SG_DRIVER: default - KURYR_SUBNET_DRIVER: default - devstack_services: - q-trunk: false - voting: false - -- job: - name: kuryr-kubernetes-tempest-dual-stack - parent: kuryr-kubernetes-octavia-base - description: | - Kuryr-Kubernetes tempest job running kuryr containerized with dual stack - pod and service networks - vars: - devstack_localrc: - KURYR_DUAL_STACK: true - voting: false - -- job: - name: kuryr-kubernetes-tempest-pools-namespace - parent: kuryr-kubernetes-octavia-base - description: | - Tempest with containers, port pools and namespace subnet driver - vars: - devstack_localrc: - KURYR_SUBNET_DRIVER: namespace - KURYR_ENABLED_HANDLERS: vif,endpoints,service,namespace,pod_label,policy,kuryrnetworkpolicy,kuryrnetwork,kuryrport,kuryrloadbalancer - KURYR_SG_DRIVER: policy - KURYR_USE_PORT_POOLS: true - KURYR_POD_VIF_DRIVER: neutron-vif - KURYR_VIF_POOL_DRIVER: neutron - KURYR_CONFIGMAP_MODIFIABLE: false - -- job: - name: kuryr-kubernetes-tempest-annotation-project-driver - parent: kuryr-kubernetes-octavia-base - description: | - Run kuryr-Kubernetes tempest job with annotation project driver - vars: - devstack_localrc: - KURYR_PROJECT_DRIVER: annotation - voting: true - -- job: - name: kuryr-kubernetes-tempest-amphora - parent: kuryr-kubernetes-base-ovn - nodeset: kuryr-nested-virt-ubuntu-jammy - required-projects: - - openstack/octavia - - openstack/python-octaviaclient - - openstack/octavia-tempest-plugin - pre-run: playbooks/get_amphora_tarball.yaml - host-vars: - controller: - devstack_plugins: - octavia: https://opendev.org/openstack/octavia - octavia-tempest-plugin: https://opendev.org/openstack/octavia-tempest-plugin - vars: - tempest_plugins: - - kuryr-tempest-plugin - - octavia-tempest-plugin - devstack_localrc: - KURYR_ENFORCE_SG_RULES: true - OCTAVIA_AMP_IMAGE_FILE: /tmp/test-only-amphora-x64-haproxy-ubuntu-focal.qcow2 - OCTAVIA_AMP_IMAGE_NAME: test-only-amphora-x64-haproxy-ubuntu-focal - OCTAVIA_AMP_IMAGE_SIZE: 3 - LIBVIRT_TYPE: kvm - LIBVIRT_CPU_MODE: host-passthrough - devstack_local_conf: - post-config: - $OCTAVIA_CONF: - controller_worker: - amp_active_retries: 9999 - health_manager: - failover_threads: 2 - health_update_threads: 2 - stats_update_threads: 2 - devstack_services: - octavia: true - o-api: true - o-cw: true - o-hk: true - o-hm: true - voting: false - -- job: - name: kuryr-kubernetes-tempest-amphora-ovs - parent: kuryr-kubernetes-octavia-base-ovs - vars: - devstack_localrc: - KURYR_EP_DRIVER_OCTAVIA_PROVIDER: amphora - devstack_local_conf: - post-config: - $OCTAVIA_CONF: - controller_worker: - amp_active_retries: 9999 - health_manager: - failover_threads: 2 - health_update_threads: 2 - stats_update_threads: 2 - api_settings: - enabled_provider_drivers: amphora:'Octavia Amphora driver' - voting: false diff --git a/.zuul.d/tempest-multinode-jobs.yaml b/.zuul.d/tempest-multinode-jobs.yaml deleted file mode 100644 index 189774848..000000000 --- a/.zuul.d/tempest-multinode-jobs.yaml +++ /dev/null @@ -1,168 +0,0 @@ -# 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. - -- job: - name: kuryr-kubernetes-tempest-multinode - parent: kuryr-kubernetes-octavia-base - description: | - Kuryr-Kubernetes tempest multinode job with OVN - nodeset: kuryr-nested-virt-two-node-jammy - host-vars: - controller: - devstack_plugins: - octavia: https://opendev.org/openstack/octavia - octavia-tempest-plugin: https://opendev.org/openstack/octavia-tempest-plugin - group-vars: - subnode: - devstack_plugins: - devstack-plugin-container: https://opendev.org/openstack/devstack-plugin-container - kuryr-kubernetes: https://opendev.org/openstack/kuryr-kubernetes - devstack_services: - c-bak: false - c-vol: false - dstat: false - kubernetes-master: false - kubernetes-worker: true - kuryr-daemon: true - kuryr-kubernetes: false - neutron: true - ovn-northd: false - ovn-octavia-provider: true - placement-client: true - q-svc: false - devstack_local_conf: - post-config: - $OCTAVIA_CONF: - controller_worker: - amp_active_retries: 9999 - api_settings: - enabled_provider_drivers: amphora:'Octavia Amphora driver',ovn:'Octavia OVN driver' - health_manager: - failover_threads: 2 - health_update_threads: 2 - stats_update_threads: 2 - devstack_localrc: - CONTAINER_ENGINE: crio - CRIO_VERSION: "1.28" - KURYR_ENABLED_HANDLERS: vif,endpoints,service,namespace,pod_label,policy,kuryrnetworkpolicy,kuryrnetwork,kuryrport,kuryrloadbalancer - KURYR_ENFORCE_SG_RULES: false - KURYR_EP_DRIVER_OCTAVIA_PROVIDER: ovn - KURYR_K8S_OCTAVIA_MEMBER_MODE: L2 - KURYR_LB_ALGORITHM: SOURCE_IP_PORT - KURYR_NEUTRON_DEFAULT_ROUTER: kuryr-router - KURYR_SG_DRIVER: policy - KURYR_SUBNET_DRIVER: namespace - OVN_BRANCH: v21.06.0 - OVS_BRANCH: "a4b04276ab5934d087669ff2d191a23931335c87" - OVN_BUILD_FROM_SOURCE: true - OVN_L3_CREATE_PUBLIC_NETWORK: true - VAR_RUN_PATH: /usr/local/var/run - vars: - tempest_test_regex: '^(kuryr_tempest_plugin.tests.scenario.test_cross_ping_multi_worker.TestCrossPingScenarioMultiWorker)' - devstack_localrc: - KURYR_K8S_MULTI_WORKER_TESTS: true - devstack_local_conf: - post-config: - $OCTAVIA_CONF: - controller_worker: - amp_active_retries: 9999 - api_settings: - enabled_provider_drivers: amphora:'Octavia Amphora driver',ovn:'Octavia OVN driver' - health_manager: - failover_threads: 2 - health_update_threads: 2 - stats_update_threads: 2 - devstack_services: - kubernetes-master: true - kubernetes-worker: false - kuryr-daemon: true - kuryr-kubernetes: true - zuul_copy_output: - '{{ devstack_base_dir }}/data/ovn': 'logs' - '{{ devstack_log_dir }}/ovsdb-server-nb.log': 'logs' - '{{ devstack_log_dir }}/ovsdb-server-sb.log': 'logs' - voting: false - -- job: - name: kuryr-kubernetes-tempest-multinode-ovs - parent: kuryr-kubernetes-octavia-base-ovs - description: | - Kuryr-Kubernetes tempest multinode job with OVS - nodeset: kuryr-nested-virt-two-node-jammy - group-vars: - subnode: - devstack_plugins: - devstack-plugin-container: https://opendev.org/openstack/devstack-plugin-container - kuryr-kubernetes: https://opendev.org/openstack/kuryr-kubernetes - devstack_services: - c-bak: false - c-vol: false - dstat: false - kubernetes-master: false - kubernetes-worker: true - kuryr-daemon: true - kuryr-kubernetes: false - neutron: true - ovn-controller: false - ovs-vswitchd: false - ovsdb-server: false - placement-client: true - q-agt: true - q-dhcp: true - q-l3: true - q-meta: true - q-ovn-metadata-agent: false - q-svc: false - devstack_localrc: - CONTAINER_ENGINE: crio - CRIO_VERSION: "1.26" - KURYR_ENABLED_HANDLERS: vif,endpoints,service,namespace,pod_label,policy,kuryrnetworkpolicy,kuryrnetwork,kuryrport,kuryrloadbalancer - KURYR_ENFORCE_SG_RULES: true - KURYR_SG_DRIVER: policy - KURYR_SUBNET_DRIVER: namespace - ML2_L3_PLUGIN: router - Q_AGENT: openvswitch - Q_ML2_PLUGIN_MECHANISM_DRIVERS: openvswitch - Q_ML2_TENANT_NETWORK_TYPE: vxlan - vars: - tempest_test_regex: '^(kuryr_tempest_plugin.tests.scenario.test_cross_ping_multi_worker.TestCrossPingScenarioMultiWorker)' - devstack_services: - dstat: false - kubernetes-master: true - kubernetes-worker: false - kuryr-daemon: true - kuryr-kubernetes: true - neutron: true - devstack_localrc: - KURYR_K8S_MULTI_WORKER_TESTS: true - voting: false - -- job: - name: kuryr-kubernetes-tempest-multinode-ha - parent: kuryr-kubernetes-tempest-multinode - description: | - Kuryr-Kubernetes tempest multinode job running containerized in HA - timeout: 7800 - vars: - devstack_localrc: - KURYR_CONTROLLER_REPLICAS: 2 - KURYR_K8S_SERIAL_TESTS: true - tempest_concurrency: 1 - group-vars: - subnode: - devstack_plugins: - devstack-plugin-container: https://opendev.org/openstack/devstack-plugin-container - kuryr-kubernetes: https://opendev.org/openstack/kuryr-kubernetes - devstack_services: - kubernetes-worker: true diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst deleted file mode 100644 index ffce2e746..000000000 --- a/CONTRIBUTING.rst +++ /dev/null @@ -1,19 +0,0 @@ -The source repository for this project can be found at: - - https://opendev.org/openstack/kuryr-kubernetes - -Pull requests submitted through GitHub are not monitored. - -To start contributing to OpenStack, follow the steps in the contribution guide -to set up and use Gerrit: - - https://docs.openstack.org/contributors/code-and-documentation/quick-start.html - -Bugs should be filed on Launchpad: - - https://bugs.launchpad.net/kuryr-kubernetes - -For more specific information about contributing to this repository, see the -kuryr-kubernetes contributor guide: - - https://docs.openstack.org/kuryr-kubernetes/latest/contributor/contributing.html diff --git a/HACKING.rst b/HACKING.rst deleted file mode 100644 index fa6c33ecf..000000000 --- a/HACKING.rst +++ /dev/null @@ -1,5 +0,0 @@ -=================================== -kuryr-kubernetes Style Commandments -=================================== - -Read the OpenStack Style Commandments https://docs.openstack.org/hacking/latest diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 68c771a09..000000000 --- a/LICENSE +++ /dev/null @@ -1,176 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - diff --git a/README.rst b/README.rst index f493bec4e..4ee2c5f13 100644 --- a/README.rst +++ b/README.rst @@ -1,35 +1,10 @@ -======================== -Team and repository tags -======================== +This project is no longer maintained. -.. image:: https://governance.openstack.org/tc/badges/kuryr-kubernetes.svg - :target: https://governance.openstack.org/tc/reference/tags/index.html +The contents of this repository are still available in the Git +source code management system. To see the contents of this +repository before it reached its end of life, please check out the +previous commit with "git checkout HEAD^1". -.. Change things from this point on - - -Project description -=================== - -Kubernetes integration with OpenStack networking - -The OpenStack Kuryr project enables native Neutron-based networking in -Kubernetes. With Kuryr-Kubernetes it's now possible to choose to run both -OpenStack VMs and Kubernetes Pods on the same Neutron network if your workloads -require it or to use different segments and, for example, route between them. - -* Free software: Apache license -* Documentation: https://docs.openstack.org/kuryr-kubernetes/latest -* Source: https://opendev.org/openstack/kuryr-kubernetes -* Bugs: https://bugs.launchpad.net/kuryr-kubernetes -* Overview and demo: https://superuser.openstack.org/articles/networking-kubernetes-kuryr -* Release notes: https://docs.openstack.org/releasenotes/kuryr-kubernetes/ - - -Contribution guidelines ------------------------ - -For the process of new feature addition, refer to the `Kuryr Policy`_. - - -.. _Kuryr Policy: https://wiki.openstack.org/wiki/Kuryr#Kuryr_Policies +For any further questions, please email +openstack-discuss@lists.openstack.org or join #openstack-dev on +OFTC. diff --git a/babel.cfg b/babel.cfg deleted file mode 100644 index 15cd6cb76..000000000 --- a/babel.cfg +++ /dev/null @@ -1,2 +0,0 @@ -[python: **.py] - diff --git a/cni.Dockerfile b/cni.Dockerfile deleted file mode 100644 index 40c44da55..000000000 --- a/cni.Dockerfile +++ /dev/null @@ -1,42 +0,0 @@ -FROM quay.io/kuryr/golang:1.16 as builder - -WORKDIR /go/src/opendev.com/kuryr-kubernetes -COPY . . - -RUN GO111MODULE=auto go build -o /go/bin/kuryr-cni ./kuryr_cni/pkg/* - -FROM quay.io/centos/centos:stream9 -LABEL authors="Antoni Segura Puimedon, Michał Dulko" - -ARG UPPER_CONSTRAINTS_FILE="https://releases.openstack.org/constraints/upper/master" -ARG OSLO_LOCK_PATH=/var/kuryr-lock -ARG RDO_REPO=https://www.rdoproject.org/repos/rdo-release.el9.rpm - -RUN dnf upgrade -y && dnf install -y epel-release $RDO_REPO \ - && dnf install -y --setopt=tsflags=nodocs python3-pip openvswitch sudo iproute pciutils kmod-libs \ - && dnf install -y --setopt=tsflags=nodocs gcc gcc-c++ python3-devel git - -COPY . /opt/kuryr-kubernetes - -ARG VIRTUAL_ENV=/opt/venv -RUN python3 -m venv $VIRTUAL_ENV -# This is enough to activate a venv -ENV PATH="$VIRTUAL_ENV/bin:$PATH" - -RUN pip3 --no-cache-dir install -U pip \ - && python3 -m pip --no-cache-dir install -c $UPPER_CONSTRAINTS_FILE /opt/kuryr-kubernetes \ - && cp /opt/kuryr-kubernetes/cni_ds_init /usr/bin/cni_ds_init \ - && mkdir -p /etc/kuryr-cni \ - && cp /opt/kuryr-kubernetes/etc/cni/net.d/* /etc/kuryr-cni \ - && dnf -y history undo last \ - && dnf clean all \ - && rm -rf /opt/kuryr-kubernetes \ - && mkdir ${OSLO_LOCK_PATH} - -COPY --from=builder /go/bin/kuryr-cni /kuryr-cni - -ARG CNI_DAEMON=True -ENV CNI_DAEMON ${CNI_DAEMON} -ENV OSLO_LOCK_PATH=${OSLO_LOCK_PATH} - -ENTRYPOINT [ "cni_ds_init" ] diff --git a/cni_ds_init b/cni_ds_init deleted file mode 100755 index a7269b0ab..000000000 --- a/cni_ds_init +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/bash -ex - -function cleanup() { - rm -f "/etc/cni/net.d/10-kuryr.conflist" - rm -f "/opt/cni/bin/kuryr-cni" -} - -function deploy() { - # Copy the binary into the designated location - cp /kuryr-cni "/opt/cni/bin/kuryr-cni" - chmod +x /opt/cni/bin/kuryr-cni - if [ -f /etc/cni/net.d/kuryr.conflist.template ]; then - cp /etc/cni/net.d/kuryr.conflist.template /etc/cni/net.d/10-kuryr.conflist - else - cp /etc/kuryr-cni/kuryr.conflist.template /etc/cni/net.d/10-kuryr.conflist - fi -} - -cleanup -deploy - -exec kuryr-daemon --config-file /etc/kuryr/kuryr.conf diff --git a/contrib/devstack-heat/.gitignore b/contrib/devstack-heat/.gitignore deleted file mode 100644 index d0625d0ca..000000000 --- a/contrib/devstack-heat/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -.idea -*.pem -__pycache__ -*.pyc diff --git a/contrib/devstack-heat/README.rst b/contrib/devstack-heat/README.rst deleted file mode 100644 index 56ae96906..000000000 --- a/contrib/devstack-heat/README.rst +++ /dev/null @@ -1,88 +0,0 @@ -Kuryr Heat Templates -==================== - -This set of scripts and Heat templates are useful for deploying DevStack -scenarios. It handles the creation of an all-in-one DevStack nova instance and -its networking needs. - -Prerequisites -~~~~~~~~~~~~~ - -Packages to install on the host you run devstack-heat (not on the cloud -server): - -* python-openstackclient - -After creating the instance, devstack-heat will immediately start creating a -devstack `stack` user and using devstack to stack kuryr-kubernetes. When it is -finished, there'll be a file names `/opt/stack/ready`. - -How to run -~~~~~~~~~~ - -In order to run it, make sure you reviewed values in `hot/parameters.yml` -(especially the `image`, `flavor` and `public_net` properties, the last one -telling in which network to create the floating IPs). The cloud credentials -should be in `~/.config/openstack/clouds.yaml`. Then the most basic run -requires executing:: - - ./devstack_heat.py -c stack -e hot/parameters.yml - -This will deploy the latest master on cloud in a stack -. You can also specify other sources than master:: - - --gerrit GERRIT ID of Kuryr Gerrit change - --commit COMMIT Kuryr commit ID - --branch BRANCH Kuryr branch - --devstack-branch DEVSTACK_BRANCH DevStack branch to use - -Note that some options are excluding other ones. - -Besides that you can customize deployments using those options:: - - -p KEY=VALUE, --parameter KEY=VALUE Heat stack parameters - --local-conf LOCAL_CONF URL to DevStack local.conf file - --bashrc BASHRC URL to bashrc file to put on VM - --additional-key ADDITIONAL_KEY URL to additional SSH key to add for - stack user - -`stack` will save you a private key for the deployment in `.pem` -file in current directory. - -Getting inside the deployment ------------------------------ - -You can then ssh into the deployment in two ways:: - - ./devstack_heat.py show - -Write down the FIP it tells you and then (might be skipped, key should be -there):: - - ./devstack_heat.py key > ./.pem - -Finally to get in (use the default username for the distro of your chosen -glance image, in the example below centos):: - - ssh -i ./.pem ubuntu@ - -Alternatively, if you wait a bit, devstack-heat will have set up the devstack -stack user and you can just do:: - - ./devstack_heat.py ssh - -If you want to observe the progress of the installation you can use `join` to -make it stream `stack.sh` logs:: - - ./devstack_heat.py join - -Note that you can make `stack` join automatically using its `--join` option. - -To delete the deployment:: - - ./devstack_heat.py unstack - -Supported images ----------------- - -Scripts were tested with latest Ubuntu 20.04 cloud images. diff --git a/contrib/devstack-heat/devstack_heat.py b/contrib/devstack-heat/devstack_heat.py deleted file mode 100755 index b3a16732d..000000000 --- a/contrib/devstack-heat/devstack_heat.py +++ /dev/null @@ -1,218 +0,0 @@ -#!/usr/bin/env python3 - -# 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 argparse -import os -import subprocess -import sys -import time - -import openstack -from openstack import exceptions as o_exc - - -class ParseDict(argparse.Action): - def __call__(self, parser, namespace, values, option_string=None): - d = getattr(namespace, self.dest, {}) - if not d: - d = {} - - if values: - split_items = values.split("=", 1) - key = split_items[0].strip() - value = split_items[1] - - d[key] = value - - setattr(namespace, self.dest, d) - - -class DevStackHeat(object): - HOT_FILE = 'hot/devstack_heat_template.yml' - - def __init__(self): - parser = self._get_arg_parser() - args = parser.parse_args() - if hasattr(args, 'func'): - self._setup_openstack(args.cloud) - args.func(args) - return - - parser.print_help() - parser.exit() - - def _get_arg_parser(self): - parser = argparse.ArgumentParser( - description="Deploy a DevStack VM with Kuryr-Kubernetes") - parser.add_argument('-c', '--cloud', help='name in clouds.yaml to use') - - subparsers = parser.add_subparsers(help='supported commands') - - stack = subparsers.add_parser('stack', help='run the VM') - stack.add_argument('name', help='name of the stack') - stack.add_argument('-e', '--environment', help='Heat stack env file', - default='hot/parameters.yml') - stack.add_argument('-p', '--parameter', help='Heat stack parameters', - metavar='KEY=VALUE', - action=ParseDict) - stack.add_argument('-j', '--join', help='SSH the stack and watch log', - action='store_true') - stack.add_argument('--local-conf', - help='URL to DevStack local.conf file') - stack.add_argument('--bashrc', - help='URL to bashrc file to put on VM') - source = stack.add_mutually_exclusive_group() - source.add_argument('--gerrit', help='ID of Kuryr Gerrit change') - source.add_argument('--commit', help='Kuryr commit ID') - source.add_argument('--branch', help='Kuryr branch') - stack.add_argument('--devstack-branch', help='DevStack branch to use', - default='master') - stack.add_argument('--additional-key', help='Additional SSH key to ' - 'add for stack user') - stack.set_defaults(func=self.stack) - - unstack = subparsers.add_parser('unstack', help='delete the VM') - unstack.add_argument('name', help='name of the stack') - unstack.set_defaults(func=self.unstack) - - key = subparsers.add_parser('key', help='get SSH key') - key.add_argument('name', help='name of the stack') - key.set_defaults(func=self.key) - - show = subparsers.add_parser('show', help='show basic stack info') - show.add_argument('name', help='name of the stack') - show.set_defaults(func=self.show) - - ssh = subparsers.add_parser('ssh', help='SSH to the stack') - ssh.add_argument('name', help='name of the stack') - ssh.set_defaults(func=self.ssh) - - join = subparsers.add_parser('join', help='join watching logs of' - 'DevStack installation') - join.add_argument('name', help='name of the stack') - join.set_defaults(func=self.join) - - return parser - - def _setup_openstack(self, cloud_name): - self.heat = openstack.connection.from_config( - cloud=cloud_name).orchestration - - def _find_output(self, stack, name): - for output in stack.outputs: - if output['output_key'] == name: - return output['output_value'] - return None - - def _get_private_key(self, name): - stack = self.heat.find_stack(name) - if stack: - return self._find_output(stack, 'master_key_priv') - return None - - def stack(self, args): - stack_attrs = self.heat.read_env_and_templates( - template_file=self.HOT_FILE, environment_files=[args.environment]) - - stack_attrs['name'] = args.name - stack_attrs['parameters'] = args.parameter or {} - if args.local_conf: - stack_attrs['parameters']['local_conf'] = args.local_conf - if args.bashrc: - stack_attrs['parameters']['bashrc'] = args.bashrc - if args.additional_key: - stack_attrs['parameters']['ssh_key'] = args.additional_key - if args.gerrit: - stack_attrs['parameters']['gerrit_change'] = args.gerrit - if args.commit: - stack_attrs['parameters']['git_hash'] = args.commit - if args.branch: - stack_attrs['parameters']['branch'] = args.branch - if args.devstack_branch: - stack_attrs['parameters']['devstack_branch'] = args.devstack_branch - - print(f'Creating stack {args.name}') - stack = self.heat.create_stack(**stack_attrs) - print(f'Wating for stack {args.name} to create') - self.heat.wait_for_status(stack, status='CREATE_COMPLETE', - failures=['CREATE_FAILED'], wait=600) - print(f'Stack {args.name} created') - - print(f'Saving SSH key to {args.name}.pem') - key = self._get_private_key(args.name) - if not key: - print(f'Private key or stack {args.name} not found') - with open(f'{args.name}.pem', "w") as pemfile: - print(key, file=pemfile) - - os.chmod(f'{args.name}.pem', 0o600) - - if args.join: - time.sleep(120) # FIXME(dulek): This isn't pretty. - self.join(args) - - def unstack(self, args): - stack = self.heat.find_stack(args.name) - if stack: - self.heat.delete_stack(stack) - try: - self.heat.wait_for_status(stack, status='DELETE_COMPLETE', - failures=['DELETE_FAILED']) - except o_exc.ResourceNotFound: - print(f'Stack {args.name} deleted') - print(f'Deleting SSH key {args.name}.pem') - os.unlink(f'{args.name}.pem') - else: - print(f'Stack {args.name} not found') - - def key(self, args): - key = self._get_private_key(args.name) - if not key: - print(f'Private key or stack {args.name} not found') - print(key) - - def show(self, args): - stack = self.heat.find_stack(args.name) - if not stack: - print(f'Stack {args.name} not found') - ips = self._find_output(stack, 'node_fips') - print(f'IPs: {", ".join(ips)}') - - def _ssh(self, keyname, ip, command=None): - if not command: - command = [] - subprocess.run(['ssh', '-i', keyname, f'stack@{ip}'] + command, - stdin=sys.stdin, stdout=sys.stdout) - - def ssh(self, args, command=None): - stack = self.heat.find_stack(args.name) - if not stack: - print(f'Stack {args.name} not found') - ips = self._find_output(stack, 'node_fips') - if not ips: - print(f'Stack {args.name} has no IPs') - self._ssh(f'{args.name}.pem', ips[0], command) - - def join(self, args): - stack = self.heat.find_stack(args.name) - if not stack: - print(f'Stack {args.name} not found') - ips = self._find_output(stack, 'node_fips') - if not ips: - print(f'Stack {args.name} has no IPs') - self.ssh(args, ['tail', '-f', '/opt/stack/devstack.log']) - - -if __name__ == '__main__': - DevStackHeat() diff --git a/contrib/devstack-heat/hot/devstack_heat_template.yml b/contrib/devstack-heat/hot/devstack_heat_template.yml deleted file mode 100644 index 234391bcc..000000000 --- a/contrib/devstack-heat/hot/devstack_heat_template.yml +++ /dev/null @@ -1,121 +0,0 @@ -heat_template_version: 2015-10-15 - -description: Simple template to deploy kuryr resources - -parameters: - image: - type: string - label: Image name or ID - description: Image to be used for the kuryr nodes - default: Ubuntu20.04 - flavor: - type: string - label: Flavor - description: Flavor to be used for the VM - default: m1.xlarge - public_net: - type: string - description: public network for the instances - default: public - vm_net_cidr: - type: string - description: vm_net network address (CIDR notation) - default: 10.11.0.0/24 - vm_net_gateway: - type: string - description: vm_net network gateway address - default: 10.11.0.1 - node_num: - type: number - description: Number of VMs - default: 1 - local_conf: - type: string - label: local.conf file to use - description: URL of local.conf file to use when deploying DevStack - default: "" - gerrit_change: - type: string - label: Gerrit change to deploy Kuryr from - description: Gerrit change number to clone Kuryr from - default: "" - git_hash: - type: string - label: Commit from which to deploy Kuryr - description: Commit hash from which Kuryr should be deployed - default: "" - bashrc: - type: string - label: bashrc file URL - description: URL of bashrc file that will be appended for stack user - default: "" - branch: - type: string - label: Branch which should be deployed - description: E.g. master or stable/queens - default: "" - devstack_branch: - type: string - label: Branch which should be deployed - description: E.g. master or stable/queens - default: "" - ssh_key: - type: string - label: Additional SSH key - description: To be added for stack user. - default: "" - -resources: - network: - type: OS::Kuryr::DevstackNetworking - properties: - public_net: { get_param: public_net } - vm_net_cidr: { get_param: vm_net_cidr } - vm_net_gateway: { get_param: vm_net_gateway } - - master_key: - type: OS::Nova::KeyPair - properties: - name: { get_param: 'OS::stack_name' } - save_private_key: true - - nodes: - type: OS::Heat::ResourceGroup - properties: - count: { get_param: node_num } - resource_def: - type: OS::Kuryr::DevstackNode - properties: - public_net: { get_param: public_net } - image: { get_param: image } - flavor: { get_param: flavor } - key: { get_resource: master_key } - local_conf: { get_param: local_conf } - gerrit_change: { get_param: gerrit_change } - branch: { get_param: branch } - devstack_branch: { get_param: devstack_branch } - ssh_key: { get_param: ssh_key } - git_hash: { get_param: git_hash } - bashrc: { get_param: bashrc } - private_key: { get_attr: [master_key, private_key] } - public_key: { get_attr: [master_key, public_key] } - vm_net: { get_attr: [network, vm_net_id] } - vm_subnet: { get_attr: [network, vm_subnet_id] } - vm_sg: { get_attr: [network, vm_sg_id] } - name: - str_replace: - template: "__stack__/vm-%index%" - params: - __stack__: { get_param: 'OS::stack_name' } - -outputs: - node_fips: - value: { get_attr: [nodes, node_fip] } - vm_subnet: - value: { get_attr: [network, vm_subnet_id] } - vm_sg: - value: { get_attr: [network, vm_sg_id] } - master_key_pub: - value: { get_attr: [master_key, public_key] } - master_key_priv: - value: { get_attr: [master_key, private_key] } diff --git a/contrib/devstack-heat/hot/distro_deps.sh b/contrib/devstack-heat/hot/distro_deps.sh deleted file mode 100644 index 972d5dd1e..000000000 --- a/contrib/devstack-heat/hot/distro_deps.sh +++ /dev/null @@ -1,19 +0,0 @@ -distro=$(awk -F'=' '/^ID=/ {print $2}' /etc/os-release) -distro="${distro%\"}" -distro="${distro#\"}" - -if [[ "$distro" =~ centos|fedora ]]; then - yum install -y git python-devel - yum group install -y Development Tools - if [[ "$distro" == "centos" ]]; then - yum install -y epel-release - sed -i -e '/Defaults requiretty/{ s/.*/# Defaults requiretty/ }' /etc/sudoers - fi - yum install -y jq - yum install -y python-pip - pip install -U setuptools -elif [[ "$distro" =~ ubuntu|debian ]]; then - apt update -y - apt upgrade -y - apt-get install -y build-essential git python-dev jq -fi diff --git a/contrib/devstack-heat/hot/networking_deployment.yaml b/contrib/devstack-heat/hot/networking_deployment.yaml deleted file mode 100644 index cf5f8ab3d..000000000 --- a/contrib/devstack-heat/hot/networking_deployment.yaml +++ /dev/null @@ -1,80 +0,0 @@ -heat_template_version: 2014-10-16 - -description: Simple template to deploy kuryr resources - -parameters: - public_net: - type: string - label: public net ID - description: Public network for the node FIPs - vm_net_cidr: - type: string - description: vm_net network address (CIDR notation) - vm_net_gateway: - type: string - description: vm_net network gateway address - -resources: - vm_net: - type: OS::Neutron::Net - properties: - name: - str_replace: - template: __stack__/vm_net - params: - __stack__: { get_param: 'OS::stack_name' } - - vm_subnet: - type: OS::Neutron::Subnet - properties: - network_id: { get_resource: vm_net } - cidr: { get_param: vm_net_cidr } - gateway_ip: { get_param: vm_net_gateway } - name: - str_replace: - template: __stack__/vm_subnet - params: - __stack__: { get_param: 'OS::stack_name' } - - kuryr_router: - type: OS::Neutron::Router - properties: - external_gateway_info: - network: { get_param: public_net } - name: - str_replace: - template: __stack__/router - params: - __stack__: { get_param: 'OS::stack_name' } - - kr_vm_iface: - type: OS::Neutron::RouterInterface - properties: - router_id: { get_resource: kuryr_router } - subnet_id: { get_resource: vm_subnet } - - vm_sg: - type: OS::Neutron::SecurityGroup - properties: - name: vm_sg - description: Ping and SSH - rules: - - protocol: icmp - - ethertype: IPv4 - remote_mode: remote_group_id - - ethertype: IPv6 - remote_mode: remote_group_id - - protocol: tcp - port_range_min: 22 - port_range_max: 22 - - protocol: tcp - port_range_min: 8080 - port_range_max: 8080 - -outputs: - vm_net_id: - value: { get_resource: vm_net } - vm_subnet_id: - value: { get_resource: vm_subnet } - vm_sg_id: - value: { get_resource: vm_sg } diff --git a/contrib/devstack-heat/hot/node.yaml b/contrib/devstack-heat/hot/node.yaml deleted file mode 100644 index e56183320..000000000 --- a/contrib/devstack-heat/hot/node.yaml +++ /dev/null @@ -1,206 +0,0 @@ -heat_template_version: 2015-10-15 - -description: template to deploy devstack nodes - -parameters: - public_net: - type: string - label: public net ID - description: Public network for the node FIPs - image: - type: string - label: Image name or ID - description: Image to be used for the kuryr nodes - flavor: - type: string - label: Flavor - description: Flavor to be used for the image - default: m1.small - key: - type: string - label: key name - description: Keypair to be used for the instance - public_key: - type: string - label: key content for stack user authorized_keys - description: private key to configure all nodes - private_key: - type: string - label: key content to access other nodes - description: private key to configure all nodes - vm_net: - type: string - label: VM Network - description: Neutron network for VMs - vm_subnet: - type: string - label: VM Subnet - description: Neutron subnet for VMs - vm_sg: - type: string - label: kubernetes API sg - description: Security Group for Kubernetes API - name: - type: string - label: Instance name - description: devstack node instance name - local_conf: - type: string - label: local.conf file to use - description: URL of local.conf file to use when deploying DevStack - gerrit_change: - type: string - label: Gerrit change to deploy Kuryr from - description: Gerrit change number to clone Kuryr from - git_hash: - type: string - label: Commit from which to deploy Kuryr - description: Commit hash from which Kuryr should be deployed - bashrc: - type: string - label: bashrc file URL - description: URL of bashrc file that will be injected for stack user - default: "" - branch: - type: string - label: Branch which should be deployed - description: E.g. master or stable/queens - default: "" - devstack_branch: - type: string - label: Branch which should be deployed - description: E.g. master or stable/queens - default: "" - ssh_key: - type: string - label: Additional SSH key - description: To be added for stack user. - default: "" - -resources: - instance_port: - type: OS::Neutron::Port - properties: - network: { get_param: vm_net } - security_groups: - - default - - { get_param: vm_sg } - fixed_ips: - - subnet: { get_param: vm_subnet } - - instance_fip: - type: OS::Neutron::FloatingIP - properties: - floating_network: { get_param: public_net } - port_id: { get_resource: instance_port } - - instance: - type: OS::Nova::Server - properties: - name: { get_param: name } - image: { get_param: image } - flavor: { get_param: flavor } - key_name: { get_param: key } - networks: - - port: { get_resource: instance_port } - user_data_format: RAW - user_data: - str_replace: - params: - __distro_deps__: { get_file: distro_deps.sh } - __gerrit_change__: { get_param: gerrit_change } - __git_hash__: { get_param: git_hash } - __local_conf__: { get_param: local_conf } - __bashrc__: { get_param: bashrc } - __pubkey__: { get_param: public_key } - __branch__: { get_param: branch } - __devstack_branch__: { get_param: devstack_branch } - __ssh_key__: { get_param: ssh_key } - template: | - #!/bin/bash - set -ex - - # Wait a bit for connectivity - sleep 30 - - # Stack user config - groupadd stack - useradd -s /bin/bash -d /opt/stack -m stack -g stack - mkdir /opt/stack/.ssh - cat > /opt/stack/.ssh/authorized_keys << EOF - __pubkey__ - EOF - if [[ ! -z "__ssh_key__" ]]; then - curl "__ssh_key__" >> /opt/stack/.ssh/authorized_keys - fi - echo "stack ALL=(ALL) NOPASSWD: ALL" | tee /etc/sudoers.d/stack - curl "__bashrc__" >> /opt/stack/.bashrc - chown -R stack:stack /opt/stack - chmod 755 /opt/stack - - # Deps for devstack - __distro_deps__ - - # Stacking - sudo -i -u stack /bin/bash - <<"EOF" - function get_from_gerrit() { - local gerrit_change - local ref - - gerrit_change="__gerrit_change__" - echo "Finding latest ref for change ${gerrit_change}" - ref=$(curl -s "https://review.opendev.org/changes/${gerrit_change}?o=CURRENT_REVISION" | tail -n +2 | jq -r '.revisions[].ref') - echo "Fetching ref ${ref}" - git fetch https://opendev.org/openstack/kuryr-kubernetes "${ref}" && git checkout FETCH_HEAD - } - - function get_from_sha() { - local commit_sha - - commit_sha="__git_hash__" - echo "Sha to fetch: ${commit_sha}" - git checkout "$commit_sha" - } - - cd /opt/stack - git clone https://opendev.org/openstack-dev/devstack - if [[ ! -z "__devstack_branch__" ]]; then - pushd devstack - git checkout "__devstack_branch__" - popd - fi - git clone https://github.com/openstack/kuryr-kubernetes - pushd kuryr-kubernetes - - if [[ ! -z "__git_hash__" ]]; then - get_from_sha - elif [[ ! -z "__gerrit_change__" ]]; then - get_from_gerrit - elif [[ ! -z "__branch__" ]]; then - git checkout "__branch__" - else - "Deploying from master" - fi - popd - pushd devstack - - if [[ -z "__local_conf__" ]]; then - # The change is already downloaded, do not reclone - sed -e 's/# RECLONE=/RECLONE=/' /opt/stack/kuryr-kubernetes/devstack/local.conf.sample > /opt/stack/devstack/local.conf - else - curl "__local_conf__" > /opt/stack/devstack/local.conf - fi - popd - - touch stacking - - pushd devstack - ./stack.sh >> /opt/stack/devstack.log 2>&1 - popd - - touch ready - EOF -outputs: - node_fip: - description: FIP address of the node - value: { get_attr: [instance_fip, floating_ip_address] } diff --git a/contrib/devstack-heat/hot/parameters.yml b/contrib/devstack-heat/hot/parameters.yml deleted file mode 100644 index ed15861e2..000000000 --- a/contrib/devstack-heat/hot/parameters.yml +++ /dev/null @@ -1,10 +0,0 @@ -parameter_defaults: - vm_net_cidr: 10.11.0.0/24 - vm_net_gateway: 10.11.0.1 - public_net: 316eeb47-1498-46b4-b39e-00ddf73bd2a5 - image: Ubuntu20.04 - flavor: m1.xlarge - -resource_registry: - OS::Kuryr::DevstackNetworking: networking_deployment.yaml - OS::Kuryr::DevstackNode: node.yaml diff --git a/contrib/kubectl_plugins/README.rst b/contrib/kubectl_plugins/README.rst deleted file mode 100644 index 4e3ba8848..000000000 --- a/contrib/kubectl_plugins/README.rst +++ /dev/null @@ -1,28 +0,0 @@ -==================== -Kuryr kubectl plugin -==================== - -This plugin aims to bring kuryr introspection an interaction to the kubectl and -oc command line tools. - - -Installation ------------- - -Place the kuryr directory in your ~/.kube/plugins - - -Usage ------ - -The way to use it is via the kubectl/oc plugin facility:: - - kubectl plugin kuryr get vif -o wide -l deploymentconfig=demo - - -Media ------ - -You can see an example of its operation: - -.. image:: kubectl_kuryr_plugin_1080.gif diff --git a/contrib/kubectl_plugins/kubectl_kuryr_plugin_1080.gif b/contrib/kubectl_plugins/kubectl_kuryr_plugin_1080.gif deleted file mode 100644 index eb2d23c4161015e5bd6c94497e161ead8d7dd443..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1865381 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

Xs4V(Hi0IAMSDfw4O6QZKxa8Nf9 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 X15 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
hYd3WfJ-G)KG>;fCzuOk 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�wFZ_`@3t#EF1`;tHAgLKKEDiVp-L4q=$YiWE_b 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

X(}$WW#e}|xf<7ZuKpO5yUOjQ*LO0J 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∓UuJRp=unQt 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 qDCY~9beS}rhq~*$UWa@;Beny-_G8F&-m+uw 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 w2ik9YJ06fw zmMRm}Cl)GG`ikM`)C*Nzm4|rejaeQV8O9jkU+Fmym)4$WG@!_8@6o$CU#lH&Cy6{Y z6G~MI)QzgJbke?cDSel+G4_S7_w9L@Qc;=B+_4GE zW&hGjAM|pj>V3?@wG4}vIk`%pDWyxK`H3D4eWeE4hMu}CxdciJCl_Amu@hwn@waJe z-97Xqk6}Ldky^hbo0lShm7>oEM1LkcFuU)+QkVd5MnUgPf~n(p`S45%CbUMNJ{9EV z&`W{G_qglf=J{^Fqj8BOmi(JUNcUgZUA@$A9m#18em5gLWyKi1LI2Lhm5-wT!KVK1Tz{6w=TViZNb~Nq-gtao0r9!tm%D zmzEAF`^e1tRw_Q)oFhG<3!JJcxC9fUGRRvEOEw4$jI+rj?1EjHE7qR~XSeNz1BF*`LC&soZnQ5jyio zKwhL$I;G`TzKPL=hZ3EAMAoH4I#oM2hh#_dt#TT(vmt32P~d z@PedhBj>4ZTmtD`ou{jFJT)FfCXt!hoE zH@Gxt@9XjZs_Gk1im!R=)bUeQ-q!bG{Qb}&TC?A%M2~s@16`rkFRF^1;-iBGZKq9$ zGfmV0Qy&{r!RHP(*33kvLqb1ag!UwC8 zWtcdvK&68dU^|R~lAB}~`qZQ8nI+Gvl=v64`u+IO{6(i7-&=_+`IZeJxoOGihE={O zkI+S~wKid(+flA6bO!@Q5bsZ)ra(;)T1O={>G_Cee+YO`i#1Yz1YaA<0V`Aey_v*@ zF|f`Y_DZg9I<|au`bTM;wR6CwAcX2pb1U^6FH0fr>&?MG3DdW|2h+VWo`n2WB~jNl z(t_1o*~MQ7w{hWSg1{T>Jp1e>8v?DsFlQ$KtTyO1Cl~zji^i>GdHyjRDTt4Z5uceG zH1(vgx@ZNJg3US6y22fYF#v{2!5~~nr@`3U)_?%0k}P*XnuFt;RmjYVz+z zQhbZ;=62v5;480ch7;!4Cm#hZ>Yv}*mEzEu^iPXoQ!~0j`o4^~eBJ7GRs8{{>`Pyf zQ;G7u27+@R7d&&dnQnpC4j>s>-b>?xjU}81(UzHO2kcj`I_+mEsSRFv>v?F0IqBp` zy3&<%FZKI>A6*>wRKIXszv3PG{Uhn7ob{}jOEIg$h)0aBZUzS%8)-cG2nKT4l>hvF z3F&mp$O}pbsxzg@;dH?R2TovyV=6KH(4$B9<%c@4eW_lDW^cB4cOGlm<qRQuX5<$;Cc?J&&87Sey$po&1fM9RcR225ptgvPX;yJw@xA>6cK7gmb<^NSejOtJ zZ59sPR8K!(9`!=!a%ivhr*!JGlTNCp5I2w@v+7?>z-;(R4k2{eYE$hi(?F+t@mU#_ zc{y=fpR*b0s;^~)lP?>N_XzPG9p)y7-|80~D9~C<6qTHXzJ>0*F>~3?M(_anPP4&z z_Rg8TdKtRR9f)5urbhdy$De=j^ZGSAV@a( zbsrCDOg>++%f6!V_wC`!H)fPV_^JaarmcxEDL^6Il8eU^=uG9YO0b%U+*Mp@wKI># ze+aL~*s^z|Fa6qB^uf@y?WhpdtMG*r6?o{PTdW-ABz|@%RpvkPRUwCIFQC;HIN%$H z&HaTK=Nma?UFe?gze@zyCoAvO;TqyM`w^?zHAwCp>w_pXa`PEg)zz|+K-_=m2kcH83R@d~PVEub=%7f?sjKAjIu6CpN=Nn7i^EXA(YQm9=J z+@#0qSqCbC45`gWom`G>^W3FUzeOt!k0*e;5h~vXRgAX4^E~m663^XDBn~(3;O2xw z9U4#FGY-L{AdHPqZ1LgEkp7uOWIaqDkEo*|j75-s<8|%{q=Q*{Z3!^?eFg>N9GM$AaDc31>{j~taH0w>^&<7p6ujQsty^&7bRyYoCel}GaCs`qda zOS$J(i{PpXr5W#&I#j9O~r1A&d7$XfCkC9ClksqWpD}}H!0c(tp`cixJ5Fi7lc*t z^?Ex|ixTYY1l&TbG;NF5ZgiwffDG{9z+i`Ucw>CyW=k!D? zM>U`xX2q4%5lrpxd$O4^+`x z1X4kj-CxQ9E-stA0aA}SV~aNsfzu|A8gi5sWV{bSKrI)iEycD48B*}dX(GY_Dfmr? zqlAq=4FaA%3{Qap^|tuaQq^lrKlps+xj2`~_3-=-^pY4D7h@n?3nCYFXPG$VzJ^2M zmG&?YeIaN9%lYCY>ai#mQlG!5ikeyvv73mx+NbFcU?OSGt1{gYI=*cJY#~8NPp%Jo zD;!xZ+)WZ{Pg-YWfPaVrYaQHfYpNK~^5+MVEy=h>j_Q{f+-WZGG3mG}XzNo+rQMd4 zEK}PVuTo>#5%=a(aB9VpX{cGWG>G<&={1~7> zkRtxI(a3ej-=Se&iNIHQ79WybtZfi-x>9;aI3+H>hmJpO(%45gYnI|#g20XQN5zxN1B2nz zWi0bxSw0+Sn>gw~iNx2VJIUBKSQytaDbcqaeGX*O1Cnr+F9KjkXEzPmx%=l}bQR^) z4CR~4R`_{PhZtMR!?sJ*6X@U$I^J|b<-{;UzcVD~OePg!Ut)$t( zs4H|^cf!S%x|*wgH?K0Px)i+AjGOLz%&+dfx!dzDGu!2apHhBG6Z-K5;DKBT0utB8 zw!Ra2zK1GjO@RMH183?Z7J|@5%bgp!_k*^6XP^7#gRid&v@EauPRIW% z2c?p)T)VhFuF18P-f%P1btx@dQoYxe$kp0;rAigkQj6$r)gx%DR+w@7it!&iO`5G_ABh-u#*m zz2BiU#_@NbbIvu_sQy0hSo*Unroa2=$$n2)ytmGP?yGL{z<|dyE(SgrS3hvrZ@?{Q zU~BJS*t5Y+ANsbYxQAlgHh&llOc}DtAEKTcihDMcHh#6$acE0UKivZEGmm~`Y(bTS zUSG#H0R;I3`oH3h3LNHRxl1zL>RGbl#O1>n7daE4<1=@}F8GUF=Oz)Zq(TQ^cUnwt z5CTr+bX3{|`hV{D*@-pKAv?q??xwFjaRHNR-E6GNWSZ}abv?AY>mcgm1YBQ)daHmP zmLn#ounSyd9e_*i#qTIx~&ZZRDaP)fOtu3F`1 zCe&ICW=;@u^`TPH`s8KB=k#~3`2AGFx?+E5^mg_VcB&HlAAemWIUQn71s3*aAtp{ffTExrY(gvZeD69kA16`{~3j`@dvNZ%{x?JEz09Osh9Q z*JIaae6bvtOIe%C^nt<|AFNyGy@#tS(=nE|anEN0bk`-8J+y0>3{(q9G_rEUkrTB|HXulvEul8B2^>^PwOgKp|+W&yC3 zx7sKlVq+$xq|eLnqiui}ML}@}zn7zKb%Otu`M@VZd!)EDDQ1!l5=_C@aV;Nkt)>lB zK9hCL#JH=Ks8t;`F$B12p==?+#J|LR^1*lG(LdOrt5@LP@dyw{sUgdJ4_-N4ihd~q zwM@9wF2m1VL5DSf&6dHxUt?}^0ecR(3c%%x0cF)06_rFe5`Y1~zmM0z#nVo^pulw3 zzY+Em_wG7T#@tq0A-J+b;~=MaF~p91@wgqBSZ;GW8CXYGOBI9A{k72gbNV!=@@4RH zZ{*5+y5%kRj7wPs8ngTUOl`yNvsZLNb00bW-RpdG+Tjev_EQ%8%L3#6%ME%DkNuam z{>-BLrgLC(W9-U>TzVgRWyf(c`zV*ug(V|QHjGxu(l}Y1=a`a**cz6o? zlm^{H^SkehzZHDo^8~_LdKsW^=+=PWf&t?9=<8tMvA06%j2V~p8p$DSMl6e(!XC^ zj&(8a3?P=igzfqk_4}(-WzQbYj4XKp`4&|^u?CJ|tJXaMyU`y=KSG-KXeMXE^4H_t zB;Z5t2y?Q6){3f~0Nd<0cjQ7kqn+Kza1$Y9-(@2hLMv?o;T}wKlfaKW0J}|~PD&0W zNJ)2>|C!5DDY<1eXOGw?=GQI5?{|9|v47-1fPXVY2SkW%N90fybgc++c=PV*m~vpvkb=8UjBsTh!Jpv@T=<`9 zRP#9~>S_8nW&rXHL_7}(5JM8UNDqY^;ymme6mh~E-ypv2$lV0fKyzP02%+#p(yjOW z@Sl%-3iWz$du?w~#oA>Mo+(%Z0M08kg9lZ&ikq8Ic$Z+x~jRV8M)46r&CH(D+pDJO2bQRThI=&p$7^8%Cn_4RNqb^rYj7djUMMJCZZG@o=X z`8F!&g`^aRUU<-gJ3k~qe`}p=^Vw;2>0QRl5+k6iDwx775g9T4B|%RI(ki>1!;&T^ z|1-p`a|BkGqk~E{b*u;4l2yQ5&2>OzW5i2HCXyHfuOxT8mh|zc^O{2?9;Ym#+4xRW?pH-4)?I!WgG0ZHMIeOP5 zf&QS22wON2f%R+IPCxPlsCB&oobPXcoJy=#SpXf3%m2{-X@ASh{G+x%L!zO&UGn!( zYW72Zftr9O5o>fw-ow<IRV|{SDy}q2H-RbT*$`*R-Y5sb>SYWm&`~DE z{SQL6)YhQ-pbV>He2p3e@=3DZUG(l@y;1O#DW%dP)90_~+vG@NLGQw7duK(+sD+K{ zh_A)5C+U85V*9s##>d49vShyPLvz|pPN-i=o5dBs3#SZ1Hn|?zRoUuvYx=9N+n{Ub zeS^>TU1Ro-SnmSciF-%T=6!T#J))aUEwLG=>6g-K@Ov)>qsF2OYyf)9<|zVrCKR9_ zpDA982)#HHx;3eHJ~=Wp1)_hr0#(Z2ah_W#mm>r9pS;(HnF zLYDgR4S6Rg!lubuI4X(@BIzrch)3#61<`H$!XWBadF1U32ZvdjV?H=GZ@t5Db~T6; zgXGj5KRGLjxe9)EV%t8=*9Hmu_Wg4@Ly!4JVMf+mI+MPM`lh;M)6b<-3jJ)-nZwP5 zWZ&~?H+Eg@2tSovU3c}=>o!FTXX#Sena8^?^}p77*V*KDdRKS6=dOnQPn~z^Hb056 z>1)8Io=Gd$8etcTGewKDF6H@Zui~anMgrea#6Y9F0m3#lVnthic$bX9N-*Nf-{cz!t2VdUq zU(02F9bWf`RdzY|pX7DGVS|6b?;ddd81evg(jCB!##_sigAhroiN`TO&=iJ(HOrz(o%!jAsd6}M}5SreVy*Xo5xF|ljoEi z*f%t3^Dycver$Y1pvmQyMh5ZVn?&yXae0~HsvJ`;uTfPvaZLe5Us;r=L$VM7a=F;#y*brb35rCe50(&+1^lXn&hi?G^DgGZqzjT`Vfy7Kxt}g zGH0BWgsJ+UsBLWWNkNTAv}RV<**9AUpY)GzNTu|qyf!Z{x~qe&EFJ$nVuK})NBQnK zAxUXke+PAs)2@2b@&W-T6f)JJ446B~y)|;#lpi5ge==8#y0y06J|nIVhLtfS<4q!b z)EH)?glh*Z0~VcPgpY`)wUcgXpI~43;tOB}On|m^3*gJav&<@)h_|B^QO#tc{W%v1 zZG0Udsh^po*zK~JhpO=T8ov|-cbfT*htu`fpw~p?MK(p2T;sg{#@WVQAocl?0aPX) zQFtl_YH~9izl1MI|2E%nfCIOkX8QB1=1DI_gGwQT_$yR>?D_J74QZ6TZYTnX2dF^! zE_iIsOAtJyoVJcJe{?>_ zTuL-7Q5lN~O*{unP0+l#b$s8C?z4kP?GQG6&9`L_Rp^ zjs-}{5*OG7#9k>jg9`zqAg-e~{3aA7LxQ-4_Tw@}vJ?{4yMM^m7rI#qnB)LuKOi?9Y9tNt)jywf;?40Vt#%La0gnCH}8#p z2E9N6q;qg!dVap}Sp)PjAk}-)QH*VrK_-HPG$cO{5A?S+nA9G0VL=xsg~$!4uLb$z zwiPvxvXyOpPCgn>u&-QO>`nY3JpG-RP}g+eKBwL8%lP;k-rN5@u;{q*4XD#++&MXs zo4e=BLQ45Ymm3eB@6I~&%5}-X?&tk1+_xt6x@6zuO*`(#r(f)n=?A8lmQ)5@(m+R`!ZM*yA#Q|MWf|uo^hnx2K zblzG_dvx;2dei^-legmcSszM?R&u*N{QPs*C6{M`!u{v}9{$k8`fWS8BYWud0PI~$ zJ~%5lF3P--I2GirGtq$pg#Am3pAyaCT-j#tr_w{&1;*0yXHf*UX17F|Jl#>CN2rB< z2e|OpWN_kA1287?fZx6poZiIH-Hv~z~zFbyeqCj!u`d^(e{W_ z6(F4vzJUp1lL(~H^I{|NGv=GcWnWZanx`s(qf4PgD6o|dSMH%$dbg$nG8MUCoeRayJU@;C zilQLRTV+qlrwIc%zyDoR7S$`Za+A0)W$!%ykV^%~-u@EHy#kBE3GbAx!&V=>kHsMs zET}8)qZMsEmtT69=iD4ox+#hp&37CBVWeiwfAoZ)x4O#bc$ClStkK%I(Qf^* z1|46%bcjSnZl%HU*&CAWK=V|j`BMk=cfh)cJiLg-UjmYU0ii*ImBmmcIh;QNgwzaj zgfO)v#PkIJVRS((33SWHF0fxEpLRwA-_bP!#MKlSIm((Bq5KhqZ#MApAuPYfqX1v1 z03c+N>VqOc%Md`J$x>-?2$v$%mLU6TAfQ;DbeX5T#Ko<4XGHY;BvE!R7aXR;bw?y> zziN)S1EX&RYD@5Z6Dz>c)0YI=+>g8!U3nBQG|Ki019bj$Mm@yaO~>@rC%HTZkl3{+fqd*7)5t6R$dQHY}= zx%s!pAt61sscQuN{bBhB#L{#Hyf_Gn6$>~?`5|0{p3Z%XfsxZ35`MX0vk6Frd@aR? zr_5>pB+-lE;f*!(zzNQYna8nXU6&a>~ zkr^D&A5LY0J^Hi!Cup^^NtQml_qwLs3%~z!HcgkAvAN&YVoRE@Pm1oAB#$it=tp}u zUP=IQ6MOsvN1&VW_pmho0vbG=1H18m6rFoKll}k4uj|^uX2zIdHkvt~XT*rwoJkrX zB+dB{HBzb6y@OMdMo5xI5>kyQmAdCFQAiT1k)%@TaF@FK-hTW2y}zzKw(Gh+@AvEV zd`=N;xnNjkZ!S{+@1-t&&57fBj9|S2^UoxWU5Uxh z*39IqW(va_3cXyK>;6SBLiIz=gWE!pak6ZUsY#P;GyOntyGSW88|f!$()7Oef+L`F zZ&4O*U$QkXDan0LYKTR{7%L5-lt!N#^~iUy_!cOV+wg?F;gRn%_S5O-^k-MEhS>Yf zuEBc_rq1^1xc2;`Gw^i0s(H50%y;5pR$~5Sp04P&YBk&&ZHBo$v%yQ*XKtn6ksjN1?xlET^XuvejL3x}y$=uUDvo08g z11cz^Jgf+`6%&)(1iGV+IfJ}ZeG8YhXuV~w5*y7Q32VG?oPeLYsV>vFZO}|foKBlI z=NjnU{?1E!0IJGCmfl@mBgGUPzwV*DKrF|no57Cl5)%2uQT5w!u($PT4p6cAsJVdR zox^zXX1|>gO=k;uSEHk5*ri+TzYul*C_-eE#-1q6V^LZ~QN-$~^TnDunJ-Qs*V6b( zU>Cn&KhT1O2@Mi8mLEh43kZC@vlG0o?aBG4u9jO^Hsv)m<~CH@FmCdEqU$O(3T0&? zYV_8ybJK<>IdHFjTwt&Q{%$Ydt*5W1T}Jmd(;wHenw%u_4Fp^*7+OHA;Cq+E__fq9 z7H5s!EAYO;EQ48dgIMjgJ+WTLV!exEeX3)9uf=XYRyfcFqHm*zlZv`bb@f(2!xiwC z9V+k`sz2(6+SB4Y`6oJ^0~(oey;ATFrO@H{iO(shhwQ57zp8$|J8|(NrhBQX(1Z+g zd{hL)=~aL_OQ&-iMwuTlNt1k|#Kn@((fCiJq?Y1uY7lOl?cN_ciT7jEtrGTaOvvz0 z*dLit2`5mZVQEy9&P~D&Jq_KB_Q{PE1w@GA9b>@d>&j_&iJ*DrWtvIkI;%Q0dP~kA z(S*&v*PunoBp$t%c(f>ipyZpEc`g39)tRV#VyMt0Gk>a&dM}6M`xQKPOz5~$9#wRv zo*I*r551f^tG6auI)Q{NM~zNN5-EwO!gzGsTmr=;nNk6&W`YGTDOJ}h{xZ|cWheh9 zO{(ozZ#Q{=%1G-(e8RO)$=Cl&mMHITiH|bCZAk+Zf~Tzgj+gE57wcdY80U+`-Y%kROcFQv!3s%?NXhv_h_@1Uk4Fi3HEt!IjD)gup%!>JLB>g9N4-{8qg86N1gwK1Ad`_ zcoZlHt4|w(AJ{eT2CwD4x7Wk*FKoUOgUZF?5R5?{7lpud2%K?$KS1;4fZKmkPX6Nu z+DKc~)~+gZdmjW~pQ$QQx2J^$pwbzQ<=(>4$b9w`Gl#+FQV=SDS6!~$%z%JX_`_Uf zs;=b(B{iT>Mqm_7c~K1EIhcx1)d5tO~8vK834}4Qo@@=09K;X z#Z~H)B-Gt3nM@1(*=?hPD(2(1jKIOv+-Ais*EMw$fSqxKkOK(p7~#N=tyVwO%MTs{LJA!q^2sH)g)dSKROP^P)LmtO>%sA`GC(@6CuCzI^TdDggJ%t2QY8^b-aR z3>?i(m0IgIuhO&{a-`1Y!6@etr!BXG1e82B#X&bBAHT-+18_g^s|a@hnMBLolPFCc z%qbZV#x3P?XfV4Mxoj>U$Ifv|Fg4(42s&~OW2;h+AhF(eLSEf=z13*7iUY2Cz17aB-aCrp)+kv|q zr7e%`bMY1Y3eNyCm+!*KP37be)AF0hHR+<9tG=Mrs=ooj+cSmm?%p#t9ypdWi`U($ zc`TwoF6!BtSW|Lm{cpy2!_2OuulD}$<+J}aG@QH8CHx>LTo%Y* z1Of&5<3ArJ3i1(uqG8(y{Qz+eEk7@n>v45WttAJ>d!d*sKTg>LE`j;k_JG;XBgk|U z#1R?YIX}ms`{}yiobXKJHe=M9s{#+@h6VuNOMv?joP);z?Lel`K|z59_(cY}m-!$_ zssykXCUS-20Y_9l%1g2XBVaKLVG<0}WY?F&CNK_Shb4aL7|2D66>cOSS)rh76eIFz z-qq9=!5E2GWz7UKZ-P>Kj3TNj6FD600))4v*w(Y)wt~Tp$09rxbd7;GsG&ggnX(S0 za}%i)1m~$#gSByT-g717Y%(weFfR!-0QVzi^s%p06~IaRW{dV%9wo$SJ^~DNyJ!#r zoTc(XK*@lodi9B94aVLw*P2QSYBNhDgk9^o6|R3hvjn|w^1*3`gkvR~@9!U9kJ2L<~eRSv_U>hN)AqSg~;y#nv{M!QxtqMHRypa zGX6U)&ym{!4A|A++#lHu%8QL^K9zDk-ip)%DqL_c=~e(FZWt@L9&{OVFvP8har^cg zbow5EQ(*IGZTU;LwytWn@Z5u>(>pLEPV^Y+1vgp=Ks0ldS$zQIk==nX5rhee1(>u( zP^`YqpOpizhWCB(*VP7qk&pr48^B7<+U9vBX?R7NLgHMKhfvuH+r8l*GK*J=sYS>K)0VKOp@T^fk|F z(?ILspIYa2Bd;IKejQo=ws7ZT`zwWCAKQGcN*}q3#qJzqu5+;+#9HY@Kk+-U+V+IC zcK2X(f%&ynY9ak*aSpO2@gg5tsv65ufH%HgG)n^jf7Hh8(+^MN+v_axo2E~2a zgn(}Ofpd5zV42Xj(bL1ALgoZa-mD*Cv}74R!jLjxskFueH+(m=NV*kunlryKHH-`{ z6!Doj;k^lYqV805P2k-`__6F$QICME%Zl}{CM0#n_>DsGI01Pe6#bT z4B@_Y&pm?$#r(tQo-lYA0%%gtRyYPHw3-f(sCuV@+q%_{;=0VTwh1WiYhD=R(Jht* zUqIVBgODpQp4<$^vxqR+FRws3%F(06sta1(>|FXduS~jMNYI(;al!?(3|7Rtyr_8& z(_^f^aMYq8uy-2oR^BE`J-Ivb%KSugzsJu8S;iK!{&>S`Ys2|3C$<-gG!Nx^DzyhB z?v1@joMQ~Y`^E}}AGj9$UhDW7*Z#D3wAO&P-B$Kj@0DBeLi zeBg*TV0)m2ulimEKFZwUv7ma~+ro#;1%O&fOI}*19;sWR5vx3}h$yA%T1deS-cbsB z%VLmPhrqVT(d-!eIg9vEkabOTVvk750R|0nqg_RQAVp>E>_V?ieV?*}JOTb9T+Mul zQ{WOR3x-Dt6+*|DmhwW9(I~rIIZ(`PTKSP<*&KgM>o@iN#3OL>6p-;fZ!% zDLk7UO-AKl>~P|uxGaRpGd9|Gq69etaCDznbfL96_{J!`ORf!rX7^i8cqhuR?0JF? zYP`svL&dr>>oo$ydpukCX8{_E6z+<%O$5(wq2$v|12qa)=WXD8C16NRWfv&Zmh zYny-xiCz_PN~jYl8UJ9izmX7N%6jy>?^NCTYw25dk5tMg+b1nf`(CZxiLQkkM(9bl z<6u#JE4_DXmn-(BVxzA=*;dv{uqrsZRvTITtM7sT)z*rm^YN$Z!`i6r=YE_%06QELDbT9*M+J+C$3Acs&}0ldBaB9CBz za!SVCP0%%gc|ZGd(l3jrVfGwv`-6*h4~Qo2&n0tLLw zN9$%6C?|-cruymGQglEr7 zxyKQ~_Y@J#fuToX>SDs1DSeR@V49iR>3Ap;>$<%W|lR|o4s%9`zav(J^ctg^o${&fM z-1aMBTRx0VA5Lyke%?G6onvU}M$Da`CHaf^aR4V38(__f;2;GnHYj6WH#wIAnIwXW zOrGH8+tFOd-Y#Ug_>;V=ol_#^Z;l^);annKc=$MyV%VUo)-WzNH?&6K2lcV-Mv=f-UH=G+9esNjJNIe(9M)>;fE%hL zTceGq3~EN<7Nr8+3Q)m2^b+x<#Mh|~L^(ZX;SRHVbVF+pDe^-Kt}oX>##1)Kfrj^s zkt)lzq|oC%q(vNJ&xQP^bzOIqcualE33bKA#hbVJaw|eg?RsOggC4#7k<3++{@2Bu z&r>C-d-nay4_g7%!f{9^DPMWd5~w!0lCmh%`8{XwRsT)VB^=b>>BmA5{?D%}R<%G6d^V&S@##Fneqs@;`dUrqFKFy=A zHM^|te&w`$f${HC&A*Zdu2Vmsys&C*zw6gyuZ>^7DF1K4A@$eQFJ0PBH3dUeVs!WS zTUY4Z9LHPCEIvV~ktG(=?dA}+yfy#VrvLo~qg?vpu-l{eGptUn7qiOKh}FhQ@t4tj zjbWa=Q4X~7qt15yYe;Bq0Af*H%LY%v5-G<(K)HgiWfzkEoiAURujZ>nw5@~vcj3=% zyRO;5{35arjdNldhOfrqS7&MjvWyUs-f(> z(Sq69=CtUYN2%6B(bn&?33W_VHh^Jv;i{BpupS=ST>-cAd*Zs})XVrP<$Wm;_@l#0 zCIR#iceRQLHKnfG9$vV_bCa|PkJ1?}7CVcg>zF=c2T4zK4A`n=6C%8hd0c#ES49kE zz2~0Ou*+i_z3jg2{VtY-Uyx&G=H7tP8v(t}8)G)v&pL;=KX`X>&EviPMyi5WxWUH5 zv9|BCd!Ii#E(-8y0PIIjg;n&uTbb1g)5mjWv}m&*ZK?@0-Pi4?^Y`+5-qh`zJ+Zm8 z_b#X3YktAmDq?+s>IQn;{@57&J%ep~s?^dH7SPc{rGx7G3!jqvqb7S0;pp&~oiV2jO z-Z*|&n*Z$_={=uVyWcUdKfYij^B?KWPJ?_UIkk(sc=POw%)0!Q<3vb{Ol*XiMc|+T7jb2)pb$+iBlD)5kYKOdrKv@7pmz zA|M@6#s$?UCX-%1U~XQGd3765qgD4_E!ZYksJAm;=dk|zeUUiKQ!I>Gh!AI+}5-%L7GAa3w^w#^yvUA?a zN1jOFRZ{KZ_ctbooZp>^^r|RGN*5)az1e@Z*7Qr*g*8(upETN~i!0G61<1oJ^n#Qm zWATG~Z>+B&wlUQTSwxFik_l!1y}F2LFky<%tbVhF@GV(l z*4)NBpjP1>t3ny$Q%7Q6w}Wb*_$2Rfud2)b&6n>D&dqL)4Q{%k z2ZvO9$r(b2d^ZbkHOEAlgS#st5PJ~byxzA-gDYBJ#lxU;LL}DsVJ3;@M&k52C%!-? zzR$$1Sb0BkE_9;}gLZ(_sY9TX-6N;-{oUBTffFjveEus4a-3q7c?%JM(6K^jT?$;d z=4xbsW@+_c8kBN9TpMu(9uax(_s?TTymt&PEo2LBjKz3ReABsZe#6oY4Z?<%q(%xv zW_zqLq!6m1n;{J~Q1=TjF^}r@kmLXUF3H_j*0X_gdf)M$G|6YLHW(1w6SX3cs}Ibp zq=EJU4<~F6v(qB`4Wj&8cZReY*yI!BGoU=i6 z2jtMfY`cSR1$o9<06jy9dFmNV4foTm7ri&7guw>5O{%&|<@ zO%dN%6-AomTx!dZIU3XS1;rIpb}$9WE=f)BMJj_%!x5?(X_m^zapyoKw}!sKs`_ACCu zS6zc{pVs`BX$t8_ph9wI^Z<@fvi`H8wA;S7o*@!qZVNuD^*Tj(TDtczp5TBi=K2mO zw?(QTd7NjFSyth;Jo+<+@0p>bbVH1&m1lDBX|CeAzZrgi6XcpR};%Su4GjDcV zXCnP48b;TeROE&?nQE+Ge>yiNT)$}G2@AGnX#g<3^UXtr$T}81D#t;~4Pp~(W*?*e z>J2#vGm0$?E97Zp@+_}Upfh1+RXikZyN7$XQ+~Hu-Oi@2&0A{v<&BH7Hbs`$ZC$Js zxw^wVs0WGT4_CJ%Q4e<{nw>p!`|`U;mp>4+0_9#k>6_0Mzj}I}>j045F^SBB385ZZ zAJg(({m5w>g8SKxy!B)u&Y7nuuC!E)NuLndF>)0`r8dh@p%4(a4w@pTXh=~_Mf?Y(Fm+c5)TjJnebk=J`&!`ulDJhvUv5BM-_ z^V``!FTx1Gl@8g!Vv*+E?OO3CC}V`=f97Jnb+|!gU|+sr`yyXtHK10OuyZX z|E29xh;4RVzW|PY$i2L~pB(C@o$03B49U|({uBAt+ud(n-#ChoBj)qu9A_V`!~eEP z6WjO>Q+!wPu{Gr#*-(F%i=FbOD;6{uP`bcU&gYriDW{vjgXeneGKK4ZO{WjMw3J%O zp#qBX%t_)g6FX)5{`I-U0!#%^e!Cl0C^B#B$x%J#P5gSa2ioA*qnpz1Gz-0*O4}2Y z_csK;mGYjpA4cnulR{sPg82$!1d202uZv1oW%jE|lpp{pE-T;h&^{5J{x#OK%pByz zCL!YeDtt;LZHMp){^uQcUMn>{z35-#cKq6MsGA<}q2eQVt+STgu0u=Z$cpK4Opi3cyRTFww(C_4>GFPwW|Ai+10aGCpUd z$iG4BY>HJiDmkK_o}-o)D2tY-T|>vOg#t1J&z}JT3To*OQ01yYvRw6jzxBt<;^Z2P zTy2j*6SD5+!9+PDZED}v%LWE!_f&zEx*Tkws*!%&wL=uFiqSX)`+#ot8XiU;ViGuN z64{wuzR>!KD$ih_kyleGr=Q2z`u-@0JBxiB28hcJOb;~O`_Y|paK=6?zBL6{-gHj-VV;}~w1IAR>}MiaMv<=kwV@*Hy~Bh)fm*p_;|q7jdqw zA~U+YjRNuH`mCN7EPY&N8xWrO+DPaJ02iF;^V#QuMFDGN-J|%Dd?~=zU;l?=gpsjYWc{58JM_|6Xg4ukLv|x zZw`-jU94I!6M5bG2uzumJoV$`m23n|m}Ol5kmK`Znaa0z)Oxbx*S#qZxSp+64;tZP zi_kge;8lY*8_ zp&&kI9z;EaGzlqKJmVMC)DNk3>xGv=p~AHyEU*CbBGKM_1y8xst4gNxCZG#os}`x2 z*6i~id(IA8VU}qDz-yX^!<}u{H8Y2`NZxKTK$NRg0RiYy>ECL&?A$_%;*%M2VO5*0 zUgqZ`A!lWm_jq@Z-%E0d#H!nhKi%q=x%uj9XH@e9y}D|}$G4;A*9--y(J zaBybA@6A2J8zjn^ebJ}Wv%4XaUWC1qh|iAoC0Gth<9cg4zj?GIY?nXW07_9rm8HJ+ zw;W=Ijf45{XH5Xt_h2Y`;@D0&7kNkfSWK!^lc(_pF;B^g!9`AQ9z zxOo~*O{7tC_!1Jqt8B(rkofQLpGC%Z2D`I2#HLMms3n){!=(1)>V6zB9|Ak zy^!Kprb?hdI@?E?p%r>+`BG4MXq)EF+uv+{U2>NXM}_1CnSoz^z`BJqWn0{-YwQ2_ ztI*e*YX-=V_UEO-3twjh{}j|JSXT6yDQRr2$u>m0X+Z($qT-lEP~(p879)Rlu4XJ1 zUok2d6a5rWcv^-g54sLtPf^%fD#l6@JxJ**0_WIygf*{+cd{8_1W-I1K;YP+i5HZ% zF1-Kb4#jDvmhK2I(b>!vSUB^BLIb_qRv+uW#3|}TEZns&R@{<3>^`9vjg(_;#|}$! zcTj8lT36t>(AK;cmHa!vV1UZ-h6G=W)%79y!onyP=F)Q(vIH?s3az=KM(Y|ieK4ss zD?3j8`_Lg^q8kI9S1k#E@McR^t=s1>JH9=G4t>06x! z=k0%<8vFQh|L3~dQ>H=t>kETa62+&?RAE5x=w6$OImB&{@o1RM)o5nQ01lYb=T&L) z-xs6PX@$N&O5^~LEMvyq2lv4h2;ZC1UK4~i-R|lu+D)eZb%d`)exz3PI(`o$^H8QI zZ`h?J?*!vthi*5Vt!*V6= z_(u>#sG`u#*etq=Z*!&BB0}YlSV5FDu4eEJp*_k(x>L?-qH55nUsLg4UK;34+*8_d zs#w8U?5C5@gi)AHGY^i9s;2TvZM9e}S+@T?h3NVlD{im(*WQmP4HmDe}wKDfNrQ7OK)+ZP%>boT|uAo;succ@0&2PZcWrMvgXZO^7{vluay0r z_0VC{md8I8)w+3bvVk7Pjk^w*bwHL=9I6pN(>z5-BEKi^!=@ysvvCX0-9bwFdOxAK`giY^@mK!A0^o@D~GXtsb*4 z_Zrqxj3eR59^4}{OVo>0Vy zly$3Iz~O`(G$>Okv&LN(pf5lEEt1+~Tc|!z`D14DXHwQ-p$grIv#5u#Cr#ktQbn~D z!@|94ccZb5^LbV9srr*>D;qp!4>qFZZe(<~2+(Z2B8zrCw`y)aEJcXK;0Wd4l~JR_ zXtRESF>M~bA(tQB~>MQ;|)u!0HO z5dc#iGt!o5dKLef?j@RzXf>{-!Tl&tL$3VE0^mWi9(RkGHcN%_j|kpJ^}Es2_g!_4 za;GQL^mfdTu&L-vEvg5pN-|Y|PM5EL2x$V`b*cid4%#XnOphKFxf(sP)(;a*R1Er* z>{-oDvDi4anp{lE6B~U_GjuJvD-js=bsHV|I>V*wbD3I4DEj^-+WVvmt$^l@+CO$S zN`h+&bk#q@fWowtU|(YhQZV;1n>$jKH?^8p(9K?&uho_q;y?{26Mb)~;*XZT$VUAf zRimk|OCj?;4QVEI{l;encN(tftA4p#O(WbhRPd}btezjeW3GRy=y%g&n7ec&vDi@8 z#;D;$DM!$~k*C?d)|kwd#hO^qY$4*Md7~n%#n8BE>vGRErH8*u?2qiWIvs5?Rq`j& z^s7H4H+>-JvS;Z$bdf(SoPr{|B_C^kT1t0T+h-a^9qQ)Iwqo z&(ogwDHi`Qo~OphomP6Hb62qb$-eb_ePG8vQrkH2Kp9tX5e-uyts5AmkRKohBanU63w%5XgX)h-Nr_2?VL zwmT;GG5%2ci~MgRqk)DW(^R&e@c7?XoeOCusZ@6YUcW+3Vf2dC{V4Yo@lj_Bl#!Cc z*HhgR`XBmQH*wj}{OthAcW!9~p?JrgR9&NgAo^eN4HHi|-WO0TI?j%4^vba@Z&pz# z+T9mKRjz*BXHSQ-QIm&xzl+8lkBB!o&B0$bE0NnwJ!L~&JKCEsWh;S}X;OuL=8H~C z!rlq9T)>77DNb@U)4kw~mH!P-5WkB3_tumd<)Q%*`iG4^cg)rYN#b@P#wQ4ZC@Z`O zE25}ZvCag!lJ>ALZI-A&yu&x#e*l2VX%2Y2qO>a=HSIMRB&UBXfdn~o)80e7`9om#oPD6!xI(Hpp_uTSleWl1C~h+hUAr`q z4mn*eCbscO5jB0Kt-o`r+8URiw)vbZIPt&5G{ce)w!NVCeNc5$s@c?~?n}{mRk+ES z*-Ll#v=HImiV^hG&QO8JTU$0_Gn@hH{P@QQ0JNR>avR>}?qFDIuYrFw(vPY@4c>X< zDuRN27y@Z`%xMHs57J!;^>dEp56>qD8~O*rlcVtAJhv-zTH!K}plD@jt~8{ZD3}BD zZ(!B(>u^^y6#F-5Q1Rh1^I+F1rAd50E4gFN{)cqisF}Vm#iNj~91-j`D=Cw->x zo7m1LgoVo-7*QmDCSpj2^#`KmVxn2;+@cg7YxTb=x$ATJwDRb|=NzojBb!C0QaDAK zeC@fsp)wU^7;XC7%-~n{Ja)QynKFwMGBm%9rX4T6 zba-8ljR0NcDbs50+UaDaNOi|-d~NSk)GgjF$y$+rq2$?QM6b-Dmox9oCl~hXeL?`g z^SmtpKmr0y{oAgw?8QljqlRHT6>dH1SAi>LbS)c|Ay&$e!Do3We;$hb8tbGS1D{eT z1;X>El)nM*oeW~UDVLpVw5#;~a`aMwK3ru|-1@<)1DmxALBdds;RHi}DheMdCMU8q z@7ExZ8A;^sqJTuX8wwdp6a@qgyzlw##^ zFg~~+d4R}~?}$@s6Q79TS!<22m7kLe4j5|WjWrWf8-(5Txy+PWosb&s<)?j_?uL=f zWq?C;7Vq6(d&#~FSs!W`8?{cR2H4MGPkIYRPDU?F^7qwV9Qb%~0A5$-TkKMqZLwH) z^)l%uiz^3-^Yq;6L6X%)l_8??;#DiHYn*F~Ff%KcWf15*>qLu!bD5yMiT;q{p=u%F zdoH#$^hEKg{Qb~Xx#)77qdeL~cIVqwfxt~rB}C>ptfv{?th)e`R(Rdv0NCvVId#jP@U9cd%`Xj^>Zb!D1%9>&}=>HbO@)yBCO71Q<5D+(C8!U1R(qenVU1fG^ zVzP-ILf0L0Cl1KcE2rlm25Wjly62X{lBfC3xG>bsyLZZ%m_R_adrr3-)Sy$;I-+(4 zx$4DlhSxdZ!!42mrE?Y(twI*;&c(_iy1s^${yaba>&(_pzPhW-)m0B6^Ul*;^QrG* zX=&{tQlnzqy)ir%S-bn2(cRqb0-HHd`=wOrw`^~Cl*I?|f+>mGTTf1|8Stb>$PY?E}o-_*LOAM~rM&l>=1O3V|`G$GT-j@yz?%mh@+{fweMIGb% z|IPSTp4l#d-UU5*3Mr@6SNeyi2 zjjgNnR-7I=LVoYrkDmhBrE`^;EM2F$y{!W1=|1BLP_<1(nXZShuOVkO!U{lACkhS*v?algYup{H(B5h?qT| z=$-uj`~vpnrP4>nM%}XV$tWH6RRk3z7ws^to`bcGE8f?`yE6B-brFX^<-o9?P{5=fHgk?HKU0kAX;~yy{=J4lrED_`giu!9|7%NEj_$CU0#!t^ z6j#pra3AB16oe#7H`IfmE5)nnXnsAWPW(YC&DB&A>U?FI{`;EMu)A;SbD2{Yd3cG-DxwBp!fuOdid5TH=Bafyir+5U}u$_DboO*sa5C* znFrcH=H?Jq98mS(H&+!1TvKb3pbhv2cplfNWL$ve=;|JM7!h=?Ui$Q2Ca^IZ_v0a) zWPb$+9nl}dZg4i=ka?uuPX~x=^J{tt^A|hwzj&j2onV<4Sm#ZcI^9*i-s5MrQ8)_R6fo8Uc@X~%-i#afo2b-Ce_MOceSen;WBFBjZYI5NBC($^&LMs&DRmcqu4V;%4LhEb2IJ z(7`A_1Dp=jlII=gO$nH&jvrkdG1}A}gM?-q)LiT1%bWCf#1;{Y8{1#h?@$I}rM1S> zChQ`{ql}(MrSmEHA$0h#$W3{pL07VSW6@j>8JZh)3wR6iU>HDpV@AQXOD(|W?-htC zh>uN1w!cr@c8Ymlj86^sxoQ{8dfz}5Xr#0x*AsDkp_{j5$Jb#yptoiQRVGg|uQ^;i zOvojAx7p-bzkjN)0X$*G3NRlxP9fa~Wv*;Y`9^cLiN=B|0ayrlx(($W6vVcA2uOT} zeO-tFT2|LJ0+3O;@-g30HA8k>-$M6T^%}@g6Cph1Z!V92$aWoyYPBiZjUlY^c9Zpx z%ml=UjRO6h%f0{xV^Y)K@0Bm%1YLuxkYvGtZxc!ToTUOrQw81!8ILZ-08XDv4E{&@5U8cTe&8h0wLU>Fcz`%?bC}^ z5^&&DgGYXRWhcfx{ z+(KDP(lMfw3)6kq-P+u!?lBh;yI^~28?e;`f$H=^?*3J1ePoFuXe+y6QQz~k>*Pl>WFTT`aMWB zL>>pI`yGS&Ay)3Q>Cvxi)l8k3aGpfd(C{^9U4Wr_RnV!YvWL^H=`yAar0~#8z_q0) z$Kq8X3pp&PBv^2ZPrIbr-+m|Ug^%ZDW^;hUilDNqOpvo*UXt(1K}wOl%V9OJkij69 zy2AQBqmWlX#Br+OLhD|OLj9tii_#Fh2SqU!{yZ@UaM#0SwTJ_44gpj$jOQl&1+^Febiq+Y)YaH1$aF;4H-`UJ#KXp~ zoOZLscPNeKF*BEt7E3T*Pv4x^((prtW$(NmnN;b?JQ>gx`6B!b!-pHZ6*J{sTdAAY z+i93uZR?#Z^Hj)=9=l2T{%!~}!NRypyJL?` zl``8}3V$wjZ=D;e+>=73;Mpp`m^@Q*pM@#Pa3$rNAQ>B_R6r7A30ge;BsiVxe=S=1 z5#WhzRzu_ByUB8ZmC;xJWVGC+VI=i%p|#jqwhv$vPv)3=M?>8!t|q{1l=akDNoXAh zJ8b8Ma$id$nwt=S&ofaf+grgab#UF?D8BWdVlrEw8{{$;ToiY@+8Ges z5yKo1AFjZ4gg^njsn8JQQpG!(CB>@1|uSWf=^QAq}Lo2*wFTj34i0 zVrF-^_t{0NQeElQQv;$%GL zk$ex`hW)zd2?pwXQ*ZO412=@_2{qX#JWx|IC{`*|t6&bh-UE9CRl^Ocx79|{Sp{p? zV(%NoecM8%6VfMp2#<$?v<~BrvpddEA;-(EJJjxQ*tO&ko1X6Jm5Ya$yUb<-kmL^j z374uX>X`w+i6VHwKk}yY)@`21p&n#f=m+Q8hP*YV*2z18*3U;y_HH^+1KM`f{JqLX zv@gg7cI*=*ZvNQ|tybhbxpXg0605~fX5l&9Ic z>)?t&?5rC)Hv~hYJ5iqy9_0=u<_VSr#C%+y4V(_M{Nt*9H}nB|XvLFXC{(9}ZZS=q z0}0Y@(zORH%b^>EI&Qt{Mp|xRU3%pjqgRY=vOO}NBLNp3E#GY=FO?F%oJ{`Sg}9RD zi4lw#I4*G$@P9qmSP|fjD_ErRu}NBW0(^@S6tZI_5%9B7*ar;cd^RX-Z{a9e zP<&CG90D-g_$?L3GVV+hn7%YcNpY%=W!MFIVO(YwH)*Ci?>v!5>iG)9XsW*AV9(Ps zpGvX!MaYf4aD`RGE}qdgB;JPtG(hAa6Y3H3YnnqxVG6 zRZq0#D0GfyW5hwUuXsUY`(-qRoww9B!+;s4f-Q4@ejcyil@uihGCZYU8QXnds+1-J z+a*fo@nOw4p`RFPqbWDUDRto>OR2>xvD;p;(o6OZ#tJk_!O)kCkGZ0!r2_9!D_k+A zlcKSQ3FXtUvn!Y{PqrJ=kUoG-n-tn3!??3-Z{=A)T^5f-hpIC$)uJRogatU*ISHC7 zN|J~bNn-tLs>n|EcBn=BEE|ea=Z*7F);Lc;ng@MQ@!VyohJ%I1z*>p&(-rJvDbkc{ zGe}dg95pGUuS1TbPejM8#YpAB6OXkZoCW((7!A2PuK3bbJzk8#oenXjD87_nKkGx& z5Cn}KVBDZ}tzi3bNJ|;AkB#kI!B~k6UvltP9K6kIh(S9r_Y{`>1=|HE{5PhE2?!zc zl~OrSg;+@|2GKz~aQZE@)5b(Dsr}1fLrO7^l4-WZAtN@O=la&$G;IIw1dAwciS?lj zbJNpyVLG-lnB##0gdU{Jgc+|uxE4J-Hg2!GTp0!TrF zuZ7D%`k*J`3uH`zJIO$R0#QX3XBR;W8H^!;GE=+<_~b)}5nQIUG6@^aW|T1$CwbfvWfBuUP>(xAT@&sqOd8X0BW zfCjcu;MM?i))cvsi_ifq+{Mt}4?OEl;fDW@nxk1us-Bxd)}!U;FGCL+Oa0!J`W>&{ zcKjTz26li~dE<8FF`vq)q`w+{;7Jpu+a=YTpI58alx_rojVoX|Q8lg%qKd__mk<^* z&|O@)Wpsa19fGV1ZKPR|IOTSta?0~vD-w~G-5>tS-1p8c9(yfL#lD@&_nNv6n>>GEz@HP z9$bsaZ>$LZ>ghzQ5wB7#X)tphjQLLfDP%1!2RP-Ir{K1@b1m=6cWRz9mQ_)uNJ}wD zU8%DsSkdlWC{Mb$%@kU=3^x|T+$p#?+=0Knqjf28LK7l<8ZwqbK7p*8!Xa!Z^bp zUhqRpwu_9S0lQ5-$nPacUYP%G@um3D@+9|4yTgd^cd)Tv5=0szTGTQVfFqI4g^Q4J zqVsg^0}qm%n;Od-9@N3iaB~s`Tj2V0G!Tb7H~64c=g1NA!}Ce8#%0Fv?IQItw1jGY z@tF2G<=se?-R-x2Hrd`)yJ?DRdVo{F0Qsgjqfb=cIx9)BfjvCbcIUz4ZO)_id_t{h|g4`su@&L<}|hO@8#PrD8Ar3HILVsK8f=a*p| zNj0?*Huz3{Gr546x$sN-_Q`XeVae?bPAf2a%jls_`1@NaY~(-5f3aq6HSMl~R1YLI zR=w#(?iRJw-^C>vz>`)Enx`SO7B2ak;>z!w{ZU!%|EqM&wAJXGy6T5pJ}h|5hcX`V zz;|lvU2~*A(Q3E&y7iHJ)8=>QKHR(f;a>gBt=V%eqtDyo*~Y=z9pNh%lH%HpNmfbX z&WM@6>(Ac0b>c#BWo6KjI~dj5AJr^)XUqBL&gboJ6gu6%IaC+++jg$@lGYzvt>*uu z=v@4n?EgQ0?R+rIX@=1-%y|wmQuj3*jnD`!Npnm^P0_7V_qBtWLQ`m@?h#T{l1ioS z&2gmCNs_v!qe>+m?&|LT`tA1*Y>z#5eXi?sy+5zl^Lahy4T79=xh3LaFp>V(?zX1} zf`!d<4%SDSX$At5a~I$~*V4w&IdffByZhkN*W>a!?(M!3^sl9NXwUS&9Z8d2b2rdo zMOQdemlhL%R7{umkvjM3?mzcm#IJ22tQ|-kU1G4g1ABGg#+`vaV${XWUpbimy& zHw?lnA@kVf_GFWYRMcp^>wbS{7UYszg{XNj#M3U>L_y|+n0A@$m6(SA@9@x7Eqk-6 zQ_d8-A|z}(0jNkrIV|zmS*9%xD`v9oUp-8V&niG^1_fn3zmC}aK0h4i>#=EcM?wD% zRr6p*s5gav08k*sJ?{!l-A9R*`e?jOmc(P|uavouhYmE@^W1I0-Fy z4fAs>y}IuBUMW8Ksj$$r!Et+tX}XTBr!^W#GB7Wfs9lJ!e~l{2Y_=5Lgg+e45@*8m zN?)`)n);d;Acl_D4+bfaxhK|Iop)>TF&Vy3H-!(!!pCxa#$x^%N;4hZh#cA7Gn^Xoj;OfSdn2Lk9}KS zz(n|W+`nf~>BBTM)0W+JdE#Z-wsv3U?JKFH21@=&`@z6R2epKdm3!8l>^eX0M;9a*Gs+EsgTlL)LXbQK+-oE)Q}}pQ)s0oX zLqR;apW?=RKYQcSV%g#c`XOZtW$C`+aUSvJ?Q0Kujv_mklWNjXdQf=zLI1yoNNYgV z&ilThFiHvLkI_V`g96piKT_0=%Jl4Hm_&zE{moX5-^xWfcd&#W_6Vet3t(~;ixlHd zqxalN)WIVP?l621AAXkW%Cmi%ZlC`Y7k6*P4#M!p_YqqQ&b0p;TexcP1nBCEl1DF2 zBU}gICCy`&;zxGxBRqBCsZHa#msXvPzCYi?^`!bPKknz{w#P+1bX~I}=QUh?f4ICC zZfO`BY7)Jx&L*J5|3!%n%a*cK_dLD)|6%91>5sGy@LhWfZ|-qkH4}2z^Wq6YNczDS z3rC@xTYu%+h2-shvi*p;737xuCA^u1s%&nPn^?-QfwckzP+m7ow^}nyIy|7AXlX#^!=Y5rjf)c-EcIeV_e3c5$ z*0U^^U-e4i+*(GgW=j^m;entRhX!p%5SWokzujGoBRDr%)9aw`~OK0NOO87G`m>{?*I*` zbMLgb=JJK`>-O|pFl6M{5~rJPpGhVy0(>io@f2X16*zAq>VMa9x$4I}^^G|`bqmqU zLybN{yp2!zJf-od;=M%K@9v};ak??5AS{h%GTKjyEN~;6o7@60%sI!-7I z{C6u&-hX62KWn2a2WnL=01Q;Ai2xp-0ay2`sfM*$CBhJ*o9U;d=h9jbUZObQAZtxL z>bvrGCruUE6Qwog@F5uB+w^XT77;=Pc+r9iN>s`iOklX7^k~TTx9z*lO4Zags7-tW zb<2|@!MH`v9wutJY4gO+-2sw_KI#@%n09%D%pl=g|23$w*c0+y_o(1Anp#@RSkqY# zfHBne;D~5@`}y2 z2@2e4h3V^CJA3;xVpPVK@IEUf&j%@s(b!Ej;xJ3y^kU9Z{VAQZy8kjD3y}r3f5K%qcHi>$eoeio)vW z;R3cX8qV^~x>#vo1c3C&&Ng47y#Wb%DjfNwZvNJ3LqLmy2%OFCs7UR(SJ$k#lvdZABwu~QU&>nLS3GPV|g)Y5z7XcU_*lSWk zNpUviE`5(4p&I8;VNpQB3%}*&q;JnoQbMNY!fiym7;LI(aJlP&bH4t4D8q$ij133k zW?A7jar=HzC1#x?;a>02X4=sCk=+%cS33~zg5S+YPwKj~C6!LZUDjSc`&P&)mx+nKd>;)>^eo%nE7vZ?r`Rq8YD z8jJL_w;2f*@zmnlwIg(PrYL_{P%VmAI$CCDr#{QNoS@Z!qNxl=9#CasUs9Xv`%1}o zbsenTe(5_X3|OOeiljmbbKxR>Cn!M3xk({ay*DMmTs}NCn`-;3D5U z<+0WF7`H-}QQSbovYb!~x^&%%xld+YgnpL*O@AN&mE9mmQ=hb2_zF!74>jGi(9KlY zEEOYwd?G2DV?w+M?cXqa8+8O~@>=6#YMdKdHejuF4v?Ddy!>&`Q$5+OcezhK>DBu8 z@9aXcHMVob#;NZ)+9ksh*Yc$~$L^dw@qO+GV^7v-$p5;mW3Ke-7uEA>_!~}8+EC>= zn2L0iZVk%xCfyewXtUhOS5Wb*B2A0|avkk#XMUuMc+9B|lL$g24e_LZ;gmn}w z7RWbQ$<<=A(g1MRKOFi7!m5zQrKY$(qYpDWb;0PaP zS7FnO`>g{nv_J{XIm{IJ}p%7#FmAS`BR7;k|o%;i}@1yue&HaWv)C5QX74o z2oDzD5f9Tg1Obcup`ecV$7MrJO&*xDW;6ju$sv5RM9=dWKDFWxda}BGasJv|XwSzjzV*TLxl)9f4e6DO- zCa`+T;8~d}_NVJd@1sm2Fi%9AHnkw^_OpG)ML0Fp(R5lFaX&=QjTqCMfaf^^A5s-| zoX`Nx54Eq@jH1toEIKx3w(3O|Y$X69@VbN-!rn4u(@UC5+*@#pkt{&w%54GApF5Wb zc(8l6PL+5^f|$So7?OYjTzbyI)^$Bg-Yp0V>4F)@@k=d_0?R!@86`|8*N{d>1$5aP z7|`gH+u3&e$M4`Qz_7!q+sr7h81SgZQ3_Y}ZqzfML5_9E500g%?+Pis!l|Ns61 z8gcO!uIJ{7!QD_T%VS828qwC}IKYg7vRord=}oYTwSRY^-(t8GtahY!iZ+{g^c!W- zvI24PF4s%>sDCejm|cg(^Y?-Vm$`$7o~%t>pY!Lyhp+4YS87IUMeM)uJ)9Q#t`r;X zXgr~odU-{bBz=HUUJ!?vGiqs@^nUbpH$m`=bNiC6;O@)kkZqMF;*hGoawlM2+YUYb zff2%qn6S(5;#K%#Yfw2eQa35t7%-=r&2|re(ub!+T@3_>8ENoSA)Iuy4T{^BkH7n_ z+$u+HHKq6y9J?cfW7*b1V2GK5!$fkhHdJFxCb2uRKU;&il6;iB0Mj*^a^Lyp+UKw- z*9+u-*Q_5x1FQ9~^>F~ZeeKSiqJbvy;wIqcD1}WLQ57GlJZ|n%R%!L6!o)9Qt@#es z89q!X-qIb~;XWVk9QP9E+-U=tuBG>=_HU!uL$U=x$wgDyQ z6vbPrQ`wox%5pjfDsWDANL3#pGLAb)NGCT!e<}jG@xO>GVK;K)d-Z0d^H>v zIU@@xbjkrkB@E2sU6S?LCF{ZF`*e|Sw%C~{ziGx5#lxMnMiw22e6^#+ob(?6U8qIo z&onNhmi+eynOay9M6XDmKqX`&o)}dBw)4O`c}Y|@DsKXnMJ{okgLtYS;|bZ8!m3<2 z&~Xxld`l1EB8=5ia2jn!x8k=gnxR%6HI*j_B_(N+Bc=WTv*HaDX;0RQJhVH04*gM8 z;Y=6hfGAwuf{a9z3Aojk0QW9YzTZ!D2?6*KC1y(faGw9#?J9jU!JfpOa z>*4bv3qadh)XJxO{&m@G5mBO?7g^1t@`uCZY*N%w%v6!>l zSfnvM1QeavaZpc&ZA~vu2YU(G5=-*FG_1^6)wW(M`ONCXorjDWqS#5~c4o{WLGdO! zuu@Rc+>0B&Z}@-)AB`8kxj%X;KKvLlVI)3!RoH`*@e2by~uNkkj!@CA&8((+_M6QE!vKQvN8=%M+t(w%aNF%h#q zP5yd&;U>-@BDsF>dh(veO z0nt8mDZ&|C0J0TFb})|#iETSUu^evm=$EWb2(yY1C4-GWCJ@KZnI?1Lv2#F-;DQwx z^}bM4ClWUT7z*ODol0(5KM z2T68PFGr~*#aOgq2Rv9_f=QBmBg^-2mjgVg-9#QT18w0KuXDxk6q*(Pc%0QA(w)oP z`6OZIWWeGbVF5J*fwJ@?X<-{Oo_Bw}vMzURZ{g!t*|4p_iaf?|fL6ApL-ISJVi63e zFe_QwU3=dHF@bfxW)8O@$ zx(uNBdeMY_Rm%&An73}Q2?lDV9ah+lG9 z^$hV4HxcG1KtG9|pu+&4??46}#OGI|^pZ%-#R!et*Y%5s8 zgUU00ZK!8J@W}E6o~SxaCX^$8t49~jG}gS7kr*((5^a{FTfM0xn@--v% z$Sbr+Ai!jD6&7P+=P85*5q5S{8u65P6a@Df3!?HK- zW3K}T^Loeflpj8=`!DY*c0Sp5TaxFtW6b%Reh89U5+y_ga}nXCk|S1#5WXI*fiP1e zqWBUL8SwxCSgG|#Q}%kC?;;2vvI%Q=K=SeCN#wNdZrv& zJJTT{{$%I_r;$M0h5X21y`Jzg!StzwtCdEnBrQ55Gg@+nt_sqohRj=9$?F)|2dhR9 z_<2#M7EY1RZz)|U{n@B^seoB^m+4!yK@;0aTKMyL!-W1v%UX!5O?F%tI*6Dshm*CM zY=j*QzJxD%&;bwB^5eEPJ`lhH)ksee?l)l&D6|JuUuL33-X9l7@rns=z9yw27HP$< z`;k`MukowTNY|tX?@0HCSD8gf>J+X*ZQ;^vy(va zp+>2cWaA<0GULA|QU)adH5m)`;$Q!Uhg|0r_5X(A6Qlt`7Z5b?5@KSd2QJ0{abL0V*> z&jNz{=#&v#KE75$nS)W(JA%0)4_#%6N#W{q%}U`^9Zlr558;s7A_Bz+rk_}yByp5y zABAXsyA6Lbw}06YQNVU)HGf>5zC-{y286R(L>aP;Lo^XuQc1pRxMG|MDYe5d8I$Xt{s_6>N%t z1ye5{5b2L!uQ9q087`xSbW95Ph3p)aR`j`pLRTHZ00VEigKNIF!78uiZOqVC?R(&i)-)LRe5r- zV$s>oqx`w|oZ?)=5tM4I#Huy^kj&zI+{slyWLso%W$4@LYS%OLXnUD!m<$+6`854J zQpwm9aq+(iAHT`5)d829CvL{@t~$l{B>1Gs6e6TyjZwtop+vwLXm8hw9gNOjV1GNq zB&+K*-VCGl*1Mi9x4&xwe4Nx{MR+&TSd6@+`$pogDbN5N5ySNbA{|{~F`e zJ>J$uD`QJvm~X#!>5tTPLs++{_(NO77Z@f9_7OF)8zVDYYVc0{)yMGI%<52stcr;+iZw1=sy%}@K>G1&j3U;ux^_F+Bum@BL{Db8*0n%s4= zb0n^coqHsP=5gu3_-5G!pnKES_OJVHX9Io@35{I|5K+a zk3H@V0lKN0OV@m1Q0H$eGipb2Z4K2oST8y@o?E_umv75-OuTP<_NgVaH{Kh;IAIG} zI~ct$<8F91W%19&UC4QO!*g5Dw2kcid#}f{PlqqM=aSa6FPJmW7dg7;KK}jE%^h3s zpULaE{@~f;08L8P&9xWvj!bp-5r1p{(3}7ES`c7(=xxvy)%nL>Z(5EYyVBc+G3{3hx-dB39DY*9U(^zg|F(i>4JX(DxvuGpgsc_V0LkM6S@%j*&`sMxx)=--F>!`hM z@{swO%oh*R2`T$tCs;fEd4Imtjdo()n{^3}VP!uSAKzc(^RRkSz1+v86HMgm# z+MbXEm9$h4H|^ovg&)m-So6=F;{30UM}?8A>i2=h(QnT$s;p8;-sWrwvL3sI-E+bY zm$JeyvNgu+VxL_lZ)~^S7z9Ob2lvIh?ETvY#-cGpKQ<|rhU)!y#P<%;DS?!gn;hcT6C=m_vGE7Z$D|_%}N+I zFaP<9EB}Noezmgv*II;}b= z$e&{whs~pDVKd7XCEPs}Nxb=<-blp8aB%JMBbnbeP%62v9k*nVO!8G~LQIA+ZjuVu zmnnkt(Bf;gv3ayk3)?CdcohK6O~p5|F7`WRGD(WgG)wH>lWyuMxp9iBvQz;onUg8&#rY7!Ja z_hX6QT3Vw{DfU|Ypt=2kkG9=!t^}yPOP2&4`W9bRY&jfV9oktxIV;P5GL;OgK5^ne z)ii2{7UelJC#EO+1ut*H5>wNU9?j46-&M!L$%$s387T2)7*3&dIlRG+PK);mV^2L_ zdIJWS_+G{;;%iq}K(vwuGTx|*2TJD87d_j|!aRN!`RRUKgwvP^pAR0aPS&u|vyH(f z`z*IVQ=x8rLIRUIJ;dN++4{HroQaO`7Kf8g52d=rpOd<&|H+)PR51MqqiyRU>`qU~ z`n{jSGQK&LMWpsIUBp#Q9DN)_z%4buvLL=u^fJn#VSNN!d}mC1sPvChE}judvD3#6 z@&=k+&c8a5Y~g#D*IWO(Sz%+U^ZKXhW|%>k9W-Iu(|~kPh$`dMaI+>V!OoXu5NBb! zy5l9%m0yk(tHk!*O{i7}7G9x-`zh5ZV4{EPGC+sOd%yjhyJ62>9~1g06-5)UP9>?u zL~^0gV-?oH*};g?dWWy5XNGF>u75pq=`uGILOE+t$KEMWmp_T zmNrpnH%Nz0yh`2de^)TNM^H~l07OpO7^knn7Kv~0?dkS#TEUN`e``;dOB}Yk=kK8v)Jv}sbi(_pINaGrDc{@#97PPYF%(Yiv5p#1H$I z9wnhaY;?3n)nuW!W*5kFt??X?_VvsR8=rCv^AipjKbFgEpLW1D%+kHw7UW1kX`%}1 zf*bq7mS(^haHn>-*-v|zL#U_(aiQGM_uKay%;MJehugItqkmUhw?l6) zYql|vE)1kyzebcWJa2Uv^@L2sApk8~q>W=}5zak8!WUcr|Kuwm0Eq9qak6*ISR!`p zuuW7yrbA=^*SkfN8$}>Kr#DbQb%?i60VBe&*t#O5S!WnuXH?fCg6QUhJ%CG9@`vu? zMEPI>7Tdk_F|Z8|v?GkhdO1kMrIWXZi>)|98$d7q>JQzhU2_WtxV9r~IPq#9HltlL zp((9nXbt{bZr!iPQowC_m9}6a&L6hr%b>L#0$PBni88*7FmBMrqLZcm(i&R@-0q7! zIa`F~^$)*+=sb?%yE@2e08b5dnR<+mPw*LoQ?eRhVu@9X$VJe5y;vQ7ZRmY10?>CF zu@e0OhHJ~S_oXa+*W?yns7LG56&i$17hu~BcMeI$@_OA~*!!Ml*+{|3qk7;Z42$ja zz8^*r_BwX~l*oy@t(7t*3|E!xE|>a7acnXGPrBssIhaj1Yv2vsE(6Xfd(P2`a1fhD z=U8WJj84IcRt77iR427KsLNbAtK;;DNc9le`k?=*6g|g)Bj|N*;zY*cfP6T5M}kXX zZ%J$zjfx2ChsAV;nJ&zmhB54U=$DQ@>oL15Ejz<_&MzBRZMJdS-vM#IF?rWPqN+KL0OAnOsEB>|^I9hd>qLXF|UyM9uMkWU~Xr=0gG52p~$b z7?k+7LLT4MB|r3;5_XUd4(Q+kx;|$m;2p*JO&$6uXT?QI%G%lIoGOuRexJXhFBAp_ zQYAt3`}!HBPYC3ul?2n-i#jGa@?M;N?9Y`jnKn*^2tbZ-(NiU9fM*pM{lUgF8=^N^ zGkJ@`q~Jib)QgAoRxAR(tj(2!UUDk%U5q9?;c(yvOt$9H%S9u-ry{b*yev#-7I~oy zwoDhgZJ)uRJoedZ6z5s2qH6`QwG*UK3cdz5%~9IRv`I#m_p(+66(Wige%p7g}o;qlus5QuE%C?^p7I( zYI_+X{1;Kc6ua~spUFf7cl6?_8c#)WJgEp=m9-z0Jy~7m>u<$=3G;_GxFxmVTh%4y zAg;AH1W_)uaSGMj7AnBSiqMmL*nb!I9^5s` zd7%a~5J0wi0M|#+k(IGXJ(=ETAKA~GVoC>qDrZ=W8&KGHeVTQE5`S%Pvf&f zcu0e8Sjv^uFeT&-A(R@8v-xCj5gY5_Hg@ufXO`RRdZX!ek)}~^3YVqCZc2G*ZZ~}J z1gfE3s~*{Z_X6eskM(Xff&88 z+D0v5(*bW?D2$`$woAMfy_{}0Knw4A<1ks=w;v~WqeBc%-=!-ojgKcRcmtO!aqI7M zrrirHZzoK$<%_2y({n?EOl{KZmH~0%WX!I7k3J7fPyXF%m5qmt>d=T<_DNXDHNJOvx zHkIGEEKNt48ac5L`%$uwLnP=pFJMcgy>?@#aor-c2b&>?xzrPl_0Pg80sSeDtBUOf zcmXeZFU7U`(b;51pI-+oL@xFTKU-wQ56g*P`vTNu$R3TKFxVx*Gy!H^EE~D{T7uH8 zY-7JYdny;Pq^U6#4qP~+w$?)bSRBU_pCkCMK)Dzy?Dg-m-Y=5ab~ty(uQ68i)x!a& zc1d-t)P~HStt=0j?F)cTGmF}|fT)8fC6JHX=A8Cxg~O|0`t*y3HdXFQ$LvHA^k9Si>Z9a<0qwnh+Fsl*u|ILvY8myLT72H;hfYQ#d+ZU3C`sKZc1KJRx@$bU+V zPF{r$In)MIUkls1mO1lk4bP>k>+F4*R~2JoZ|$Kh7=_svJQqshada~)f{uGr%KAce zpeuLwF$~!Ap0DnS#>~Nei$M0lP*`Esik;%sXX=V>!cy*f0wth! z@#iJm@n$p2nI8M`qayLCxsH_rw z^4W8m!@SUhX%M65Vs)?@3O2h6B*j+m5%Cs@A2nljsV7B}%b{;>F-~LnUod4X-0Tr- zJIl1{D}o{M$mm`46K?uzTjOO&zdkSgck#E~&OWy^B7J3sJb}oE2aa0dsGxs0+<@8_ z@MYa>Y?8a6&zeH_q>9`N^-y2HeH0cP!Y+zKZduLtR5x1~`@^o=8%ZTDv$Tn7sAjD? zVCu-B9TN91TF#4Jz}eacLOiI`lx~DF?~%juA>TQ;(FD89#d>!FGN7ts91fNZu|m4Q z|9a^-r6_n-vouC>`R($*!;&i(dK&^(N;6KhYeWBOZ7;Z`{Ft%wO48b&=TfgUxr}JH zTxnkZ^Tzn#q8N_z!~|9j;fvUIP^}k4wWUICDoOQGR~*j(_hRQI(#OMj{=kgbyGY`u zty}jhb3+^|NE(V!Yl3Hw;&M`IG@tcUb=%fHLN>5Jh>hvjY+WhcS|UO-CHG{Y&#zD{ zAJ&FE!j&U3);u~q6Y4)Z=r<>Z{~EmUm4$A#Oeg{|MW6v>2XK0~ZWjab8>-ST`l_A6 zMt{0ZIO|b(l`Ey*=IR?3(`6(08)Cw%W67RKAaE>nz#g`9|NcX7z0yts=HOWAPfoBo z6(A#8ow+UDZte>qh%)Ob!u<{C7h(nkVLaIS^0nmPE=-~%XcpnkolE#7>-+EoPvG0| zL2^#l_3RaaC7dCxwML!YG7F>ZWuL6ycmD1UFEV0VI0B&jQr~9Z!X#^?j}VZjrxdb~ z*mbO787N;|xo%%~|JJ#$1T)=4;3#000|Wx>%0*uduoYYd^jlkKVAJl4=85DU%5Q^x zcC%N{p2Q~*r_UcxqRo&^Dcb+eu#4=nKebF^cwt+$k_b1teouk#M-;A?7X4VS+o3+p zWV-{$bL@BjcWTcr8`I+hwh#Z_guRl4?}k|CS3@w!h7P!Dt)GZ07Or1G7K|pl&oABD@I6;}9EBL%GiY z^Zh8Wuf)zJX#5%JvqW=rae7^Ra0d4vEm0L-e5z_wLR(BjaOS4!%@0pSor^4gTkQdL zB$ZSptgx6}b#B$el!E;MTc$2pEFV#geRrvbW1Bo@Eal zLa7aVs&kilxTDok`#0Rn&daI9_zumS76)z7oOuu_yC8(k0IIWh83)+cSAfcl1n^Km%6E=~l7Gd-8AW zfJd#Ab874Lv$`2W<~1MKc_ZnXR|rc63^=?k**?`TGYUR*ZoLPwg`y8!q5z6^x)=$< zvYSH=B|_!)iJ@H4;VmJolA{)BU0#TgoaEu!tz@{s5Xg8GVZQcc@a?9hes5BfbJBjO z3GUVQRTbXgV86d|%S<&`^%A`;^S`UASNKNq@=Qh-2l&=DnX?J}3t}m@Q z?@HPk`9o;9A0X3veJCVB17Ylw9pL4E%|qH zp2f5;-e>_hziM^zQwrK#FFlPj1!y+rQ*JcG>VtE%i@v|Ke{D1?`ZQ>}fS;#*?S$i2 zAB)YbrcTbG9#UiopecqccY0sr4YVyD%a=?M0g!K z4X^0hWNb~0@DiS@L@iuH?W#T=X_V;mC-u9IG^pyIO(c~Du!>1iABh1`v+p`;+OS94 zXE36G^U25ec(sPn;~;5GNFO_ki)|GXkrqN4iZz7M&7pfFHBIFKRk_07*FQ^!A zNa9t-!t=XDsHWE9K6Q5~uS1Qe%}FU6?!j3%y$EOYep-dTU>D{XybfP06A$9YGdC`z zbZ#OW2|EtO^@~y4XD0rfB$jcr6^1(M1$=l^bIrH{T zLRxat76fDls8#`VSbeDAO?o26Rm1tDHLsjG=)jA&?EE1(xKBiL=e#XpF~trwq~br&g~29 z<;B;mPy?f)r0VZ zV|=aSyjRC1IfEh}#P)6LF{+j`o?*A#sdJ(QV@kE?lhht%{~bcdTc9I0aW^^(HP#AM zroQ#gUfKbPllGr!y6_Mcxk6K(Fex_r0tWn(puj#9pL`x(sb@>GO0!+z#&Q7Zr^Dh7 zI$J@h;Q@=}URD<}P@&*#IU>wQ5!7=jFz?TM&GsP2pxiJ)FSD zsDnHG)YQ|kwFSd8n@1i;gAc-fzn~gf1o`LpB`ZM7pm!`NzG? z*{qN8u4j&}x;wfyhoS(6Z8nLjD;K`q9&6$IGH1Y5eT-wjZOMqOe#XUCT#%8dZR5eR z5sNdr?(^jCPRgfl3(#x+vZCjO%gW2s;W3v-u7td(jsD%V6Oh_NOwV-s`w{8BTHot0 zADt_`8FQ7E|6x-A?J?CH@c(@FcXm#|x4kLp59T91t60qrl*9k3%y-b8%AwKH0{}Kge^O7M`8xTSmj8Ok8oO5WM^t;vT5q;dKmyt`8wRL_Ueha5EX`i-DGAd3 z#I7y5PRo(vD9%MM+4boD6&s)Y5j~kh&aI)v+s%Pva2xC0|MVYv8M21%xh4|A17i-T z^s%Ya@BjOLy>!Pv1;a~#sIm;Y_J6%k{y~<%!wsM6^YKY#t>f)LC@!RA48;?WIJ{AkVj)*(E=M456zf%hdVFw78 z8OO6!C`f?LqGCv8HFc2-8|(1(90gSOwxFBl#&C0+p`ntX(Igv9RQ!E7G7=&!r@L3Iuz&%0mChpdjvGbZJ(}*wUt#f=kAdCX!|r$Qy3-sJTgJ(w zZm)by){WWh{8atD9TgANq`dJWecC@b6ul$IVDZqJeclEtD*i4&zNfa?GltLWK;*tp zts2CmTnsklE)aT5@LBruiO%L+T0m=z)MIYJ*%VWnmEx9Hw5uI?Zvbt;7)?IwcrDMm zL_nCjL;h&w^e1uQ?E#eiAYyGv)~378OYVM+^Tk<}qla$UE0 zXUppD(p2+S1(jI2`dn|N!AcW+|3Qz+8-&}wp2lq+tIv84#YN2OA?J|c8WU{xNuwWk zjh-xbnisl7jm*@QIBk$1pMC0YTKTig{WdvgUwayx$N0-%y{=c+^$jx2*o^xuz7qe; zzT(!&;5&FAT=MPAZ2$2Ky(%i#V1%%IQR-)r_1S;ZRxMg_#;)MQd0)Z4y=SeAz3%I` zI$ZE|PvwGZPpkn5BH}^UOH%hpop+TtRt9OVe?o=mQ1z8=n?6@Z&4fgjc^v%s&$Gt) zM!$fEjVlHn^^b)vkK$h`svTJZ_AYRQ)Pxv3?zi^+0a@nNj*zwQ)H5Mf;{TojI;k7N z19a*RbsVmAR6=vzF$O7AYzO?fuE{Yu6k!Ji#hGFjH(iTqGYWihAd7o-=D0IhPUYWE z0w@0OfNaD36HiZ(CTg z%b|6YioCcYVDR{(WjScmQBe=i)gk->qtEcgE%N4a$BXAz?lBG+n&S=dB1X*!RuA&* zz8W|tco*sL&gqpgDx4I=)`1xP*q|SH;Z+bnpd03Wje|jJ!L)dm*9%4hp_twf z<1-(?VW9b>>Q#ILnE_kFe3nr96<-D7#v5^ja#OvkKyksX2uxgow%luP%-5mjR5sVA zZGPf_tgrZfh&1+!L>^hju^$)}BradP?p^}n%OO(*`unl=SF?x&n2ubvnO)t4L$5So zURbZMCDehin+i9=X;McdmI+^J{@ zJ)m1!nQE4(%KBB6#SFD|XxJDeF5jDK)H6=QuG|_OiXnJX)Eeto5PB38E@Fq@m7(Y? z^1f-ZZv$!q${H6W=Kdc4rl0%`Ld94lhyu*ZQWq=$)TTEo%nXt|x7@YN-u3R{oXcm- z>9FAH#$dUz@m`Vp%yH5nYGsPb&_Bi%a4M>OKXJ^Ry`7b5927Q%)DPtABK zYRtB-9eVN&6K;|MCT^&9wWmGgmBf89jQwRo?CrZyUs}+j}`b;iA@7)9fL0k!g}4! z6N~bIHO4W?QN4(G>ITiYP(O=}d1TE6NC1_jZb%PZZBS;vNUA{y1xD(DWx?PEW@AQm zC}}SW$jG+g_F1QZ2ZjR-L)7H&4TQ*A_gkxT&XeL-sr81aW6K#GXRcN2P#ry zW^^{f26CuqG!81h?HsacIU&0j+4!J%bK}O14YtBYv+s!Ye;;y2qk(Y*5ZbV&keoUy zK>hSH?loAzJX8A#o81Ga^!A-44 z_00g|KG0ZMyn)Fv<|s*8b()Bx)&zBPjX7;8bDhD651=M*SFKqb+wkH{)7vm0*eSG{O?4AHz@&6L;q(FxMhhDnEHj9>gsL4`;rq^!6GO-=H4J&)^t=)8t=R)!yWdOES zLw@RzghAVDF(qpJ1dr^Y()?yBwGR4zZ$)pR z<4a#iEoKuwjuk0->!^lTQ)vBS-x^B_Q3dPi&%eP-1=gLiH z?aXNudj*`CI@n`Z}Xr`Pd`U4a znj`Brp#W=x77#A4wB4SI6dX5Ao~E1-Rc&Y_PaL~u*k<9aFse<%hbd5BsWe$5UY2Aj z6PlV>83cv@=>QmpnD2AqEH7EcMJeGfLXBUl&0`|gQOhHEv^X|+e9nN*M*KHu*smf7 zu)(v1>%&+tvNi-pLe3uMMgyX)2GUEPhAfxf9p9{gGgKyS&G;A|vtLD$0R(Rm*(1XG z-!$LHFB;&-S1de~_*|Ar90<>);+Y(>`5Yd;uE&d`j|2cYs57I=3Tyh)hw8)jd71IQ zmku-VrpELeDvA)Gc&P9<RCl3>#FR;?PQO~y#4^@(5*lgm9^5=lg-y#bEh~h%p z6L7+Fw*J=^18=Hdn5_t_FleH|ul`MZFm-)sn6sqRbh(n0D$7&sXQzE0Jf@xrPx=3i zc(s48E-`*iv-ULs{zoHC^?T3qOrP`gXGF%Gyvl$1PnF=dPgL#Mf-uV4cyzNKr_n4V zqkl_EK+|}r`Qx0_>T8iI=wHB8tTuqJdyo=?|0Y4)SHMy^$b6k|Ple2cMpN&>m643J zi0i|)_o?vwrsrqGg%`ItQ+Vq_IoJZqW$ zd1S383lY*}c{WJ@B}l2Sw+)+{e^z#xIxG@cpf{<>{5i59Spb+zS=Q`L03Ah*zyck; zz<53fE)(=?xp@L= z*{ky5pkyG(s^S#}_@x$jvnv(L(~fr4G+oo@h_-+=HzB0RFe0|>_(eenps6(B0RPCl z3l$X&=Z?!P^;#L0e#OM~@w z1hBq=bEOe>SYMb1q#pEdu{AnG4BnU!oKk$Uj~I1%i>n^eR_aE+aK@x^$k@&2VsauQ z^LWOYCV<#I3}@L?KR&1B(^sS{io-gN^CSd6bV`wKL{Lhx!6s=+iHXa&hB~FILqh&p zgtUN*)o~_H6a0In9`?J=O<`^JD|MNVq%X=adY}-FS&~Egp7yarp}T+tK#5(~-BOWh zzY)x+=Gry}6meL)I2iz>PVtqdE2nUO22A6D6~98R7GT^d8q)7ke>~`7_c@0YY6mL9 zE#Jx~#fL1p&ks1g1D5dXPRF>SqBEM{O79avBX?cAGtukG?pT--rVVuAqHWuTf-&KY znSl1@?b2z?LkdP1+cVkI6z7cwBdqOvRh%eSKrzyeDcc!(3h3pHnq;yxPj!H_TN8`` zyO1$=T=@6)dMo~=lEXA5wa5pXUaPH&p~-N^RdWh#=;N1gR=ddsAHQqEIif@>rgyT+kom0e+(F}NLGul7s;K$8?cf6B3M?oxM}uCQH{JAnBG5yH zQe!0Lhw3`>5=TR^rHbZg~yse=9Z zDqO*8WObVup2|~U!=$hqu# zc%HD<=E5m!Ag^-Emp1_Lbx3!>#vi=CTx{xEboHc2;%z*0*U7I+q^nuCwOC#i^W#+z z&b?8~ihdBOd(!9EY&9my>)x3-b=i(d7LEX_a3S{&Wm_r?Zc%5uh6Tmx?30N=SGDB$ z%4gb>92Zg2L-;R(x!Twp)GF-95eFid@Tx=8kC& zd9xN35<_WL#w8qhBSs|TRsr!3U10WQDZqfFuKX7(D zWq_E>`7po;#Y!n0MOuFyr{aY+CPE|-AUK)xBUSJC6zi&A7crXYPkKJC)Eo|in+lsr zXIY^?0fpADjDrY<1)CY7(xo|DSwWBdeI2LiwbD6++2b0|S=xb>J?Rnxxd5zcJ9j7{ zA>zdPIudTv(%66F7KFDltkw7&>}HGvVX{1`N~nUKb`Ki^a+POv^h15As$l99FN8lz z2!jLBrUG4bFBeJdw1w>}2sDmRK$hE@foy3h?3J)AnPQ>)jxRE;s|0nsSXzIvim=ix zO4!HTOS0s%JCD6h-o)9>wo|UdgR(?+dRmY^T~W2ibU9dhWt z9>e3`?RR>UX|uWArV_u2Zo~!)*PDTi(pVpwKNnm}CCF*1?xT!ASxMv;a1Yj-9S6ysqpG5`m}-OQ5ZXgz&JmmY477DV zM!W-^R?yNZGuXrpuMK#MJ-b|jSx{DO{g$QV!<3%S=1MjLu;~-yL*6ku5W;nv=YPg1 zt4wv7OJ`GU$ zDq1#^-Pn2$WIj)zbZj3&ZlA;NnJ%h!*Fl87%)!?RznGp_E_Thw3h$3J*N;xJjDx(+ zuwRYAt~09`11UBRiwCV9DI|m`){%3`no(gN813C%PAY*Y^yo*HvOwF|0KYdI+p4_w%r8+p!|#G5^<0u7DgRk~6x zJ{iytd_@eP;^hq9i7EKycowr+VxSKl6N^aZ&h8KZGa%d|qMT*Ue?vv*i$vS1tczDZ z8vi5PIU#^i)wlwYs5;F;(eCpVGR|4Oc4h2ZxcCES!6K>9~w=V-guDfTopNyk3-( z3crtG6PJV8e-SqahYDhl%{};c3Y_~4)_Ms#wuFDqBHh#_#NXWl0I2IqttVYt9h&on zR(*nol$2TDh5!`{n>d9WR7V5Uoazx>OYWM4wZU}^!b>hbhlst$@&H(jM+$QOI(SPu z@>{g^tfH)rr+)_69M)|)sl*EM^WHz$w3-1KP_-HrU%{ts%=n^&!S+ew*>M*+|&?XOIqlVd2opFst zZMnAj1rSch+rE+TxC!!8A}zX{S5uMj7qCnq)QO1NHfhiO1ljYDRstwf0GZ7|tGkdk zO2~`kZKo#M%Jf#XIiJhb4;L6D-hr=Kx$qle9S_y2&v31~Y_1dE?be|N2gXSgv8>$* zdY|srT2sMfd0^Tqs@!dRW9dpt$0JayIWzq(fXIg5_o`>x3nRX zhtwIe(>hQ6y>aVhi(GF(jTr+**%=c5vDRb|xhjC+G>P-YQd61^3*@w#i6pP=4ed)ylvv++&MF1cB*2Be+t_KgirL^;3OayA#@a2rBc zb=!!EG}1c!$6si0*US}aOUN=ZLy*j*Leo9S#45kA94Nw=|E?K+kmPo9if^Y7U4PEr zZ4E8<)R-0I$pS*Q2&`VX@utTSrKz;6RpZc^l$XOWyCg%@EAM z#YzF^KeT?F@8BRq=CmSewjgn!m1a9Jriz<0vq|H?YH--q#C1+xMCTlI<)NcrH<3mqh{DD7*^LTu~Hvmcd})$FkvUusLy7=STclP3c^oi z5l2MQ)LNG`dW;}4%o&~`?8IT>t6IpljG+U&f(hI~sDnx!ZjGFfMqvJmj;(|`;S zyaFJ&f=;-oGl+=1+i~XZgf16=Cn@1xtj;j-?2o-&N!(6tGTa{Q{s*hSF!x6o2=`a$ z`NIG%1HKB^LnChtlj*D9oc>ymNFgFMfbh5SPUQC=s`hQO7 z`t9o(XzB6y=}nX&zM0Ub)kvS~ovysjFoDbHzo)%IiNw>Lc-`(K9kX~G;B%~#c#MiC z!&#I1vwXzeEPGqrxeVDkLTC@1)*a?djJtjw|FJt*2H$-8;@Qa;`g4n6K7XC4!%x7u!Ye{-Dh-G#GTy8Kd^R} z&|A9c6;QFPjze!~4>+tbu2< z{YlE6b8pY8Z3mJRmu=pjy&C$*VV?nl4{_}Dm5bl|-8T0wmBWL$eJ{R4E({GO3=f)w zi8bfhK38Atb15OigVnG*-hJp}u!v)@^tUh>%)t`(H<+466;-jhkQG!QBgxNxsd`P>`&&f{~~pIC6=bY_cX3&^KV;_ zvb)I^7YAn(t&DfpyHwP3?~IhZ8uXsw>r`Q~{GBgkoi3dn0;-D&5*p84`;C!3luebV zQd^TRZNkovCjHoI_`{#e8PPfJ9Gi=nHir|ks^cglvt_R36@%NcgQCK*$chH;+k#!CpACM#mkKC>sbc;j!X_>&C)PhmQ_pJ6U_AH*vW) zgN#pu!#+O~C3SI$p8}3sFrcZO25t+j<;7+LQPTe-F=Ndd8E;f52W-+uTnJ--vx z)I%}#nEesd8_bJHZt&W5d@BQ%Z!%8jkGP*}*9~_je}ekf1(X_&UfHu*e`*LhI&>1l zdi;9*<~cjO_YjBpTL$qobj9agmENtd)1TaI`L#tsKS!>h_5IXizg1lSpzk&`b$j)> zef`Jv;2|MzKaJaDucztVzw{X1rNtU|KPJjXhA8S&xyY}Uw-vtSdoCdlF8;zH)*rDv zxhQenY%=ZN=X@mQ_ZBzk)`e?!z7wh5b{3mw+=Sx3YJIzvZTC{dzpoZRR)P@4o@BSh zhW|dVfBpLS)$H4fgPE3BZce99?cV<=v}|g1!=sQ(kNUP` zv~}Vl_Y-{z$8_yw?mb@X2U3aubx&Phjob#@26(qcA7HuY&gGeV+3J68Zy{G=a9sE_&pZFsWkbLPx` z7HaGswSL{?uRr}$ZtAAMZeQ^nKtC$F}dsvgzTr(M=x_H4I6c{nj z@G8?b18A%bpUGc9Ze!SHurN6+SZ0@N##=G&y;{Yt<;9PUv^AFVfVdr z)NZqTMwy^K_m*AVz^lI~{V7AF?ib<%|2$GQC~x1oPCisUbz<&L1`;g{v`ryK{6&0( z4b_4wlbi`=0EQXgw2TbLYf841%RW%j=sRToCs} z2t=NtIw=7AoWw9^8(bCWrP}Xlk-U9u{yIQ7_0{O_%~jHLaIp(GO2U0ROI(>Q|Fu^QtIR&tvdj_D+rUw>2eC$yTa)$bkL#b562^r3gxtd~7*0`#Xfn$eY1uS3zTO0{ zc{K3*eUDY>r}tzR!h5c*Em2Fa+p>a#p)SD-Z|HtkW6#X)1_T%r7H$rxynm3mCkL{E zDsMJZpYk?4DYRakfX1HR*|c(twA&oFj#kO+KA}zlS17*p$N8bM>mEo^R@4HdUu8~0xomv)4!n*%Y4sC$Y%&)&6ezO(lEB*sFA@DjjZ zz6f^Pz_-Z?8FMhRCFA0M@UOX8{NA;VEQ>s(qz$3}REFYoY0WL+$5?J%T%gG%_`fK~ zkN0pELk#b|A=l)IglnD+Zrt|?U|m|%4}`&qC;)G2Ts$CO&>TU+M_+Ek9lZu)01Nzf zx~sngRSp!Pw+!Y``ojqCK_K3?xQ+nixm99zKDW(zA-lyBp^w!HltapS@E zwms7^bz9iOu?`1TTXzi7TFYc|S3p)zZ_z>n7k%Aq*qb-5G6%fBG}G-RWd^_uSkN7P z0gC*qX$o2*tk3ebeYx%cTzKeZ+X%@0DBIE?j_SWY3*(jt8Ak#!SGv&D_*R3h{{jic z+s)m8hd=@g&Pqi_zszXF>73FRhFeVl1+4Z(oKK#duB-3Z99+DC^{>@2>*_tt@J|&@ z3s|gOCs)fgOfe4k4N_^CQ{P}vCppV_2SfEaB=V2*+&VhqA+VI<1@y8)afgzO#|{}* zuK)@oAYh@QNmrR-B`IUgIJHKlhMYR`6reXUETcb#BtJ!Y`3%T1CnX`|A@IaIv;qm5Mn6aE^shpk~I%N268o8Cm-s z{Eer43$8aHd}*YesR8|hK5sRjG3=FpG+dw52MLpOj|ZKVt3iMOEG-P3U~US60p=b} z{ssV}Zo$y#1Ex?5M>V`hPJ+TpU7Wy2D&b-gO} zhnvjN47dR(O`eWJF_J|~nEKct*S&(VJHA?P?fEyO$4>wG&#lWll#8w70+sge>ssr3 zcUe8&*5Q@u5u(PtBxmpfno6}-PXP7FZfNo4f`56}BpZ6cl#~1$8 zEDUj$P=JEE@qscq2#_}>bz_AUMLc_Unq~}W+44|eO z-{P|gnQEmm-x{p|S;!a#Lc4H?RCrBn95iXUsSRca7#()1xPvSYNHb@n2b1MpSB5Jy z;7>FanI4S;`Jseb z0qirQ5214zho~USbYi9goiILVH1$_;rdJxVw+jR&F~G+l34K6?-JyisMg?pC)!kew z=mQVU+ksE$;W=o}=YrI&vVn(His-1C_Tu=yh6 zB-LfJ&NRZFwlS9=>Ofgb6?{$_i0u;Na(StCdN~MS+~^OFA{AoDwOU`(Bu}As!P%2a zCylnM-_opY^=ZUs8HUSyY)1a#U$G%F3 z1Fl1mHheC!gIKx?WB`eI&9KM04+A(U6-k0Lq>VyiI*{m)TnN*#R2zh_CXvjFyl)`Y~$V~jsukl_}_pqNjbxNuA+5v^UNrCauKQ8 zzw}re3dnD4Ar(nTWcm;;uBH^9H|X(x+#l=laPX4QXjs_z!S~VlAcfIL$)73mkhL6$ zofDYvP)lX)E!R$5U?cK{r)DNB2~jO99DmcZYJdp2wxyY?l?bPH7(leI$9U0U6)krH zHZBGl;2Y3stHmLBdx__HfR&?IO1aQmXWpG6GJZ)sAP_1LZ~Fg^6M!qPa~Uu~-X}oM zH}be+T2+y%KFG70=G}dsJqJhGxilV zplLbnwR>Uu*kQAe0p7HOu?3&WIaD@f;CVH3UTpf$2V~KKSJ z0$DG1RsqHdVeRRQ65m-p3?e~`q9&Gl&Zv>O^K<0%ez&!}My=aXmfDt#9btSgZEsR| zMJHG3;P{T1NM#{5wU-Rt>qezdLWUOs)PtB0$lPHFn9Vx8I)kS3Sl(k;GBrIzv z5}7v&#{1orCAHo4<~ao=o@X(_{P`4gMiV*%7y58?Wy1U5?xWdHKKw7?>OcE?#M*_4 zvM#QUl=a5eh;4TvvcaH8O~w~TtzM!^I>RhcY@*0LsjqG*^Rna$Hy{*;4 z(SHig&~~-(nxw)~{oW^K0;$?4Zy(A&Uqy)Tb@{q;dfiHZ(ux_@3G7FYX%$IgfEE`$ zIr<~Il~!@@BPoCUh#Og~v9dh4sIUdlK&bhIo4i3%1gFa;5yk*iF7q4PYfm4qQ^--7 zfKGe!K})QQ=84Tm0CuM#KF(FG81idLv?1k4zI{auy`J&6UQiW>2G1 zU*gp<7#OMB>7hFi_5HF|I?Z8HnATUEtU%}lX$^iv1n-m>&h-A02I)(S1VIvmCb-70 z)F6$ccD6rf4pR!wZx{E%>!np7uwYK3_A4-UFdnNUM^+4(uIT%6O07zG|$^j2ReG;2>a`gm=Fk<)TmT)y>VHa?X zE-C0M*(^sUE{k_?N_NSl_FcdYy*~mg`}Y!&h6^Q8Xpk}q5a*-5mAZUER^Ic&^mM&$-Y&$PEW^CE^Sw58*{Ek?B6qN}*M9KD#PFR7Inf8; z7hjw<`H_Z9rvn-^Qd9e9D6nlU|zQx62!68(U2#}f40VzEm|kh#fxK_gM|jw$5UN42{Iu$oGR+%nC{hXw6Rr=ndBho6Ji)R1!dro>RN*8OQctBdj;>LaWoW9zEcPh@mUDTy7l!Vbirs5E)p!Dlq zvBrusWw5)xZM$Bnbs?Il}n`XI4ImEEXF6e0SWp(@~TpM z6KWF2dN{;$x|>a4(;GejF)MkvPi%f-Vr4vscD%a zu|12S#{l(hJK7>kwU&f=#A2Nwi7(LelQ;R}1^R>nj5sG@HcPj0L0VlYD*)A+d-Bg0 zB_ychs(6)6KY6CrHgbC0bN`$3FMkM z05gYY_%Ka+3Q(c|pmL48Er_x9Caa_hyusiCG;*ielsKP!n9C|9FhiQ15l{D^quPo~ z=MQ@EFa2mn@1cV!ij#A%fn8%SwF}RtUa^ zDy<|1f!I!$_54yDBEp9b@YF|N0YWDfmO?KICN%33zfQb6whyJx z68>fhHdT~q8^O{cg-MPeVDlFny5-iyf6v4vZxTY)MZ?< zONtB*6&-*BL2lK~XfV!BWN!ou#DS~$;6?M8Z5rROqIu!N;p#SpTp+3mtEPT#ETdZV z2d~0QVS8py$$6r^gXD%x^Uq3$FaC(!ZvB?SM-Hl>f){DsI5$I-(_Ti67i4V z^vbpb6u_!Zkf8(#FhK$G>FaLPql)x;Y3)K|C9U;#RWVg?-6Z?yr694Tyy_wGF$oQB z;>FxNgVdaI(Ra~mGF+c-0gZG@ckoI)Ftri#DvA^qJiT771+MLpL`1GaZ)lD)0*TUk z4Urosxu462g#?veOBEXwz;=yG42j^Tg7FMqhiBTLYQq7KH>a7ppb`66&s`{I2I;~n z(pzqE)FFH-06pc%%plY)1)8rw-1vmt($}E)awrfI7EB_gTunKmp}(rZPX-#bBL%Dy z9jW9!P4^aKZTS*-zVhIMsKib;81`;sL68`~T*A2~Hm`U8i&g3e=&bA^mS|JsKTR6E z7nY_iAzd!b8sDicsDtIm5PzPbdC#EeqDlev_cc^eDiQTG5u}V4?PpYbEte!o5$h1b z|1BUlmPriMx^9o|o~-NFUyI77q7EH2`#28ew*5cU*<;CK4&#wOUaUPMDIFkfn?agz zAUo<@)5f-6mrr<(qk@mb7EB`c?*U@vrCHq4Sh~oa0Smm}VI~08WrzYba$5pS&NM%7 zgZ^X!TYq-AtN^*~p)f;wf~kN7>y`3};M?a&0UC)V`E>8|RqY3UU6z&>KB>|)oT(cG zTv{eEMZlBbYGX?gz^iuypWIPQB`8;xlll&4EF)K1S@h@C+{$D*4?zx zHs@j0+J|Qzb9dhI$#K+@TOF8eM$a>fU0A2OKAe>+MPi|)rGjOkvMr1An zH)i2M6H1|YGk`|K8Z87-QGW?U2D}SpsiHMJgs&j(lkK7WM5US0>qYL-u3)j|2WgUL z0eoK;1vLdv{zq$jMJ%R*@B6~`s~YG4}~ zB4d2$sJ?l9v0`>{G>aDgrm?Rzgb^ULnRn4a*IH+RoGQTqH1 zj3maGcb4B5^N@h^6?GY};x9h9AmO_jZ4bx=T@wQPHyw&wfaxt!chUG~CW>w(7f0~l zpEz6Xy8yb*6lGq3C<~elwZS{bV(w%ILm-K0GbubF{Ysa1ZfZQ8*=sn(I#Ui`RDnTB z#nyw8|D_eLd=-G+OYi*@@P9wwwjNYHB0JY|$NLX-KmUjD#Vz2gl7;>G zf4C>)cE4yZOGAY5MGT%u1DSgl%$OC-+Wx67iX_JhB|#@DycTjT6-7)g`=dbeKSx~L zc#-YqYx8GwiFK0F^J3evw9r339)R9Ee#q|opS|(U*FzsQUEaI!^Phj&_crSNVzlRg z5GytY#56#JFA!bwceRYq1z1aE9C4?{P3`);>EkZn?XhQ4H3^iYZi!CM@iwCJ)7B{R z%7^M*Sx3KYs{!=^u^tyRV7_ged`!F}GZ138R$%YMh%UkKhWxrc3D+2`9RV0gZ3NkJ z8PMh5U-lp+UTr-+@#ogrDw+(`rh^Vbh)xuxehZ+sebt)*N}WI|9V~qY=nKWen{eD1 z5HI~;bqf94A&hn!PU2ZWhyjhq#kNi&ZK)odEEaW*9+;(MBeQjkAc+%fZn8qOuuAf~ zE_3gnmLTLXb3eTIn~TxgOcAsh1l^<0T_x{v%Q(>JS-eqQvPODmy)AbM64%mqtG+P~ z?iT%&X~~(Q^}Zs#1Q>mszS&)}`>?Nm=SKzeV;SE#a1_J`f7~U0zq0YYNP~OfGL`Gr zEotoV6Aiew!~eDcg(tA!$&s{odih@sE{~r+Z~2?$#U1Z2;w0`tAb$LN8Bo~$Rzz6- z?r!wY_A{r-6yF&$<@r!y!0Y9i%_8l|tEc}J5+;R;<>61q(UMirhmQT_JP|<_n)Mdk z^Sjh?OhgFE-xooaB3B3IV?P@BZ1faeOcMQbk`j=L)-YM<-CaK&RXpRE(IoW^xJzAXTf1 zuxc-$1S6?{E8s^s&TkqS(_SS=V2dRq!-rSlqtYvN%j#Ab_Q?s?+IOC+u?$Xz@V0qv zT>833Vt7ZM&2%gKu=y_AUAFg51)fgLD2J6Evd*Yqn4I%3tF!aFwds=ix(e;{n{Ssp zf3I~obaC6me~IT_T0A}<546=B^K=xR1BQ#jDREMwX;L2j+J=9cja>JQ0D>=fhLZ<4 zy8t>&ftj?+$-VSpt|vF8Pl(&y(wUFnaCG0c!sdXGnzuP}4f7P!xYi_N!TMJ{*K`yWsJM1<35a)YaR@K^?Ol(VhKCHCY{J4DV}gzW1dz$*F~RRQpKq(XD6u zW{V{o`b;iExIh05vF|Ro@)*+k`Xt0YqwL>F5z;Q%d2vI;9pnlN&4ll2jb3B@*YCe= zYCm=MvO!#N%8AgQo}|uj71Jd0u`$MkJ5y7M{B>2s1B!U4?p4*k)p@bWmoX;)oE_F3 z`}fY3yEV=bU`yHUoWBuIuMagpca&#xrE=Ygy)P?Q|It#*zC^2!`bm#z@;q?od6d)3 z(B{wu!ad@xts*`WH~&2DQ|b1r?JpPR zh(R)Awm-f6{;FQhP~aeHB>J^WUE?&n+ecrFOQHVMA?Sk&Vl-0Use1jYj7sWk>NnafwMlyAh8D7}s{Xd>s2 zY=T&Gfc2LXE{&%`MX?~uWzF$Vd(ta0)t6WR!113c7zyx8N4m<&Mk~ZwJk2Rcp?|icq2Hl~vjh8}Q0! zu1tT=%i)F!ICm4E)U}#qWjRry9Z4+5^M!DO>JPE>!eF_P`)e$=a%3ubO0>OOjhG+2 zhg)=Uo~vHB4fzm``H?z#YQ@1yZx&EmhY1}s(s={`7~{6WNRfcrh-DXZD!B<^?xvwl zSxA6NqKu}5Y+m~K;I35da^d05G;iLJGrzp`*d-8{8JBbeR4pF!<*%535kOp{vneqK z#}r5y0I|YdEv$MX9jO77hcK7s5gfV5kskv8UYM5}X*!`S7nafO&WMhhFm)M&-BFf0i*Nuv<}s@C z=etcuS8OuW4zUSD?Dl}Pv}e}<*Q^v#5HpR8V{eRKnG*N*3iY3aly4~ZA`{%yT1N|q zjJ7lG&oX*v?9#lhA27`hy7!Xl7@Z;b6?raZxDy^*M?)}`LmbGX)KINagxc!KQR>Rh zdQNh8A}qKX1R`g?d1UtuR{gkE;;{@EKN9*=KL2Bqw4@7cUmA~Nd^y87T#heT;iEK; zY}J}!5h8xvmX{E9=x`AX$XB$lW~y}cVM^$}5&^`}kc7R%sN7>tzJiduhZj}9U%aF> zrAduG5wfmk$k|69+^;m`Up$A^8zetJT6#FX=Bp{3rcO7mQG7%n8cgxsfzeYczuWuQ+YzQI_l;yxgo#nm?!BM+VeyHFkj zilH^{W+qym2W1nFBXWqPcKk?!fo8qEy1->$Zx`;5bv_iivK@IA6h;gW{hlO0$Y%I$ zUv<3R;5iU>X)XLw0)9;Ic!dA?D;8acmkof;)~B7LlvOb>K<~bu%Lh{{L= z{ezVFSO6d(e@f{~I_W3N%=O+8k!WunY@GZGD*gKd{*3*bR=7WTl{BCu7!>LTY%uCJ z0-0`lf9l_O6K|s!qPXdn@5zIJKCUa}qp&vOq9|N?I8rwDL=?+{SrX4Z13#=@YrhGvd~c)f~uuL zrxupi1Q)I7{|pe2why9;1}pXlpo{{{=QLF= zN>@1tD+QermAGu!u{)|jjXNbv;Brkhy?ka%x|rpo;U9i&px*RbJZ?M*X4k zj5)(zt?-W$5P@Ep<7aT^9f&|54mnwmUg9uilAp9^z+^p!JLoxXXf}Qm#hVT+5MwJ? zHX8nLK!poHM>!w#Dk|yt>oyH`jo*_i%NM5KsUR z@?ib3Fwe!~MxfF&K#Vn78Ou?IMu$~qQ8_3n$CEBZ7Y$hpgYTY%searI9FQ}PYZftt zF2fvuQ0OTeL{`JB+(j+PAUWje`&~mYIdC@zU)>3G{-u8^quAg$U|un_hO`Oday*3z z{_QM#$`HnvZITB#xNSuLc>4QEWz`RkV-YKNzafLKgiSPu(j|6Xz&#M~oD)I<$V)2J z?qa(ysd3ptVy8r-H}B}?U>%E@1^yjnx4DHZ`yY?}3gMB(Vsrr6wAEZX!oda1daA)o z(xLVYmfO4-$bN1wmbmw_v3wT!o1R%wymk@b04Q&RYsvDp%tbI5e#p)k2@F%dD#$dy z6`?)V|L?rPTOyb>gBDPzgrWTzBmijhN0VS42@Un2klCdUq_Tn&4s(8_xQ|PmT3Wml zbo+~k+#!~8A>5mkg8N^9UwR3~%_Ik0di1iqazP3|c3^yyrowKHy51Xsb0q(2CH}a@Luuv~7`~ zhgIzdhmM^oz0iKpI1l)C(_j0|sZarj^HP3Yy7~uS?aX!7G?oRf-A`Z4d=@>SYJ6nzVtO_(5+IUmBhc|9xOR>6A zBC&l@v4&+$9o$BXb95;-kSDDw0?a>?ZzWDu$*d0?9}CO|oOl~N0Hs5(Br{w5(5B6} z^S=HfB6}Ch**DSmGn-s-tfA(Zaj!DHGU-c!xJVY*;rxg~9XxOe9@7(qUKTsc0UQtJ ztA>1;^MOAgj3RW6nvE@z*ieOE%XI8J0nFrGEPvgiYz4@eDk-cJ>Q;kjrPz~lERb{p zy%5H&U>$)*&SHv@KQ^rjka+W;YwfTI@K^FY%(fRw?}sym4WZn@xW&gIHR8>FEYEpO zGM|3@tS}#9ITjA>yJvDgAOjn7&ta^s=^jn1E0y1KFf$4BIPq{>*z}^kDk|- zIsL!kX%E7-kau_gex2PBaupJmkF0QPA>q$2WDVilUl!`flo%VqTsj?0!=eL9fPUY) z|5>lntSH7*0Sk%izc8~M*Uoz;sBX={r!0xaUWo6VGVfuKlK`&OG2Gh_ph zusqu;8%DggLel;8FAtg0IsoR%TzqenZG8jg83Rl4m=)0^#-G#oR{CrE))^>ZdcDHA z6GuLd&xBS>=mik-vyjQHvXKpiY^(gxrl+S^fKeeYsy}_4lj$8`db3G0=U<&2Mi}Qp z&W~m`d=%FrT-`PFw^49NUx2e*h%1EIQ6IWKha8GvW-XS? zGMHyx8#I2(6<>>2w7^rhq{=r!+j#d4|QG(t994tc{umlMjYC7f#vRZ_l2Ym1(52b)xhR)#1` zVi%(!m(QxFO&A**;6SW#;REXm2D2+Uo@y~}LHs*NapVmhI73>sO=7S7@YC-F@#~}W zYb4Ch7;7PnQ&@!a-3h-O;IMe|zk-0$*7!3i{@8@WN6KJQ)u9jBx9?#`{=Vh923EZ0 zm6PA9X|E^eitKI8t6^f%yFghcMXF7q16%x9ao9x@88BKCW3C^7Y7=`g?_2){M;R`9 z(xdDt@Q*2dV^>hjsRu7~>b5zgcBmYQ1_55uWIhJLUh1uEy^l}y#!Is#`Fqz2i(3D{2*|c zDBbL7z4BC3Ivk3Yu^6nE2^<2~6$i@u-Wxbt*kD|s?z!y4h&m!hFj_RuaCTHd#D?k+{nns@eRs$nhv2cJM-zvXMjk_0(0 z7yw`o8yTUWnL{K5&d%wGD+>kKeV`Y1K-FXb4ruRMDTFH!)o3-CPTQ3kG#CjS7YlBK zE4a~J9oH3TKuH0>10anU%$P@TLqdLoj;%!tV8bei02@?L3si#<>(5^F8}NldDSU%0 zWo=B-fxrC#=9;c*2Y|M#H^;7=wc}MqBl)fZrtrON4^;(1tl|0`8<&p6$WiMYd~t zLp`wQI&cCju!0XzZU=aF&QOIG%m5tt<{FH94?dgLqg_7H>^THdO$6Vg9tQ+2#kX) zkTJ7a@%16X30O!dFoWa@g@>Rl0IZZJ%!4R=gDkATsbK;?#KH@Vp55yFXJUdXSXDo4 z>qqcH2z>(I1vCy2m}BNfDd%50Q7UkMqoEtCd6Fv`0f)u$DW=xqdTYh{w6KBqtH9g+cX%YtjUP4W%BDDzso?RXQU=wJ< z%`s&FYP~Q6Qp}57n^<)G=tGRMIh2G6JP4pv!)d;7g<63O01JUFGQst6*9U+md-vuP zDW(Nd0CVURqgoa~E^7dAr064X4-OWgz5!_SLZC5RtU^SpHaDo#NGu@wau`Zmur@f_ z1k(l}4@om(aKOUGY*ULC9^U-YqJt#Qj{vTupqMmhB*S-QIgRTT|-e51NBHZG?%#1XjRdh8xCAaOJ*|QZSdn<}87< zFe3$3SOI{I76iiO$P{w9PmDIsB*~Z8_V7VW08+>+7G^91z>H%MF<~|{01!olG)4db z6%T%kN+2SDGs%}XW&q#~V*t>ljYUu55|h^$OTm?HZt20HUveQu0|2-=W;FnU*#<7m zmJukF4p`}CC;;5p&zJu=UJyed+Hk1lwMbT2p#ymQ41&jb_K@2fS zP(`5r1TrlzfnHb=8(DgIArED?aV5V^tkFf6O;*4~wrbaqix%!8k&G5oOkm?KQvryh z4Z~!40f$hQ`GqPH`WPxhF6R=+xwtmLr3C=Br6kl*^V24l6@&|Eq$x#~NjRTIJ{hHw zLq?h9l2kq!2^@-L>VyNWuy;Sm@aWh5!hmSa}2j0RSz;{GbIt0q7tE z9KzUbp%S!EVJ`nBa`0e-4OGx#1QARKfdw;6;9F^jS_s36%!y_~gv3u^B8W0_00DHB zh;}G*o;$$73aXFpp&$f_UM14|15HcayX*8UUnw5Z`BqxXv7Xrju8{c9@13;_S2Ehbaevq$!0d z02cB9YoYZo-F6Ns)!VvNH-Rt#0E2qepa6hCtT{vr2t-K`=;k!zIizVXSje;hP(k3G z@5cJ~)hX#NRc%TJLPyrtSu*VJt(2ynxVf;uq!2sT_hYne#{Dxq< zSHdKfC7I<2Ync)^-ZCY*JV_==k_2J`01B!E0Qgk-zFv|LX(;(x7{XQ(t^q)7W5dZR zGZzLkm|<+c1AsSVIfEgT!3<-#rWv}a1$Sxz2vA@o6yB$WGH8LG?))Y-@g$II9u0lY zv?u>Qhp;*8tq%asbY@91hbM5NkDU$0Qx+5|kX8cIf=uY8pCT#{%s~O9_XH^v>`A-O z1uuAIP$^1P8iab@vz|DKrryqnJ}bzth)zqM8-IyGiw+@kv|I@jPQVIw7^6TASwj>^ z3dWJ0QwyzxLOdNhh7s0np-vUZJxh{ImGqJ%Kme;iyb6SpG)|z354L`qkzt=(uOiNzu?|Q*HK;XgOde zNun7xBt$bMOB);6%+_6-wvHrj%)xwfP&8u3EPUwI>E=wj)66X`eGIkfPOUdz6&kL- zrVB#ZzV==Re=Q4rH>-ypD53pt1&N_Se~n4phqI5;mv8_L!R z?^k&MK%YD?Vz5vxF?yr_)0P6=g6bUJ40YwX1 z-&AL?)W=r#vX?zSqXl?;Y;I!^W{pO}&EN%PNWq7veQcSa&jl$^@rtc5zVIzmt<433 zYlk4#iXzv_xMac%;K=9fszl1b({gm#8s{$Oo02t-cbCOTXFdzO-+w0fNuZnD;1Zn8 z2OspC9qwQ;3xryD*VUD zAUL)pTt%ZkXWQ5@m;niG*ajUiQ%7yc!UKMaO*m3=oRchVpGf<;4;@0xEUaM+I2gkk zvLIV>!xsRp_#jNoA-fbHLK|}+?M@ju0>Cl>utxiUFK7{hHc)e)nEP7SBN&7?03Z!P zh@w20VJKaHSfx1_LRo&X0jy`Cb!%X>NA;!*VHoP3p(`_tYD3>sHNzpYV_cB$pKKv# zmF8~(Z3hBTA`Z+T2UK8y+$IHl#tLKrabyg8MnD5bV17QJ15&^Z;2{na;D9_J6Z*h^ zn56_(LG7f&40^x?<01?m;2C0p`dlXjTmZjJKm$4eRH6xKzM&e>AuY}-mWD(iLa^*+ zr;|!x97w?fMBoH+#`sj=0V1RTl5lK-FmrmK14<@ogSVu5iEwVnG~2 zAfnJ`%x0o~no;>|2i2U$$J#Of9!(%-OdfTjkPPV_r^MRIuaWExf%dWe`cWX~Q62RW z{iK5+185-)Qb8ILCGrs==_dmpav~!VApJ2V`tg+@@*k6|AW4EF8S)}OQpoO+YJ%Vd z_Fx&Dfk(b%%5>o~N+J|Q019LQ8k8Xt0OUqW2>}0m;TkYu)Ewd%%m53iVg(SxG)&@w z6iFbaVJcJw0LWoPOaLh&gar0LAf5r6OrjeEVC?*nfdt}4ip3!);d4%>1i*n56rdJV zAr5wd30mM5_8=JS_mY# ze4z{|VHa2d8dO0OO)Up%K^j&e81_I5guxnC0R$pJAhsbENDuA=q83zOP*i~idL$3B z4PnTs1dx$-W*`N=6Wiv%7^H#sdMFcI;ROHOV2}Wy8@>cUz62M-few&>7d~Tr-m5?4 zf(<(0FwOD3MkXX#ri}>DAPY(2M8*L{#v@Szfl9JK9n>Ll0wVn{mIAaM_v~aAG(rzD zATO>%vyA>M@>)Pp0TcX5Qs$r= z0H6=};TA|?9tcYo002}3Vje=l1Ns0SYT@(p;2VyC65PNILV*zl0u#Ui6Yg>*aDfGQ zL>4e1D8c~~SfLg%1^_yNMPj25BB3E_At>a54yH^XnB`#>(E^Nt9RNTBM8KqEhv@)d z9JBxv+JSQbAQVhv6_#Ng0H7F(fe!zea}iPFDYxMUiGXgxAu%^6P7q-l1Y!ejK^ihZ z1en1XXmB2|W*Oi`6~bT+!ofa9z!~fz6J8}TDN!KaffPW%97N$E!eIcE0Uta;AYkDe zEFcrwfhjBk09K(ljZXxSl_G#a3!b4a#{u2qp%XA+!|VnT-eC?j;59@b6Xv0VkPjVd zfwZ_&dENjK_Mi{!pnmYO6gUY0Ho+T^&mq{L8oJ>Fx}+RB!5Q{pX(R;zXyF5Z?!VyR zF$4noQ~?|o_CFV{X`oC+1&u&o>0*cK{{Rx?2r!i@(w91^m{fMn z3<>^(EM(KKMSp^2Cl23EwEzFIYhxD>W`E3QLlk5SE@*QiL4Vd}kM_BSmS?MO16n~G zHXs9#0Wltc1jJz#)ZrIYft(@%9aKOB%7GHJ5*QfbAe3_%Zh>l{pg;sHQD01vRC9K-=09>W)e;V=C(5C#Csp$)!a4Ibbde$WDfK^XjS8BC)S#6biO zN(-2x9nj(vQzQXeVH^Jn02yB81Wup~(%}tg;TRIY6vP1)qt*C402)YvJ1{{<>ZKe2 z-~mLT8!`X~Ml7W^pa{z#9NwS>G~h=?pbUDa6EuK!if57r^AzVQn#6cYZzy%rt9Q@O!NG@kxHp!TFWlc7i{!iMbjQ&Pe%Vy$+ zla^?8SUQThBJ&N2ah8WcHmzvZh?Uj=uUICu7-fgJXoHMoVFHW` zFlCpvih+2Gi53T9!-qbA7OX)821rs|kQ?fSDri9~!(n!`)mCr;9eBYE)Bz7HU{W;T z1gwD;HlPhkAVU8{U=7;F9N-`YJb)V_0T!%b8iL_f1i~9O0UQLP>>eNv1o;<8U=7j% z5gsMT03aRWql)aA*d$1RC6EY6o3=PAqO@fMU3HW zS)e`J0T=2;D*?a)Q1^JUusu9L6}|xk0PB4|j0Mbr6BZU8_5hKiK?6Vl8Ga!F06-lE z#Tu01HOznnkii%}U?FZnQ4`2}JpdvoLLmp$-2=pk8{%IjVUcaG@4rflQZ4!fdS#+Q1Fk;Cs7yVOK2W3JIiUE6t4hr&tDK zCA5L2y1TRwd@ODrnTV^a`m?}VC$d@{$qEXnIt;w}sy9?55ooN5%&L(Ks})qO+1jn` z+QJl6uGhM&!KbWU4kyrBCT47`+gh-{ny`u3uQf}r7yGSum>>T-vISzWWx@yGI7|Rw z9`>L*Q6U)GAwR;`Appi$^&%ZeLKWhH4bE90Y6Spd;Q{nVHXQ;Elz>V+G`fB+zvvHsSBErGxr_aN1#UejA5Kp&m})NXJqyN@6dTWEGr&Q&yK1w4r*0 zz)Jr{0Fdzk0P6G&2I&~mK?5Rz9p-=>Pytfjzz*tx_d3B2RK*zxr>VU`1Ja-g$Xf~A z0|35{7Q_JuLVy>X;Q>IP7A%()@?is31s9-?4IV%kf&qFa1px@Pd&xV3BU2!L0Rgz7 z76t$sv~?Mb;Q_dy2~J=~0pJNX$WX`|#~DW;&eoLMff5n`58^-surx2yKneIl3z{Ge zv=tEq#{)(=z*T^pMBoX?doRda2WDUf7G)khVFbY8@*oHVjA0(~KyJcN7NQ{oQs6EX zKo$1E4E`5V=3rL@0?TOt&uJin6XFBdz$@C1FPcCLl)xnj9b{|=$E`#`0)5a6y(j-x z24xa`K^Wc8AN|o09mgjf$qC&nMB>un+9W95(GR_4Bz@609YZXg)LF*Vr9;$h!q83T zWLo_vM7`5v9nxn#(@%Xb8vWKkoz-{U)lvP|S3M@gq%tf(1zy0S$6*=hMjo_59%R8R z!GRnE;v0lU9Cn+OS3wNOArBClU6s@!UL^#cuLXdC9mHW1HsBY;Aw_%<0KOqh?4uq4 zz!T6RMV!tdJU|)D0UVG)1$ZR8O9G74L0fG))c0agQrj>9fazL53vm1d$O!;$!5m)T z7RX5+LZM#lgcX=#9J~P>JV66c;T-_^1Q1~y%;6YjU>DS(9--WgpZAsy_)a*+WT zG@uNGAja9@7(74)OraZY9LIH_3_L*Wp1~Vh!2(R79lF6ADB)YM;ZfZ{2a1dgUYRZz!_iAO8`v;!cicocT5`q5}+31;U4hxNt~g0cl1Z)-Oe4K zywBtwGzB_R4%I=%)AQq%gnifr9oJj`mSA5e6uoX{-_UEHycY!b8=ducAJk9c_jTVR zcpvwFqWB-3_FupE6=eB?|M;Ch`EMVBjNkPG$o6w0`bDDowg360qx=88fBJPI9#lmz z<{%Gx%}#=%2io9);GkmOKxV-N5iHp#!=MMO)YJYCAlQtt0w4?vI{;n;84;(CmMm|~ zz_H>c1%Qid;9LX%#{?1|E$GZS5d;98i#f^wV8UeMMkYBdwrSDQrA+`qTmk5IkwqkD zW&kAd;j&E`CNJicQE{1qRfmodxFf^0{|g_96{1*xwuJV zi!}i9>_Jo}B_b^^C~=EpMHsgZd-4d*;igRvCU4ND6R@8SNLH!+=;Dk3V=$eBBLP+6;8(P@lfCVbJ zABP>%7GM|{hDhRyBC-_VhY$KEA&UrFh$4s@wpbI5GMYHwjWI?zB9B74_+yGWzDFBD zq5vQZB^O<1lMCF1(E|WDjFCi^S>TYw8d+y0uBK5kiuI?Br(QCS_HAyMM(c$x1&u#oX`SsFa0-Zp#ZS( znoCKfmDy;Am4K5%L?MBMmzvGcXk8`Dz)~46)Hy>7Oy#Lq5KKs^0TN}p5U2_?X)!`_ zbC7ckEgM)u-afLyD}Vkph|=GJ7+s+~12OGuR_bG9!3m%_rwfB+VuFyk5*c zx3_c9FBi=s&@SJ+snRzc-E@&pL+z5(HdAf2(MSIx?KFEr0MG$W$s`sTK$?!zW? zj48Y1r=#wA6ED1BiYuI4N2VF4mqEGey$iAW?YmR&dU61y@xtUy)Ly+1Gc*y3PEY8t zx!^ACzIpiI!w>%Cy-z=W*pP?qQtG8oC2oMB< z0~5v~`6+>c473U4Ah#h1PLM|y#30=?7`gurN)RU$Tu}%`*uaZykV76sAqWMhg$uHf zgdF7010(oB8m&--IE3K|F;zht+7O5?d7%k=SVX}c5raWoq6~d#L>9I%aygt(42h`1 zD&8=OC8W?6Q%Is8LJ^Ev)MBT?z(xpxhmCNIqZ~m79#y&KJ0Me4!S=XEUO@p05o-?| zBlbsp;j2A+92$K7IEJ8@L5{CUPkn|kgvFqXk|z5|5-2I2PI_`NRxrVsUc;S9R??D4 z*?HCj9^LMboqi(cHt$2y&B1-X!KURW#1_loNvI@AzxaMFu)R-6gAKyW61r){_95}xj~jp?ytT@_=|3%Bm;D8db8=l09SXB!D`D#-@2G3!F12J%IOECwcbc|ufH@)2t)6LF}!}AmM%;ydHxpaUQw4NDQXbK&gafxov zqeU3!+0J>;e~vVbD(z4hV;a$RR`jCbTuc%@8qi7*^{5*?p;K$R#-C;(t37Q+RI7T? zvqq4sLoI1c7uUG>@h#M-D$zJ*fl>);r_zXxV4ILPVRE*KPLmMbSoVk6Aa-_lhr`7D zOuO0}POQta&BlU+mqXt!rI4ruany9miG|G zP46VwdqwFULcae8j?jW1oJ8<08oxt>?te#D;n!w3yFc{sicb^Z@y?IFcgk;nfBfPL zC-}Y%-tT)`910j0xXMY+a+A{$;4v4u#+$wIXhUu0HW{Q%KKsW%9vM^(7n&(4$#kas zCoS+Ma(;lmo~1Y4>HUHF%b_53_fY*J<-SqVIi&TI|FP@dW_PZi9)zePXy(%{HQC#~ zb+m&w=`^QN*>7HLyH_3Wao0QD&Cd0=1ODG~-_YJc26Dfz$m(-o`ko)E>qGy2 zmutWJ|)`PTmgGRU^M94rmc!Zf-bYf3xM}#Oc`CIe zm3MmNH;F6}c$SEIn8*a0$V{`haWH~flqiXtNPwNFi37-qq?n17xQeLQik|q22q=o8 zw=%P6itQ&~tVmm>*odcCaH&X({TGSA=!>qXi?w)(vZ#v7*o!5SjK}zl{}hW#wS64I zU2!KMj;J+@SXGC|U7e_nqbPrrCyug6j<>iG!Jzj;VKd z0%DKr2s5;ykM0+bi1&`jn2!Yck3vC^?s$(B0v`f-dglm^(hV6bX+VsgUg$k{+2L5NVME*^v#YkQEY;Fe#A@=Mr#(H(SOZM#dvR zxRW)s5kHw9Mad#T8G&E8A@AdXPWWa*xs(*rlvDVWRuq+Gv^h=*0M%oNHpGSugq0jZ zl{{#bEF_jpsg+f!Yj{XEXgQT?S(a=$YHv9qWLbn}DMEFbAa}`xQpq1P29|EAlv$}c zTUmuHNSI?8mv}jsiK#Y2xtNBzmWZimdx;=@nQ3{6L6_+tnTcqdsgrEOlV4<%lQTI) z5|pDEnPL+^{SiK}nVM}0fvfqKe0iEpAcKYYhO;R;wOK~EshgDvmA^Tb!8!kh8c2g^ zNI+LvoJ@I~c{rTS37NKunb6sX(s`E7IhEJxmw~97)CruhDV#spn}!pf#yOtIxtlHl zo?^h7+^L??S)O#cop#xsTbP}CX`i<#pV&#CEXbeM>7U-oo@-{F0_vIgS%><01>y85 zT~#ytSTdG|bSZg}H4`xrGhSU2Swuylw#R)pd3W`pq4F^xCL?yY6?~x*q7sUt5-O7P zSTZKcp%+qsAK9Wf5ur81qezo$8LFW+s-lLcqY3k(J~}llN^L?4qc^I5NN1!;3Zp#J zlI%#MO&Xz3x}qYgqgUFa3t6Q=ilrOsq(tg^UOJ^yDx=y~qFK6-TU!65WQ3$)`lA?1 zrYEYUXL^5WTBjT;rW(1BHEO2Oho*BXp=)ZTK02s+TBv+_s9^e{Y#OCZTBePPqbv%k zC6lOQ>ZXa< z$8_8UrR-R%E3pz!gR4oSt4znMP4}x@N~>02tHgSBzIu4>v8zS5tl$W&T_vn7!4lGH ztks&V*E+4))vQ&=tw2%^ny>F_uwI9-Oz^J@%N_%(ukdQF^eF$a6l<^H>aGQgsocl0 z^{P|hN=YEQa}YbS&q}Nbdvz7N9vAztBI~TS>aQhBVKUpW1go+&d#)`DA02D542!bk z8nW@qAUFH67#pw~F|f(SBAY6rT1t$Hv`{&Kj<-mm@j!B zqgGp!V|$WY8)sdcwobaXHiZ&o`>1g{DQX+GSGyxw>$YXfwP>ribIZ4E>$i6+s=)`g z(o1>CDksG-YfBTMad#8;1f17K!kgK>$!nDrlAW}w%fU*__?&}jE)%Fl9zSL{HsF}XitG(yT zz3B@;-&;fN%Yg5Tz2@7KKH0tYyT15qpyj*2F+!1c?( z?CZeQ3&97xz5iRm_Zz+@sKLxD!D{9n%NxKAtb-yPzZ^`#9E8FbtisF7!t>j~FKodB zEW_PP!wGD{3;eKTG!s469YmC8%4974S$ZVX2e!PHD=*Wt^hm1Ugh)l*RQ^-af#+f|F zn;gkkbjXyPGn{#JVhiiOCjheaUQ!Nz3el) zJR!lHGs7H{rV5Efn~f*w%gYSR%`D8n%*)Yi%+pNF)hsi|jFUfi&B#oX*!;}YJgFkH z%_Z5*;vCM&EVbUu%}Qg=?NN<8#deeht{Z~18hcm0C$Iq9YzczTb63y(oNfQ?v+;^- z0u9gM%B%ssS@ujJ0IhiY{ImnDVg=365}p6h?JRTIn$OKN(a5UM42#j62h!rU(K<)c zs%O#-^DBjk^9U*5LeCM?Ir5U4K~( z)46rk@%+{`9oJl)cH}&IWi8k2;ni9V)=s;BbZyjYt!-^B*m?cbc751Wo!I%oZJ`+0 zjXl=v0oiOF*0=1~`n=b7Els#Q&)>+{#i-ej*V!UU+9bl)p_taeDAl7)*~~23ABozr zjoI$(%(i{b?`+y&&DyYN(dz8m)Cm9F-(0q-&D*PO+_f#)uPxlK&D^sM+t4iA!AINE zo!jl9W_I?155~nhmO6n*L-PYZ#N#twm}B7Wz~WswkU8T>)tX0 z-}Q~(`LW;a?RX@j-!wGe{o&vD4d7+$-KI0(@I&7WF5i&D-y$R65H8{EVc-#t;2T2W z=U;PKtz1^(b5{@x<)-yS~R2Oi=#rs5_3;Vq8hFK*&1&fYVg-zje6G0x%{ z?&3BEb>?%rdr<(oX`Xg=p_{^fc8 z=45WleqQINbLUV#=vZFpa(?EB9_U=o=#m5HkL(_S1L-tm=OZ}jdEV%JPRobB>51Oy zc>d{??&y84>1kf-WWMOAzUOhi>U2)(l78xL4(hWm>Z}gvijKULF6)=x9+_V2t$ypU zp6jU|>O@lPf6nQz4(zdR>195Iqkinae!O_pM(nuMde?0TO-4vHQMgAU&}M0c?XreM z?prhNYTa1wo>Yc4UGFaL^e*oxc1-jI@Ar<4^RDmpetQyv@7+Z26U6TTPw@H$@CR@2 z3GeUuzU~1}QitTS?QZ|@D1`75ukZ%O@EPy$8xQdX@A2s#?;^kO65sFwKO!9OVI&XI zCSUHdSn(=fTu-GTnD$ zAoLWCSxn#bQ&03BZS+$Q1xt_hT5l6qFZEvk^g0*yWl!~C?-FC5_G{1c2}1U~%Fk9m z_D(;maE~Bz5B6ri5p93>N}u#-|MgP;_JmLMWsmm^JNRAC_ln>6Ob_@)@9ln{_H$qL zKyUY1-}j3@A$wo=oDcbkZ}@`m`Ihhaf&cfL|M)x0_-Y^es-O6Q-}HgTox9@W0wZ>2UnxgLgB{0Y@^B?b$-KuQw z{`|=w?%GfN%zym){{8zd{rVyPo9+GBzx+y~{$$PmbshdRU)kd^?&d%7Jp%vsPyPVG zMBqRG0s$OEm{8%tAPs{ogox1KK!Xt(Hl&!5Nx_X4Av%2MQQ<_2A{}ZhSy5y{kSi0C zB!bYv0>1i9WDAq2{dTSoJCDGv=UWnLMTvp$^;oQ=*p`i zOS;Ueb*sm&WMi%jJ9c44r8d8!&8l`}N~12{K4mIbCtadv@#56Gmta|ocd_Qx%a(9N z!$;!=<~aYjE?2*Y0S8t**=uFTh~;MPyI5gb$e^2Y?)exq-_xRRdRA-NE$WY|6SJP} z8X@V`k{3#ydN2#^&SD`;)LfW&>Y>NywoY!@Id00jM_=x}(YoO1#i#qu-JQAd&*1GY zuid<~`Rd}+laI}Q^Y-1?Z`TaJ6?T05=jjWDZ!GHOb8n&j1eDLL{{T2}s{s`(@H+Lf zYpXr|>?^N7gn(O%2`w(1&^eB@2(ZHsm%{Kkfk>R~#GD!oN(;$YJkLd&V9ZHIl4zvN zMzB&GOGoWIEO9O$;R;bMAxA9oB@aPt(xxR>jB>9d|8j0hB3pzq%NeU2vCHGMsvN8Y|eG(rXo z&2Gp>30)My1kFkmN!u*dkg`nw!jV8pPy5ueP$3-kP)7?j6e8KqE48>&mAh2ASjU@{ zu?I0-71H}U-R#!)a&>iATYIh4L1T3l)<9n~1eRAyH&r&lB(6=uiZHg#0?UXqJE61zF#GJ7*FI?Nw$UCNA-T<_TkX2%e!K3t_r|+#y~z&TWw-&j zyX?H}23&E$87JIv!~I6QaknQQobAF_K0NWuIj7w7#s7}{b9HfEXow-0GK?mp?zJ~r z2zSGm)zDtA{aOETuh(|yXzyKj3mM8?c3OFt*Z2I058ilPRf0V*<#z`@dEp7H{@v+^ zXRvr`XJ_7e=Zl5jdt%>;9st#^{WG|TRA;?1*9G>St>+!x*H`<=$A8@V8$REG!z*htfVV13CmV~5R@tu)fxX&c}q@0QI{c6 zr3ImdN?`KRT#=OJCXX3QTb2@+2sva_5*da?5{o6-3!`4v_z`U0QJd)t-@R4|7wL5~ zmvY&rfy_BXbb_;c;qzrX%L&hH%2S)`eC1fuIU0PJ_g~McH1pDl)<5m9T&9>tVfW*pC$!uzp?aV--7A#&XQDiES)mV|LliYIbORRZ9)` zsiM~T428X%XlZ+PTGOhQXRKYVYiY(>)Z&Y_uPtqD>vh}N<`%c|YZk|N8#CH!<_cH% z+u`Iwxu9&0aQI?wPMoVb=*nffl6zNlZE{`Hu}ixy;cj)+65e`!cO~SNmw83PUF@n$ zy&hq2chB`+i^Mm*)@`rm@;hJrc9(B4voFm84B-6!cSAFrV~H@L+kS~8 z2v4|nqhYYcBz&|DLm0zBGfszp7UB$3_`@D%Nr@pQVxs@CcxWzeEF;^r5qB0WpeM-gwuqtIh3JSVnq7>Bh@gz%xWeIm zafcu0Ar-Yr7P_Sn8Cv05c6E-g|U6`Z^L*v{$s zxpp3upqp#xIgvU0{v@KK+a&2Rp;gkwzOXJtG6%f zUMD-(!>;tGABUtl57bR$KH9CWaPBp>h|hm?cd>qx?`*Z#-^rzSxCox5b`Lwxw zJe}=UP&_mdZ+43UC-N$t{GTieb;#qL@mT+HJm)X3zRUw>^OpB~>7{adsf_;fjtM>L z-LCqmus$lI$8XKFd|cl19@?lw^X_}^dnEuLUlkiZ@r_?E39Fy>0f@h%zs?*^Pc<#oB#9QZ-4cd zzy0CgzvH^UEc?Ini#GUMzu-Fv=hHv*7!mxw8CFH`_0~js5!Y_WLV89@{oLi`v* zcxooMYjt^h!{q!D#qW~MO;iq zl3PY#43}dJj9hfOXC%gGv_@paMm224)ak|p<3?KCM$MbKZ!{~l3rChRM*xsUYfHj~ zJ2QQQ2m@<4jN3S9dp3ZZN9X@yxWH(ej;qIsTL^mWN4@yRaXUDJQ#OPk$ageIvjNC> zTgcK;h=S~|CWuISn@IgK9g7?_ z$g)An8YD@Uq{*>3NPI*Hn2ej1Y%h=GNs#o(gv801R-OTSDPwG>Qv0wK55D8PKg!PFx^G0euyOF)54LZVBPv&*qS zOubahyI{W280_OA^=Y&od37hE@8zq5G zy$PG;biU@SneCKL_oGfX2~Wlu&+9DDx{1E$Y|qN^&SjI5`mCJy#Lk*Q2>Oi9zqwE3 zObGOJPaz=BQ&;M-D?tD=EbcqJtPYUhNuffpz)KKGu z&>+!I>kLr_B~c+5Q1Fz{4^7bFThI}e&=MuS^+eGaRnhqL&+}~27mZLG=}`;)(XYu+ z7u`?-MFwzg(;)&a<-OyioVNL#ZA}-4@z&!4*Mtz)gxJ<~Z7zCLzjM8UbQMbM zYFBt&FL?ho*D)PG`Eu9dBtDLmFZj~eZsk||^H&4A*XhdF>DpHYWSf25oQ2&lf^}Gf zh1i6hSoagyt@zjf#8~Hx*nw17j{R8hDp+|P*{m#C0PNUzy;zmiSbH^De6`s8dRUR2 z*>SB|hP~PD%Gu;wS#@pMfIZpsO4)-Iz@25+o~_vWN?M4Gh>xY%frZ+bRa)el+M;#Y zbWz!@P{cmV+OY`RKpWewZLhLbN;f-Oi8URy4a$gy=*Y_1;yLA=~)f z=PlOqMPBh$-rJ4d=4D^>1y=BdU!JgC*`;6Z%}h?!-uKmC$sFHYRp0Bi-|Vg40S@2E zxL@N%;QEE#_Ql@@wxS1?-{8&PG@@YYP2cu0-^y6v@0DN-?p_FHU=Chi1vcOf{*w;A ziVr4V6DHsc=HCN`;RKdpU+v!uHlPu%;N|sS5#C=Mrr{MnVTG8cu%t$)dQJ6F9i{)u z2#`ABx=iA?T4FYJVv2y`L_*@L(g-TPRF!ICk9uN}iek;oIS?um;RNF$xZ;7x;-=YR zb~31+s^TyflrcVtGHy9Jey8^tO|7$HF~(vt)?z8<;=4%WKlZvnzT-j8<3cv$L-vbA zu46V9WH%;cI5uQHrsOYXVoPphOnzidj$}?=q)KjMfq3I>iQ|0g(Y~DcfwGi!XtG)8aqee# zZZv#Wn}nWciRNgIu4sq8XoAaVw%KTZW@n3zXOZ4#l3r(%erc4B>6ON2ny%-D7BhTa z>49wNa)xOl2Qerv zJ}!^1#^JCw=76a&fiP>SW=E|~YZM{t_-N~_hU;D)>$RRMD1d6PzUwK5M7^%-sZK|= zUXQ_k>vlvJ!RBkMCTwy9YJ%^ls2>cZf^GmZs@-6LId#ImhAy|ZRuui1b1!) zH<#gNZ#_F*k=V|g=I-{^mkTfM6`b&l;qdPE@Ct`-y$Eri%<#Sq?gH;neg@b8cZm`Q zQw;xbjj``Jaq$t?@2!yWA*FE=zi~dv@f1J8AUE+LuW_~L@g4uwf*;>;kTLQZ?{Ffw zasOs=Cx>x-IdLze@`>5<7kBW@?qTPs>(rd#Wo6cLDf3G`^TBGDHCOFAtt!$x4miJS zIj_5Jv~wwRbBNd|Js)k&G;<+Fb83}5Ku2w{yYp;K^Ft@|KtDS^msLlvb4XWoNpEz_ zLv*YjbhoBdL)Y|2PxMX~wLVWBI){usPhnG6h(8x~PG@sMXLU8VbW+E2d{On#G^zGE zu4@4TAh1$VqU^9vB&Z&XUk~=%8FsBMcG^VtqvnofFS%wvc3_8gHj;K3qV}zLb`hb7 z09Zh$zie-xZ4ZiW{||_w0c9i(iS1|CNn5 zcw@vm0QmJx)DDP`_;^?Ml0U_hC-{`#%wGorlP?IA5BZg!`IfKwm%sUZXL)dO`LOo) zFi?4)&v~HVd7)qUqR053_x7Y$`krt4qksB=S9+M2cdDNbs26&v?|7%*daI9mi63{c zR}ro!cCyELniu=7S9_ypdz*jzsgHY}pZl-Z`m=BQv?qF%gV-eabrp{YEpQy}=Dn`@ z5QjeqY!Q5jAbhtm{NqCW%HgzYY5awFd@y&u$yaZ@yb8c<~#RJMV6ucyaX_*`MF&oo#yQ>#4VI{T_RK z`pw5*eHz^-R(`aN$DM!uA()+j8PUg_gSABDdc}yEqKQNh z0U7`#x>%wXF*d5<+$+ACnUv zHfn@In)(;27*e?^TdjWhDuI^Cx+bh*wK{07SM7>xt*4R{UaB{Y^>3`N~?y|jzsLN*fOZAwcVCVEx6lCdz5D*9jYjchboDukcBGcu8i=~DDP49 z0%EAimkGDDaL1Q2 z`mm!Bhum_!4fWPYckQsoD$Ci@(@~oZz|w~`&Gy)C z|9rO9I%BQ1)KkM@X`_Sb5MO*i3h$DMMLYpcX~-*^YE z_uyoA{&?Pr?=5=amuD)u;u3M5I^dYcIk`(d_pS9`0SCOUQnvS9BH6j?&ihfm-yS?$ zx;H^QPsU4)d-B6aiG1xR{*EI5^;ap+d``|I4ZT>~Pv5)s*MqOU^jT)FlJ`HKuax-e zlYc$-mcJDRW;m=+op3iP z=Ua40xriw?I1#2We#hD#Jn5)*R7o;2}>PrOPL5i-S%6p@Bd6rvJ|SVbdV zQ7KtG$QCuy#UaWNj8ZJ)QpV^-EV@yPaIDB0!`Q};%u$VUWMdHd2uLPYVYY9yN9d}blV*-LUB^O^}+X1SUKE&X#?Md@sw}1HRJD3iu0k}a5^ZTsU+UDD zdUd8tO>0lB+SZ-IwW*4f%s~Wr6dftgM1K8|7W^t$@aZnFS{v+OCny%a>Ta-rMXX;b zQrO563bH;+Alm@YSHV8kvXTv$VaaCM%W`mYnr$p+e>B?9HX*cZ8m(fl!q|X0R<)j; z$Yc)-+Yc^Qv!|_Xj8=g+R9os zwo0k)a{p>u$ZEH^(k(4@eH&gAb(XxtZLN8`%Ujv1w+X<-?qJKSTlr2`BjB~~b%py} z;_er+-rZ~eeeWyah(PzX{6(*S6C7aYGMK>8eehrYt6&PB7QUA4<8*AJpjnYnF$RT6 zu`1%3FMJ`5Ar1lwNo*PuAEcfn%*l#dEL0VzCdM}Y2#pQ$<7*9w#V+2Dh;NMINa9t< zOBR=el3?T=Q$#c{Rx(+D++-^Z8Ol7KvW1e2nIQ+6%TLC!lyw~DE01}}W$rSX!Q3G+ zyQ;)nhOWjUj($ET_DjPHErJg0fjQSLLD6&+|VSGmzt4w0eT z9OySMx;BKqE2P~lY0RX!(w|0is5d=oA2a&Yj7~MFnGEPyQ~E_>xOHHuradh%cp~)* zto#Q5thLEnp*?SD$D7*xt2dtRok3x{ny_HH=4A--*iqdVaW1USRjjc<*co8$dXx5WdF@sTSp;>PZH z#Xr7qi3ps5?=AR54leIq&paYEcXrD+{_>N1yn`U8`N{`QZha4(;yk~&$d#V&T}E}J zI8_N0kRxNfu#pK`D0M_0B1x;SwIx{>kXE)FL$8A!0AkMq+1YY-L~~t_Y!~!I-X3@V zvCMtzg0cJ8@h%a)pXBNr>AQ{V{>GmKp3-3-!q^L6cB~)T?`2+l<6A;_zFZ~in#PRc z?a27HZ=S42@jTuCzWAnOUL~bByhc!OdA0NK?v8gmAPWYe3-TZg;$YiF1Oi^#G1;IGo){47 zU=j}D65d}9a>gJz3r#qOK!hQhz2CHap-+UNK#XBgK_Px@85p9W8af(iEYe84p%^k6 z83KzLMg$r{K^(GSMobA^M>s z0%AodVkA!DCMM!^d}1TkMjo<-Cz3=aQle#~VjVK#9hxE{w&EzNA}kuBEIy*^)#5Ai zVJTLGUh#yEWXzB_+=xho4v?HmNZoe<05MjHG5QSvBxBHhjLosdFt$wpm%s@!D&s~l zBZ58SHX=zkR^u{WV{eJ$YjI;lP@}e48YpJ5HlFY6Lh6qcdvb zI+9~M_G3o;Bhdup;Z)W>x+BgVBsyjo`_N-Sn&US{BsNZDLmDJ7Vq|aWBSG?`Mf#&h z%A*5Oq)6K1MdBlZfh3RA<3n~NO9G@zz6eYnj9Rh9ZS2NFTw;xNnt=?#B2|@LWXt3E zq?1))Cko|E5am_uS!O0$lBQh7Ul($TPvQ_p z{NjF`B5UH6Yf54))}k(c2WL+!gXMyrqdH!p7JRX_^I*$szTz(p!!y$3JIbn>V@hKqZWw}JZg~WsaQ;^qFO3N z1gfJlDVY}PLUt;mermv6YLRHFsY1?Qcxt7tYN$49rjjb2MkMD!y zYLfb@>i{dR5~{F*2C+t|vF0hVf)!OBht8VM28Y-&x>9uC-s7h;-QtPf-Yp-G}x@xPumg=%vhPi_3uVSi6*u*|HA&bP= z00QQu`Iy0i2Eq!6!qOVUo*#`otgA$<#iHQtRjkG46oX`J?&0DsPkgRQFs|q#uIEZ_>uzr9 zLhkCy?nbyS=fW=Oo~`DF?(V9t>H03{p6=@k@9&;&OMLBKWPw0bZOOjiUS3AaW}sXt z;q)Sg%1Q^+9@Ghj?DDzA)pqYdZG^^#@Ai%u^m=a%j^<=8EcdQ&`IZy-I-dC2to0HS z_JZC0vKsl~la0AAV5G0s$u9uIgaA`7$^I`rkwpS)FZ`aQ$j#1Q+lGOE3R+aQ=4H{-Uq{6Gn?4E~n-_glU?c7Kouh@Y?#>CUZz;MnFLg zr{N8so(`7>4_AZ_+prG{F$}w&4711)191}P@Df*F5mUqyzi<@i1Qze`3}f97V=)eE z@esR56;H$!+h!QM#1<2A7a!giqp=d3XKpkxXq<5nOR*fsa2ZR49cQr`kFkI3F(1D1 z9gFc42c;lOA|d;+*#Yt#CvqRBaU&lxTSD?8OET6uvL3f_>|L^1Ns;q9C>3*Z55plP zTd{nOvJ;zf9v@yRBQhx)F)N?mD_3$XqcRfHvM9syD(CY0)$&q8@+|-IF57Z1L$NNO zGG7X_FKaO{FY_&%@i04cF$Z)1H1qK^tMM}vCNm>5F)K4SU$ZYZvo=>VG#~RCQ!_YQ zXE&#FIj{5MwX-IV%JWvOa~v@J{zms^FI&xT4eImy@^enHuL9cOP$}&_Q!q5$*giLG zLKiIx3LrxJ1U^S0KufeeQ?x=;#KdB>7!maTMf3+cv_?a;K|>NrgEU5;wE3pAKbO=+ zkMu;Vv`mZi_s;Z7AGA&Xb4JtjP46^J_jFF9bWC%hx%{kAQdA0CbOu*6Q!n*GGq6)f zHA_GB2TwIe7j;liFjfy4RA;qRZ!}m}HC1!9SbH_p3baaf^^$$HS6}s6w{==0wN}!f zTeme_b1YC3byLT6pxyQVU*|PM19q&LwO*6;SR3|W6B%ht(rI*rLbJ6;Xs_e>7-Z|D zU;hMU|7BZXwvZr4N38E<7v*Pf_EC&>30JIX6H#ZAwrY2_Xt#E1AM9!$W@Kk}Q`EK# z$@T}AHf(eDZTog;1Gi_t_HdszZ=W`9gLZBscW@hbbF=ny)AnyWcX3nqal3NX;r7eQ zGCX%Tc!#%mk2iUjw|Sp8dZ)K~uQz+Qw|l=ge8;zZ&-VpgDSe|Uedo7+?>B$*ArxPcD}#F49eCpf$6w}Q|2gUfe>$M=N8cZI+Ag}Zl#xA%s#cZaX{hpTsp zr}v1XcZrL4_Nul2d8hc+wzrDQ_lw7OjKlYgzjuwh_l>)Fi?er*^Z1UhcaH=4kE?f( z6Zw#L_fA#^307};C%JPhIn$E2llue-GqwCONs`x-2UGQx3rUtwb_!cCl4I0QaqRB1iuaxKIr(Dw zpLR*2$v%04=1MLL~2`pD4frN1nt$LOX{dZu6Lr&nU9E6%77N~x>Fsjo?@ zZ^WvTNvrdyeRF!Kd-|+{y3A<$t&@7KoBFP!datYcud{lvyZW%hda=uTnUcwr10j=B zi3K&29w$Tgy%P0L?^E}flxzpn{)Z@I*>wM8y zea1IE)+@c%Cp?u;ePDlmMx5$+E7wHm0M@WK+9TuIvxV9-N!z#M+lxiq3(4G1W8GiH z-P1__-uFFq!D>zbK4|>?-YW&++YhADz1aLk;z!Wp>wV+HJ(U>#i%qBPg zn-s*E6J<@9J9Xk*>GLH}f<|YeL@ID;Qh`JP4wSof52S@#@X2T03^-`4zxc ziDJn<4U2W))TCx3qD>1IqFl0cAKLwj*P-65d>i^rDKKPOn1u@)W{Ftw%aDm1qkJ5B zaY@Q6Ctp69S@THFojraA%~AAdjHOLmM4h^&W!8fszdp-UwpH4-X9F0E+o4adydV1B z4g8>R;=P#;KMq#6^5trqKQEqKxy?TlC+uM(bU$cDksr|c%zsmw#4?z7O8j!LA?>q242RTwOG6wg9aK8Be2(xI2 zAqGlf!}c6>XdlM-6w3k-#5C z46?_@9?P+y8h5;L#wCqx(#IzWVzNagtBmqWAUix#u_U=9(M!X?EV4@}PYR+V^^$1Q z%{Sq66D2iKiqlRw=UnN|J(I|@PCo7Q^CUn6O;b-o@ig>LMCDX;P(~XiiOxqM?bA_` zDs59xOf%hdq)aW9^w3H@6$#W*Jv#ML)gar@idSKMHO^Sayl7I9T#XghSz)c!qFX)6 zRaYx`ebrZqfbB?FU5mBi*oczlXxUhseHNlYwao_A#RFdLl*V%cIt@qw_^DTGZk@_vT-Ggzp_g;A!4%Xpe zAtq40ft$q*V}CV%nB&?uR#?K$LUuFOjw$|FwvyXC8Dx(|MwwZaUt>85msf__WLRaU zmS)y$#<^ylTju%Ylw;1%prDP;8R?y0?Rndzot~NMp`R`~>Z7fOdSuO*-nwd^uLhdv zo14bEYpuV&EbO)^ds}LJy`G!ky16DB@BRuKk!eid#w;OQjJ$62P z=*I^?W%17^KYjCOM!$Xa-)G-v_veSde)+ehKY#o8#~*nAXUD()QLlUiT;Fo8kU#|{ zPzg#P0ui3Gzy&t&ff1A-104uK1YYn86x85xIM~4qTJVA%B%uUNxWN*RP=zRDAqY8` zLKv>Ff-Rij3yBj#8RjsCI0TLkeJHIB4e@vm+1QAPm@Og>ktIhoQ4+(o#3DAtiK;uE z6sL%gDhBV0SZt!lkjTU+O7V*+3u70_xRNbGF27=QK^Jbb_W2Y+$2YKl1fspayYL1AS_QQOHb0WAhr}qEOpsQUJ}HY9sy=B zL5Yy26mu;%L1re0IS^*z(wWa}U^GYh$ya9Nn9sy!E46t`ZsyXO-<;++t65HEiV~cj z+-6kB>CJVr@|^HYr#ug`%y9lPp2#%kK8@MWV1AR4{8HqG*e0}sEJO>CDd?IS3I!-Y z5us=sRzn!-&_5z_AWVqpM8#Oqfnii34JF7!N%K+viVh*6X|j zmJpbpbfY5C0sw3pQkGg&S0tq=Pi4x{f&4V6VZ^C~GK$n~Wf1^O1*ubss!)v_bs#9^ zh*fWzRhMqnq+RW)N5Cr7ry8}awmWM_(u&oQ#$=~vO{!0Udeu0tm9AWM>s`^B*Ram@ zAW^mJRFx`_xb9V~Ien>L0pQh)ELL@VEo@!?+CQgQR;LS*>HtZzk%MsdvHm$=XgxdH z#zIxJnFVc1T3Zm(Zq>B$TT^TaGTV;E_O!4~twD547~a|zwG;|2j(!_i;`-LOt2JtE z?|0ke=GM7=g|23ctDob_q_?NRZHKCR+2nTr_aZW(E@3-+-Pd||y5L1`X=93A=4v;* z>5cDt%llmt)fc{>4PaKbJKX4I7a;*2?{eAO-1pMLFN6I{VGwip#2EIliZ|>K5vQ2ME0%GKg_~jdp2ox%&hd+D z9OH(}_^|j@O^+jN-4S~R$rn{JjZHVy1?S+$1v#I7A@W_3-b=JpMs94b+}@V7cgw@- za+RTs<%eil%3MyZmvI|rDv4J;W!BZ1zij39sre&hZnK%+jOHTGj?d54(Npmp=RJ!# z%YH_5p!1w&Mz7h?t4;KD<%}gdAKKFYkXH2k3T<6Z5A4zb%5-r@?dFQW8PkiNb9z$^ z=v52awS=B@p9^H?Sd|*guGTcHq4elkM|#lXqAEgkJ=Pg*ktNA?ERnPr$!7n#s4q35 zv{&M6{wy2YDABe~uPxbYLFC)kree3p{g85NB;0Qen7XO0Rc?#>A?T{?iRbNZXpuYK z%FYq9-|dif@3!55r5C(sn{R^$ydeV@Zov_IE`BHc-{`LQ#PMBm^8V?)YVO{nIzH)- zb1LL68u_RA>+u4@gtRK(ILMRi;&Z>8I?to7^ztD_B~OL1)K~jGu8Y>|K?ZyD#-7@;132uV zN_$1tzS*{S^v7_YyWG)U_rk=c@Yu(B0mPzVWZ)6w0TJ+U+eQaSL zhSf9p*R_{-l9HSI-Os-FlwUAIUJv`>PhKJvPdq_{FZ;R^#NNiY`NNZ+`sF`=Ak}w% z_GABHIzz_$8Rx!0qAz~?QoqTzCph?ZFa7fap!~-NzJJpH_5OCh{lA}o{>^Ux1fu?~ z??>Qo`(%OuTkrjFPx<8kF9G|{-~P`5)lUHFF9M$p`2@_}sxKlWDhniMbnfIx?gSw^ z5F$P>1XVCjNU%^0;shTe1?fg~;G_lP!~{=52JeOj-^2zJh5s&)By_M~_{0a_q#)=7 z1|4DuJ!emhFj59E3WX2~KyU_8rv}la1lcbLM}i4O&`zFE#gZ@wsZa%Z&`rXy#>Q|8 zt8fReum`iy1*K348^Q}$5DS4z4ug;m5yB47uuaks38knrBzl5CO)^m=D1s6#WnknlOS(@J7tzH$vDP$9;C5saH!(C8;uMw3 z^HhWtOYz5G5fm%`5ZP9d6glw~B?lF^PZS4`7hO>yZV?pqPZvj#{%Y~dj!_nOkr{i@ z7=JMqA@4<^u@xWe8Lv@CmQfpviyKF87Kw2is}UEI5gWg;89VVDkud<3aTuks7@0sE zlP(^m5geJz9#gU5vXLEq5qQus0G}rlIV7i9|1BhCnl2W@-1BvF9&i& z_Ocv9%P-#%FmG)g;j$3dMJ^YUE)P>4xsuWdbBh8~C1Gna_3|<8GBG`oF#*vr=Zr6J z3o{?8G9$CkNb{^r^DY(hGyf7aHBB@Jvo%ZPHE+%Y1@kg#(=%gaH91pqc+)ULGdH~} zH35qvoM=)skrSB^80V78LQOfjkTjap8W{;XA5%Knu{vK9JE=rFnUgl1voXCBOTaTU zxU(>apghy<0=H85!;-O)b_ z6bI}7Gd%C}IT!RG0yHK16OR6qLEY0q6VyT@^aT%8Lj_VjFH}N56g>x2M89lU5~xj< zUO^=jK528**f=R0sR?3u4&-6{xbYKEiBKUMj@6=G&6iah7BmA^Wt>922 zB2l-rQ169O1tNr4bP0q2FwUY0v|v<~AcPi33{X`Jw4js@0tj3c2tHLQM3q!cRe@6f zRaIHlAYK(#K{YOBbyaIMfo^qGAGIKMHC9jJCPo!lgLPPQ)mUE@SxG`!X|-8Xl~}d3 zRim|6>*7~S)ml*%TP5OHxiv4mHCV5}S&Nk*$W>OC^;>IIU9(kJx7A&%HD0gvS=qH- zll5G|6<61FAlkKG-xXcubzJ#%TKyGY!&P4eHepACTA7t$0d`)Y)m5c6Vi(p_4>lqO zR$&YFU&R&YU}Z%C0AtIQ((3U~y|hs;0%9X_WhGTp9Rg;ju}K*akOoO0T-9(l1abG4Zx@$w)97&#hKMAWWhu8rEEkF}w_y$!Q-9WOKUa}L zS7Jstaq+ZrPj^I8S6MWdbc;4mUssG`SBO@3awpYxZ`VX}cakEPc9+(7Z`W~w0CI<5 zb0xKRP4{<^S9zKDYM=LbPd0focXv70d0qE)qc?kpmvwWtdr$X!MK^qTcTIm6c(M0< zmzQ{>wtBtydZibB$5wuch*PJs8MEn=mUUHiseglMDfzdZJn4T2%7AtMiGVqYln^+8 z6BY3YFN0qk{;%Z5x4oa*9yvbBrpbzB}$E(myK1ai1ne0a;Qt**s9$lU3Q22f39oIaXHxSwLi&l$$plZP|-8 z)Av%clJB^etxr;Qc|Fcpn8)}YS(%q6u3tJ)&cnxfZQf;Lh66R+c0B(#!P;22r}`=3W*u#=#$ zujR0F*`DvZt_?e{_c^jTd9nlBBNUsl>&>x=8M5j6vKyPSL0dxkTAls+u1Q-`PJ354 z`>$6!v0GbKUR$*@yCXI`wQ)1H1$(v$TenS{w>8_h7aO==(6jZqw2xb|NBg!hLZTGH z^YVI}`w$AEyBlR|jj5Xvt(ys?Tf6ld1-tvZ=Xkpf0=m5$yU(k)gu~ozmNU&x<6}|6C#r zoy#vB&foUaTcXiFT-#1HUjqs!v|586C`VoYnqpVA)oykv-UzUDpqy*^AxRsR`K4TG*ML*e~eWvpw3kecFfJ zV6h#YwB3bjXxVq&+a(Cx(Oult9oDJcAguk{&)uHV{f5@P-h0X2SG{46J=xj4+5z5H z^WBtOUD^xY-Va__6P_z}g5dp~m>xb|BR*6Ye%u@W-_2d%p`GH(or3@_#2_+B;AEXw zRtsg0P!`VQIRxe3MCCz{<*Ur)BQBgz9!FB18f`w$aDFZ?qEBLeA$or0^>8DCzA=aX z5E<=j=tulo*}6JUP#2)>9HQ_kv}4$N*Z${~-t2R}`Q%>gwVv#6-tL87=dWJv^`7l%%kB3B?uRVz-Gt@|zwF=s?!P|p z=YH!$qU#-p=)Y|8`5x%~p6LNU^9P^qr`}F<1quG%kUhjQ0z}W1_OT{P6_daRzTgY0 zUX4?~Ogf5oU4LE>O7>~r>sjpfp^Y$ff2&^KA7g*^YajIkf1!#W!;W7UlmGV-2MrgL z^`9>Kv2psB-^6Zzy0E|GwExh2zx#u~`6uG}b8P&Bt^9%i#=d{`)gQUo-_G5?`T_C! z=YJvUAJYNC$Cocjk_;IFAfdwlg$xTC1i&OB6NXHnK*^HCipGr^DMo~Xkm16ENdyu! z7*eE4hX^H3Tv+j9MvfajR!pgqq{)H@Rr+KJQ6fc)7-`mo`4Oi`odbC)^_dW;%b_xj zc0_s;DgdS@dum-MRVYlNGmY8=sgCb4ISw*4Bnam&bi`~2y|orqewnlr$%>Ds;8xWzNzVyklHCJnuq4Jr=m}ORVt8l zmW67epd#AprLSuL73-XWMrtddtcvR9m%VaItEZ_N>gce17RxB0WF~9Hr@}6k>!`*` zJ8PB8-s-HYejYomx52K8EwPwlMsBx*p4;p~HQuOgv{K6YD@`_S3YEIMLKWtw<<5l_ zzdrdpu8#t{YgWO3AxtW_`Es=H!_Th!SGyA*C2PDYLA>pu12^2U!5?4zuTL7EO0mOi zfgG*K4D0LiS1@NPG0Pm!>9I&Rvzv3LJHKVJ&nPD>^Tt<|Y_YfzC$00+JQvN-&lv|z zt<*#}tt--wj!ZMtFCYE%*E6>)wn|o23$3557PNN3RJWxp#%?P_Qr1?;{TJM9+kN+y zev?e}+zR>sy;k6F>&-V?h_|hHp@WBAc;7Q69{ET^OpbQtj$aOWA`OOAl0f$1kAdcs(E-!3f%+?;1s^Dq2nKM1 z9c-Y3{8t$Tl29b&1Caqi$Pf{}iGwWMpKxCI7a4wuh74(;g>c9g9qx#S3-O`+fQUi} zsnC7@6m;PSbC|&;=C6hxbRZN{C_yHcu!2_f$=aL}JMJCid27?rwR$(bFp4AyYz%_t zx@Sgop;3EkWJnv`=sY;4=ktd7BCr>W2QziEN=I`?9#4bXmpCn5E zr#o%fzjnS8o&f#f6SYE6bVjbBM||i&4+ld@gs% zJKgUpH@wUp;ER3}oAbJNqUw#!b#IGa(wMin-A%7N8Wcf15H z?{@i%Ubj&fz=siVfA#y|!bn%b2`0>f2Ye#<5?90hF0b z!jai8i6iW%38T2eDt<4EM?Bycn>fY_PBDX5Y~vs!_{1_cGKd*6+u1@F03JrHLaO`R z_+A;g3(0bAsSIW-Z`8|QjwhEDLgs`(cgj}AvX>V^-7dcw%xp%io3jl6=QFE$&TAI) znA5yv-O3ryboTR|%^PR~0lLp(KJ<1I9iK&qxy@v*bDYu4=SkbS(s>p%rU}hxN(0)= zp58O4H62n;LweAN4)vx-y-__=In#AcHLNdf>QI`=J*2z9TuPoUB4_*jgEGwL#P`xom&LW(@JgG1*MoFrS^O(2D=QA&Qm5{#l zpl4L;|3b>mliu^jdOcfWuejCI-u0;0q)2pc`r7wi_MHzt??dlTfQIpZUx9#u7hMI|fLif@j2D4yVSoUr ze||!M7&w7_2Xh7JZyF|nZnte4_$D3r6&dJ&DEK8RC>|g9bs>m>BRFv$Xo5{gaw1r9 zB#48HSAibagEzQ>G8lI>xPmsgfGkLXF1RBw=o>~jc{^BxLr7qimsCo1N6-{FSppYI z^;(Lwg^}ZhK7tuxxJhHEIAsVCXNXyVq=sEM6<_FvtF?uAB!@ylhp6?2lN5({!-ink zhhoS{ZWD-gsD*!Mh;wL$d}xP?=qHEhh>2K;k;sVuZ8(XTaff7hiGuiumG~x-$QEe0 ziEG%2h^UE>7>cKuiGNXwdH98mXczWpU^^mfcVl3+1yl@Ci>q;qZj+19wTrCwCBNt= z!MIU>(TkX2jBkRB!}w{n#um-^CC{jgTIP$yhKuo~i`Q6Y%&3jP$c@72jneoP)VL$o z$QH}^7wEVe>i8AxIE~?WjN_P$>xhluSWxv?Tiu9{-`I}y=#2IVjk#8g0$Gg&S&j#} zj|16{5UX{nZL$(C*DmQRU>a5;!? zNtbnLmv@Pmd8wCs$(MZzmC7R#TCiDS2bgmym#cM{gQ=8p>6DDwl#aQWhS`{s`IwX$ znTR==m|2;cd6|k?l#qFncL+_wawMRcIiiV2qh*=DgPLQ~iY?iinD`Z@89A}3nw-g+ zqUoBWxSFS_o3g1Dw5d3@`I;(8oV6yx@hRivW$2pt5S)0EJoiHh#!AYIN zX`RIxn%2oJrP-X-`HFiLSBMpxmDM``(fOO|={d03p5`f>fs>x{xt;VGpL27b8dabA zIiGjapW1n!0Ggiynw;brhxU1(+*zLfIiPM6pir@&@D!mwF`?sAp(=5q&GU-PR674@ zq14o&?&+cM#785_SRksQBC3cf`k*V?Gbm~}Dk>5u+M+Z1qBY8*h3KLSYM?$Lqtof5 z61t<#wFWurWbqeB{^G0LPr>ZC==g-2?o8ug?`N}@DcF)9(HTWX@1 z#Ysn!10R~BjkBa#`Vv&Lp_4c_XS$m(Dy2GVrbhavM7pNm)24WciEs+0ek!JVTBmq= zr~5Leh6j@Q`%BP8nscXcjoGPk>N~)21 zsi^upnp&!%nxwF*6`(q$r|PNg^lJbKYy4PiADL((A(7FjIPB=Fz6PxEn2)^5i^(ce z;TWt2892nstVVgPT2ZW+A*}$}tOfO~v!<;HsjJiatGtM;-&&F3YOcmguGT89@0hID z`itFqtqVzv7sajR+N}|}tm!&z=p?W0immSouzN*_bQ`;xB1?26+p%6+k|-OpDl3vK>nA4bXE6Ir zfO?WNdret-vmhI@F8i_nCF`>z%d<7Bu|u1)HoLM$yCX6yW;naFL3^@FiK`n9$?x3RjlavQdE zo40n`wR$_Yep|MHd$xj`wuHO3c>A|}+qQ@sxQg47VyS-R_mq&!BTN~VmRq@!>y(sx zd79gloXfeBi@BW(mOKf%qbs`Q7rL7}x~u!St&6&>tGcoKy0h!KN_o1sTa>w5x;e?a z+_|b&IZTdgoNh$C-x<7BIlN=JyjzMJ&E_#40aJHPy^m58yC;vk(!(Gpu=urNhOG!?@DJJUqib%mg{C!!(Q% z?kAEk48%h$#7Zo7Ox%4=T*Jvnpg%mtxL3tQY{Wsl#mHO4O{~R9yu&3a#pp-HWNgE_ zgvDq)#zAbx#@oed4980>$6~z0UmVA63_M4CI&~bycbvs}tj1PsM>`UWee_jr!Bnh? z$W-%HR&qN3jeMGqd`ywd$PVGit6@lyEGd($$(78>kc`QPV^y2%hL^m^rcBATY00*; z%AhRC!}-d+6U&L!%CtPns(i|<9Llb|%dp(bvi!-qoXWzS%ePFQwXDd)jLW}l%zVhp z$?VL_9L%=t%*qU)%)C6etj!Wuk0&C~HS)7eqeLEX|hJyJmZJ3SrI6{Xaf!_-J!)JrYYKJC;BWYiFC)jIvu zkrUN4ea>0!(NZ1ORDIN7?ayM3(`dcZX06pt&DB`#)?>}qUA-DARnb??KFyQYalO~S z)7MEY*O?R8Q%%?!?L>ARIE7u-hkeu)q}X@E*l7*ei2c)p9XXTT){gzw@nhJTZP|Jq zKztqAem&Zi{n&|(*@~mtaZ4js!Bm7(lwaf!vMrRf-AYf1+ti}luwC1fGuytM+fKRL zw*8aE4cxaa+`LWP%Wd2|iQJtN+}<+WO&Q%oqukWZ+}7>f$vu|H2Ut&iyrBhPRz4xS)PpQq1w&WY>5$p>Zx9Y(ve(vEe5#wIWVao30KFZdv?CW0Y@xJZq z9_!go?}!NR_znOyZe;~A5JsW1nX3h$$3_HCDhuzq2LA;JFTR|&@M7rj#scxxC-D@o zdKPi);7!0-V|9Ne{Ht`P-000CDV#VOWBuNA=WZ2N*LMRa< zJ_L~P;6*D0A4;4!upq{S6h%7Z2!*0Wj0QE5$VCexh~ZQ{(Rlcm<4O0hQO`Sd1EsStT4wYqfd z)UpksUQ8>Hs?WDyJDTNMm*(242;JuW3zjchuSWsnC9Kx4+_Oa!!+p%SG1IvRMKa;c zq#@qUp7Vy@ijy?M(+fd&CY%~FSJwbyujbsE_0Y%|W6u^Fo2hSs|CtT$zOA}<@v*=w z=DtY0HbdH?-zpbw#kq8W(QQ9JobvkTA08B8w?bKVS!2@Hv;zA5BR6>c%fZ8y_3>#`< zL;yx4ks%N<+_1x$K1}fn5lJ+$iH2UhVnr55axujiX%xvu9CM^-M-Y1)2}llw98w7z zjr{S&AeC&0$PgJa@kl6>lyXWYUv!cpB)4>kN({ezC`>UKBJ)Bs9YQlrf!2i4$`irN z63&I>Y_rBY@dQ9mI(@V=#VFGRbfGJsBveZ{4;3iUK|w53|4c_qtn(qRu7a~VP3z-y zFfl(x?Lhl71$9eN7xMHzD>O|rRe@GD)z1QP>Xg)4E!&jUTP4g@EI(mI5Vu)_<<%`& zZ9P>=VUyMM)niBXRaj_?jTKjFLycBdY`4X;*>0`fRa|F*{np!IDH69@oa~eBTG!Uq zmMeEf3r#$EQDwDWcprRr-gcLJ7eRl`9XH^R?A6a;c2nY)VD<84c3*)N4p3QIt9$rf z@cz}8-irH;7-M5WPFUoQ83x$lkReVvzLF7E$l`)wKG)=#QAW?>i9uFXXZTe9IN+aM zW+)7#!)R-WA<|^JI;Nu@VxriRR$4Ho`{S>nr&*Pn|7ohpvwG>KN8%W$uDSYpYLlu? znry8JbSQ0{)(-o?v9T_Dv#lATJ0-i@7W-|z^TX#>U2WDG zKl*jjO`m;ofowMjch_}?-S~%Smq~dmm=~RR$YqZnc>t)lzH#hhH{4gmf3G`u{jPpq z`y82wD2uZaLP>u5<%fQ`B;c2Sy`uYjKYsbau737opZxUaDzo*^Z2uD=00)>ov*eF{ z{EHP(+y@2tHL!sLyq^Il_!|XohJmD;-vc2C{}~DH4}=w*$pte=LI*yug9$m{vZ5El z*^Q8X8?4_9Q;5LL;4peR3?K|SSU?(n(1JpAhzn!LL;Cr!h7=r_|1kJOBlZx9KeS;J znb;5~YA}mY+~O2hSj7ciQH&-$VR@3M!X>iMiZ9&Z7zr3bGsY2(B2*&`p?F6W=COre zL=6#3Xu|uM$}b&)0%z!$1h$ZDEQ`EMBbo5XNb+nXlx&P8F&P+5iiDGd@gx8SQ=kLIr$JeTP>1@n zj{tpVLMs~4hGsM?8)axmDGE`+NK~XF&F4Yuxl+oI)TR7%=tol;QkXhMrWbwb%gnhY zo9>dGJhzOHO=@BKC{(5<)u_p&sYg)?Rh&xosz=RgQMXFfiS~1~F##<5L^{}?6gI7h z%}ziA>rlo5K(S4Y>|~Q#S;rby|FA@Ctf?NGP|SK(vykPiWHYfVztYnR!4)4+jC))u`Ie->MVfG3 zOI+h3_qSYO?r<0KT75cfRhe zZ+h{oU+3~yNcQFFLF60Y;)eIWMc&+zF(Tv}Gx>B*|Go&6u|i~YZimVSPI8p9JRl$|*UI~q^6|o~-Y#Ex$zNX3 zng82X?3Q_ZZk{ll+bicLsx!!GCNrDIL+2ganZs*d@s?}cV7>Zew_?IDZXb-}zJ8dt z2!?KSCEeQn`gp*X_Q{6Z%V8wyJkWY)cCp(>}dWLUtNsS0Z*lls4 zEp4Vzo730cUACjP?QR3%+n1TPY{rdka}NRCOYnBI*&S|oE87a>-gWolJ@0yhdkEsz zH@}Nlws^~%+w@kq|GgX2?rIyv;3rHtz#A@bf^T`^&8_&t1Agy;=Ue0OHn+vs{cwCo zeBUI`w#gk%7>K{S-2e9Y#X+9&0&hHW9Z&bkVV-iC_nYSo_jz7oUT~JL&E*&udCrsm zwWZ&@>B`kP!lP!RUpHn$Th~I@wXk)thdu0Z5_{P)775+EF8HnEJ@0aVi{aUhc*WbD@ndm3;&Ej8$z!GR zmXAE#GtZRGLq70=|GbC#UU$xq{_mx4(diGLdd%-V?XG{k;5*-T*2^UHwpab)Wj}Y? zTRus#C;XDQ|Biamo1H5g+IEL(S=R0-m-$3+emSB48d*=j`X-G&J+*(C=L`B*-e*)} z!k-tHUSv|f{e!0ddyD)thyVnN^CQ6j zJHVMpz_BPm04zVka6qhpKnCO{`J0dhltA{AqzY`0H(?PM89^-=krIS96ih)BBs&f1 zk_;I^4xGIf)Q}Qvf)i9h9Av>8aX}U7k_&9T8N87iyg?jHK^-I$7*rA%q(KvGf+AGG zBjmv#{6XFGK_47KCw#&c47?zGK`T^3D`Y|&%t9@+wSJ{6s0l#7Hc>QS8K0Ji}poi^$28EQkp5=^O~m zJ>TgpkeJ0rLL2j;F$Q%Jlpg_iv9KZ(@Nfsf=nJ~$fM9B~IHIHlvmXryX+&&KsNtry!3@jB{yS`%T z$?>zR4h*Hz0LGbQh?*RUn*=|Y)XASr%7s`;k!VWud&-hjNt`rFV!?_NRW5x9H^IT=iu zsLQ$3yi;V$R78}$G)yk}OTcUl#LP>@luSj;i^}vu%3I8`Y|KY_%)TT`%mmF65zWK| zJMB`B^RnjQU(kV^SA7x4-B~$t^(EPbJZ)2-#L^S%M=Z)OG~bV-(d!y;IJ>RH@NaS>x0xZPicB)KHDpQDs$Atw&Ui zMoP`q2yxY@fz>6YFiREI`=LiJTh(B#)mOdMVok;tf>dW6R%jhlW9?LAWglfdv}~Q# zT_u}dO&*JUGGGO>aMhb}^|xPTR&?D~Voiw`)Pfdd(J=u4d;7!G8xyr-L$RaR9kkae z!B>IMSJC6wzVlbLt5*~q5`ukL;fuk4RoK2`Sju}?gXKMh3#>44xhsPF0`b z9U4C23{K$?&kR$h#;l}u5L7fgkhEkR(WIR6P5PQ+nPu65drXV_YV_6R6*@0zVev4Cn z`>5`Vs#vtj) z98KFa&BKjLmcC1rCJdEU6VC+ABZ6Wnzf|h0p6ZtNX`qPey7lU{tm?M_>z?Mjr!MQQu1%s&>#;Cv zq(BHV^%+~48o|DjC?9uKJ#Ww1`ZtThCYoTWCzozW3KJB*v zsc)RhgDjzfPRSv*rrZXg-HyqF1Haz}|0tmB%G-8{;%2DhK8)lZh~+M!4k;mM#O?CY zZHVUW;WoeM2B7H%pz2oc>jv)Z7VhUpC%TdD%b;#jRm$!TZm2x(_b%`FR%3@AZ{wCq z_9kxpW^Vlk@AkHCVpZ?P*l+IcZvw{(1+L~MF6GOp;e>8*R(^0EzGn&V5D3rc3#ahj zo}}fLMnXK)#xa2nt43H0RR$9RK}c#cmUk6(C@*Y_asc8#ZZc0c)w zPx*#ld48XFgwGL$k9q6}d76KCYnS+hr+8>rc`>*UF(`V|-I8@PLWf{_M3K%Ju@rk+ z`Ymh-r+*ZvzmiLV6KKnNK`Gd($9k`qdRpoFthf5Gx0JEp`ZO_ns7L#v^LiQidaf6O zv1f>@SNpSPdogi)>4bZdmU|lEd!@Jgv&Z|i7ksk!6Tgpo#E*NqUwk}id`JO%$X|Q9 zZ+pXk`^OLb$R~U$q5Q4Sd&LKRB@unPC;iXQ{J!7(zwi7W*?ZP6{JUR>yk9Qok@9+X zK6Mu*pof^#sEwOf{}QjDq?^B&;P;H-|1soO%HK!++YtWWU$N#d_OyWh=aBxAH|U|i ze(b*%mxmMV-}&eNekNZ22!np-_kQ(1|LJG`?*FCn2Y>XJvFhJ=`8R*<-+fDse}DiW zP#}|n1`QHq0w4&(AOjylj3`m!!HN$XI(%61V8VqBCxQ$KaU;Tk7)O#+2%w-xh9X~r zOsP`fNsTEx)`Y2Zpv;ROZIZI_eDkj1 zm~-21?K`va|3t(s8%m~p`0-xMgePl88I*41&!H=G7VLR(;nO@*Cte+tvFn7RAuFEU zy0+!ASAGNktwc+c;#Gwp7vkEua+t-9?@Zo2G;^KLn-G<5_&WCL&Yx#bzWq7??=Q(m z7yp-gdF<0gqSvW?J$du+!KX()9=!dK@nOz~7kmAI^xsPX#x@>7S0_4Qb+s)*UJ1e#RA9V3RyP3FMDcuGnK$Co(yrNC+zFVwX{Z1SXA3 zI)|l@|5_5oWtv`wWTsVao~P!QC7vnfn`BNT=ay*J31^vj&Iu@dcE(BQpLzz$r$m0X zX(#|rQTkLCP(Z;%Rxe2nxQ2@^wB$;Au0S^eH=Lk@CfOK)5*fJWav6%~ z=%b1*Y9*TwC46VWc}fi9aS%reCBp>{+$YCBx;XJb6(@Rep&q|@pTiMB+HuAjz6?>! z|0J_)vOp*kth2@kN5r$pDo5O2%MI^H<&#GHEcC-f%gnUKNcv3k%q}OLrNJdXq=eVQ zX%TkTRzV$RZ7^5e@!2+~jWnXJm7OAxW7ih<&ovunx7BZ-rZ?7nhvv82A!{Z$$R5)T zciV^0z4k+fPZVL|eJ>t4hLZl z!lnfQdXInwL|_6F3AJV}RK9GbV zG@%N|2SOK$@Py*a$x1l#n%e-6hIz5!@N`HJ$CRXpNZ|<(SzaFk^!fkreYU$<$#hm3X#g4zZbie8LtybL?ziM@F;lalEyCqAR8&v~k|pW*zcIQc0}8s-r;#MFX7 zvr^19YK936Jt$HR2}OufMxqVXNJm&%P_Sr}qOh}Q7|99JiFy&C9j)a)Pr6Ye*07|! zL1{=-8dHy&RG2i?N<~*Hnvd%ArHs62OM@y>hx(M4Iju@aQ`*y;vJ{|dX=+WM+DeQr zRH%X^s#INiRE|RRr%PSSPOT@$*cBBw(L-xjNn^XQ1{A1jttDIK|7uOJiY~2pg=t>f zI@giZ6|UUGYv=T;8oIj9u!H4mVg0&STCPp8^aN~T9eY^H_SLSJC9LBl>qEg#)w0!N zPx?rUKGU8LwdPZ8`B+On*NzXi;*)LoXbV2ueh;_a({1;7%RS%zmT$q6mqz~ zGX8Lm?^K@k=?A*t3o?+EBjoQ9S$#=P-;&YSZh66Dz5Idq{l34Q`36Xwyw&oVO{H3^BUK_)-|w+(rdK&DbtNPHrtX-YQu1v z2#E#&5v-zw;YEAe)vn65ftPK8Y?~h~ed=ld8`c}f^|=py>~BB&*Aw6EX%|SPoI-L+_53f*U&`~H5`DZ( z|0y4b{?I9t{pv5}`f>UG_O0Lj#*m-m+^4%?(!cTbD_{E42fr1>uNL=11^#IF{`|`y z{`0qA|EZt(v7h`kAN|=MSm0k0=-=^Z#sW5A@I0UZz8?Vs&mTdc1y)Z6{@?a+VEu8= z2X>DLh9Cvzp9V?~33{LjG9U^*j|yrJ3~nF`Mj-RZU<%Tp3))}~c3=$hAP5c*2L9j( z4q*u%p#K4(4<6wF_TUUIVFflJ4nE-t|3={p7U2p`VGCB_4rXBuCeIC8VHfJ)7ha(f zRtf?d;Ta+!5sKj!CeP=U4;+$@9F7kiijN(J4<3S#9)1rWdXFD=4q9le-Bv#@iY9jb>A|`rbCvM^;s$nLQA}E?7 zrL^7mgyJcVq9~T)Cx#FC{m=K<;(&QiE{+c`ijObWV*lOZE()VAvY{Fd<1w;gR2X9# zE@Lt}qw_IiFBT&;9w9VhAu={&Ha_DvDo-H!PD3=`7kq(0lnN3-&nt!FD=q~&nq#M+ zV^yi+@rYxkxFZ0Z<5|fgF0JD#|60L4=HvCKMm&BaJN{!i;v+ojBTvm^@YExv0Hi_+ zWNZ+mPGH11B4j`+okUnt(mkY8M5I(yBu9cHJsM<4PQ^l+RYQWsMcQLVO5{em6+fD! zKceJF&LmEzqfNr3NU9`BvLx}GWKX8#L|%qY?juINqfZ)TNA{yqCL~kJBt+mHWysuB zPNTI9K~**eR>~YULWERsrD1sG%7NwSY$eiJWm$gQSq_9)s%2NQCC9ntJC5aF%q3lV zBU;`iUgYJ*)n!8VWnBCvR0JkmKHXaWTaEd2%dZ%R48x*;J!=-VwPeQu`~Rp<^~sD3&qXly887-)EU6hwq5 zHg#x#@~4RsXn~q1hEAx4wkUqSsE20ggU;xG)~Jg96pNClaz=!NCSi}lXO3FvjvnZW z7U_>hCz6t=k`5@7|E{N#?kAB(se-PiJqf0b#wdk4mHH zoGD0Di!6Ezi?!)QylI)nX}ip+MAYdrMhgSwX+-Sln(~XEaw(ePn7_a&yvS*xx@mzW zs=X{KkEZF83aYx`DWlSTw zMys%3yjMAYlOK199_tV9&-zlLkTjO)6ZE8iKcuLdl+o(#M8 ztGhC6x^Anyc5BAk>&C)s$Ledf^6R;ZEWTErmhzt2h@f})57f2 zUhU6bZP0oxBZ39j&g--`4K#lBwqAF0a}y zXnY&An)-eZ(}Sk$TqLhoNbImZ{=NYXjJduT`zQShxQVI^t#6P z@?H4G$oPU=`Eu{{8V33TjQTz=er)gTb+2Z~?~TxJ_O`G2p2q$LNB^=fwYe|;!tY-Q zFmMnsY29z{<*)J*0rPH~_&V?bLvRA)#RC6E14Azci)#c^?*v58`Ew@e#u@Bg?TPi}E37vLtUZC4Vv>Kk_I?GNb`O z7Y}k8tMcK|ax2?1E~7Fb>%=8PvM*0^F7NRi^D-*~vMCF4FjGfyXhH5pUf~s<*aV3I zO|v!E#&b+_-@TqSe+@QgjW&xjj2xLaQ?uoXv)7O_M3^%-TTMDk^E!ufJHPWo|HO0Q zq4R`%b0^=kIOp>~?DN<3GtK;SHRCgJWU~RC^E~^rJxla8GjusSv^--CJvTu@v-3ia zNI@HIMn^O_PxLrfG(2B4KSMM^OEgJS^hc+(L9evSaCAUh^FRv(NPF_WYIIGPv^$@) zKF4%OBTY}YG(g{UP!se}@3hGpHA%a)NyBv5JhVu&G*lz?O9#o^!U#tzo*$0#D4)%B zcr^eF9yC8hSo`rh*a<^>i6axXV@i>h%?w{s_`@ZQdGYxj6AZV+a7auauS_l|dyw{M^Kcvp9M zUw87LcYCY1?!@LJa4-@z}iU;|S-*}6^_==xsVHr7({|_F6N3%mS_WPdg zN=_w%NcnD?FKi*@cVu~OZ#l|zxrTgsYX5cf(w0$*NSVKO23I*xMLAVZIRd}AS;hHi zpLw;V`IURQoNGCi@A;j3c4niw(5N}cu=!^Lx}ozqnfp1IFM7xvy5%G~q3b!L3;LY9 zd7#gBrRRB_lVqmtmi`gg`?jaMwa0s<|IGVNy!$Ve`>=yMx~qGk z;`_h5d%bu2wfp^$YqtL3LX*OR@k|DU$w8$RS){pfps-Ge^8qdv$6 z2j3@s>%aAH!2apaHeWwJ;>Z5&lm6wKzPRK4V5k1x+r7RAzlY#{>Fd7jV}0`b{_+FA z?9YDfM?dcm|LYh3=t`sD|3#^|%Dp>|ZsDQu2Gkf{F{}7x%gG$Bs z3w-#u|NE1F{Ljk#tAG6igcU1c2!_d$WJp4V3mFap000P(hZ8AQv`BFw6pc_03iSBV zV?l!m8H8F2ok2qgCZ5)-khqk>e8!Ow?^&y|CQxVmp-owBwLniO0*ZlvTgeouGF~| z>$Y|46mDFvdnfkwi?^&^!VeAK1zeJ85|WE4O169ua~92(IbTka8b* z%?C$I#vHnJ?9!A`e^$LY_UqHJThCrxJ2URqx_S3@XnMEj;H-y#hF!d~Z^FQDGapSH zdi3L_sjs#!8hduj()V^>%{#pK$;izMzwSJ_dh&DIe+T|t{&w1&Q+r1bU%m14`;9-_ z{L2l%0N3NLz~%k}Ek5z)L#w_A@sn`C=^Cug!U!#kED0ir2qK8ZNQ^8Co~Dw_FOMV> zkwg=NKvBh`SZvXX7e_3SuoHj!tVW}5|7=mm7<-)Y#}qeO(JsG=%JRr9HDVIS97Ehu%Or!$X-Fr}G%`vgr&MxGE6W5kPBEb%6GuGfk`g2; z>+JH*H=+Df&Oqth)6PB(O>@pQacUFGN5fQ;OE$d(5ynH&0`wz686^~?D@;ArR3(%^ zZX#7tO;w8~WNl(9S6{s~)m2#qz*bb1h;`OlmB1BORd?le*jIm@mDZzrCH7QfcYPLC zW`%`TSzTGB_KIbLbv9XSb3K+@Z)Lq!+;OK3H(FrN1vg!Fw`I55Z;j%XSZU|I)!b(9 zr59R#kt^3-e{;gOSZxOexLXf|>8f>?h zzPo9&!~U7?sr}Yl?7+!h+vd9APP=TL6`wM0fz=e09k&Pn~ekbr+rYl_*8q^u}H9J@?Lc#~t?H zTMu5P;X#}bC)AmkK0}KV|C0V{>Q}NJd!x30JN&)ChueFkhTy*Zxz2|?{qf5rdqK3` z-#q^H$wxnXqwWv2eqhnx{LGZTi~P?;0i??S=O-BhS_Fag%OCsrN0Zgg-idw`X0VT6VmLZXL zT{K@9sW?Uzn(=ySOrsKwct$zC?u%cEq8FRzJ~q;?iDcv)7`aHtBwn$OaWv!{5xK@f zHj{Y^G7oU|FT6+spKQY$VZ@zWRrGWBp!dFNi(96l%J&JDes89u}pH1gj6IX zQwb7NE|HUM^rS6e8Om99@{p1QB`ZhiN~YYhj;J(dDSJswS}s$SvD9NO0qMrWG!vJi ze5UZSh)hTRQkTK>rXRHlOh(EC0B}MJ)ilEt#teZ(@$^(Wd6dbZ(33Fi%z``BCr@=g z#7Op>BtG||PktuEpYsgpH3$07sTlMp?@Xvg7V4OW&hsb`g^5J{Nzr;T)F1?%i$+1h zQF(gwAp#AlLo>>=|@Z&Pm}^crJxCF zPKN4{qB_*4|ID;$L2-Iec=B{2QT^mfF)Gxm>h!87o#{s{%GH}P;iVgmsaShT)|#4? zs%XurT6fCUth)85QyuA7=^9p}jy0tMT_qL~GA50n<~*%|(?*yOgzRJin@lR~o({{O z#ExdM8folfBWtC}0-&;&xvXe13x&->2eQK?><|nK1;o0iGo?LkaZ)>2$+DKRuC**| zIU`%gqPDYL`fO-jGuqffiLt+>ZE!U;T-}P6x0dlOXH|RK*1pz0(nYRjm5Uzcy3M)R zjm>m{TV2oM7PQYTZgeF>-sPHCxacJ=hZMVA({?vE-{od_cbnbz+IP0zg>Q4^t6utI z7Qpx&|Bim6=fLascf1Lvu5HizVCecc3GVHTGAAXFAhgVNEK^DnzVHRLKE!%Myipk` zh5ENq+MJ&D`QX*ZI$P4m6nw9cM$+ImwA$GnSpI9+bP|7--$h2uVY)BTn(b3)uw|D4mbAp@J z;+_k+g>98@U%N1w*7if#jbd|$^VwX^HnJ^J?;78G&%1^;2BnSYa=ZK7@P22P=M7qe zw_7X$&$pflKJJIx8{zmy_p0|@@L*#+;+IS_ud%IewD`N)?Lw>0!h zUh#Eb{NEXu_{IaWFmpSP`$9dBO{d7V{-Q_f7 zdd!PHa+r33a&UGztE{ENMV<);5jK1=PquqRJe_PwX{&rYOUGDsxxr*WL|GvD# zbnj=sd)9aOcc=3l?1LBkvboN7uk&5;X=gmyyB_!u_T7GluR7xG{&T_$S@WQ_`lvCl z$IzF3^u6}^Fqf(lU=J#tRR2h6M|ITCG_&=|wi)a{+<4lr#7MVieOFiV`r7xN@^iLt z?E!n5qyv=ruHXIfW$%&St0ef(7wJ@t-_z=Qc=^ERk?>cU`+iT~`@Ro9_Q$V%^S>PZ zS^d89x1FixlOJpEr%3nZum1YO{{GxA`gAY7(l7b0?;^4f_XhC%3b6kUaQzan{TA@% z91x)94*_jY04tF9Ffal&kOEIG{w~n}=*|AxDl|4w1T8T3N-(_u5Cxqt{{(++1X~aS z|Bv}7f@6?F2yg;n^krgl!dE(GFM4n#eo$M25GRI^2Yqm3x+MuSW+j-=G>lLskZ>ZD za0;V^2V`JD+tz&=Ga;66-J%%TNxH>V$g4*Z?oo>Q41c?oV1V)!Z%?Z7xb^anU&M7F~!kx+;Zg z5f*z67iTftTyfSeLWVvst8`J;c##-=(L|iF?2^$Umhl&mZ9leni!EqOlE*h22D1ecOo{=8au^8D=9`(@{>yaGsk$c>+ z6SGA`%+L-+1Yv~02${uTKoJTP(hV1q2paMsQ}GUyMO7%W4lNQR4RI7(g(H=~Ba09u zpU@#ik`u46Bu~-{Q<7L#QWQt>A#9~1i9jJQf+1BhBW)5{as?*Qf+j1XCui~}TM`Uk zQYR~tCoj?`Su!GBQYf49BcC!PYf>rEkSTZ4B90PQXc8hRCMUhpD4`N5rIIX7(kf9B zE2B~?r*bK)(kv+gEN?|DHIgLh(kZ_ZEyFS?<&rD;@+$o@|0~B*C49{v8_aT`Q58$f z*>aJa8gm~70x~u29-%469MdwtATo`SF}sK}t>7||Bl@^!Gb@ucJrgr?;xykTH3RZ7 zS<^H%Gj2w+G)?0*DN{B(^D`H7(_r&9RWmoUaWX}dIB8QiZPW5_!Z%wJCw3D#dvi5^ z^DCs2j&M^sjWaZ}GdZnuDzFog5KkmzB1MKE9m@kas0Pwl2&cS*$Bv~Uz(X}yrak@V zJy#<><#QtFQ#0&SHU-T){xj(K^Fj=SJp;662GnT^)Ij|+LDO?V2XsFlbUgu7Li3YA z(~~|fw6PRaLmQMso2Wq|bSfqkJ{^=lArwPTG(;VMsZX|L3Btr)Ibk3GmtbV#K20g)JnBLOJgNUCqhI- zWJ^J&B6^Ta9a0D&vI(^GTx7*ei*i@KlnBCf49OG#+7wOIlvdcZP5Bc}S+-#@wqhaH9r1Ny9d>7Vc4OZxXKf5-9_g=4zc5XlFKJQ%q|^_GY_QX}>mWZDwna#cIX2U=b~CcV%t=scj?e zZCNF5J!NjgR{s{(XXO@Q^_Bs%NoT#b|7Xv(Z2OgLuQqSdR&2R;a1VFpFcxqj_HNCV zXC@bMA6IezmT~v?VkOpZ0~T{VHgVaOb0K$SOZT6mc3#KCVr3~A6RKt#%X2?><7Ag< zX*X~MH+KDLb{p4mdsm)*w{~Ioc6~Q@fmdx=_GIIUcy~9Ej(2#Iw|J+CY@3%vo|k8n z7JKOoXn9O|;g)%4HhZZTdihq>aQAy_EPU;De3iC**H?G9_kGn@XTi66UG#d%*L&}m zee+jHAGdq+MGI<@D9QCJ+;l9rawUKgToHIG6xdoASS1`7T_N}=Bsf?o_#!MAUJVr| zG`Lmg!h_j0gTHWr1+#%UcuXZZohJ|<&lNf}tSc*p&Ev#6HZTO30 zIE=OUgWYly!I&aYiCoeD3vXll}mYnUb&NF*^_5^llQNd2f3ATxsrGJDEb(VDH%CnnU`f5(`cELbI@}T z#eFY#L>;sFdzSSJ8JcA&PWCyRrMa7Vm!A*FpSKU83woi?86CA5 zCLr^6uCsm1*`52jp6fQBGg_fH`l18aqanzkQMaKv)Ee0jqPdZqQ(6L7dY(J_Q$iX) z0~(`e8m94Cq;Wc)Kbo9tI-EKBrdztCg?gun8l-dDrgz$+mHMZ*IjCtGsz*9vd77Yu z2Rv2-nSFUzmf0xg_?BHcmpNIGgL$iyd8~E0tiSrKZzz>Vd5>|Tm&N*6yqf#kS|{H6 zjq!S}x%!vanvd~1|K9Z4uFX2E{koV}`K}Ket?OE`4SO#d`H&x*tr^>`)B6A7y7&b9 zod}z<1KYCm8nfY=uQ^+NJR7h9U{`stiy;;<>r!YTc8p_NK4+V@cg41Eo3(XYwp*LG z`|`K>Gq;0VjfwkMhTFKGIJuA8x0kz!oqM;5yQHC;?x@?kuN%9wTf4WLySv-FzZ<;6 zTfE1cyvrNAUHZGH+q~DCz1!Qp-y6Q;TfXO;zU$k*$$Kn`__eP)zu$1bxm&;WlDY%j zgN>WO(b&NKTfm|Fz^RzQ6}-TSyTQSG!oR!1z5BwuJHr7S!Vz4;JKVvY`)8@`eR+Gt z-&w?6*6q}r|36P$onLoXTwJ4JoKUv9W8Q)FN_4d81`(2aw58#tc;47KnQCUA21(NgKK%rpa>3Z7^zTXnQ zm?s{w9sZIboCNKF+O&gN;~Tr;HCvTK`{YvovJIZ)JzhRRUJFEiHaI@u z`&!yyUbJQY;W1v0eSWYTzU0;V>8pO-!nEMo zTe8Lb>%%+j!F%k#yX?LD?7KVdxqI!myY036?Xx@Xv3u^XyY8*~?(citp1!+je!Tr& z|GWV|yagY;3IDqd-@6gN@A;m)6~FNrf4dz&@*!WlCBO11U%NAXxivqzIp4TFU%Kra zxz%ua4+A4e*dOjD;O~0!h_)=PF#2Kz`%|HKz2NN zaplEA3N{u1LdA(%F%na2{x(QVSfO<8 zx4_D3EvDqQYpk5ZvP&(r?>38VxZY07QD*w}<<}Ji7fi4c2rFSQ!6X4((!vENtnk7O z0}${<5FeZ{#1&s`QN~wD%yCE#lLWHJ6q9W6!x^J&aK|h|>~g^{o80ooDT7pUNGId` zvdSJ$)HA{(>%6kfGW$$3&@nGOGs8?H?X=QR8y&PnLr?s()lD1x^c7n1xURoojU8`U zX3Ld!TWh1$HroB3E!Nv}$E~*AY~$^=-f-tT?%jT)%N5;V^?Y7lhT|t!;)O9TnB#vz z&X?qf-+Z`Tlw)qW;%#NV_~wm+{Z%LI`s$aa4*&bHvb!F8>wmp& zJMFlie*5h7CQ*CtzwaLW@V@g7{F??MpO@~$AG=ti(_dP>^oUWPz4hB;zxmH#>x*6XmGK zDSnZSVRR!A<`_p960$o~)Pfbkb4Z1pafWFe$Ql*dMn?8=i+n^Q9SLGbHHvYNmo#H0 zA-TzcbW)O^Y$PTP8A>%Ka*=t|WFd!0O69z=lCiAi9$6_%ME;SIfwW{V9XU!GmQo<6 zgh>;Yxy<}U@Mr~DWF{(og^e@THqN@Ayo66^i1d0*2&Cv4&nlzpj1?WWwn$d2yRH85K zCrW3!G=Z@+I{GxoIAOX|f%cT3HU%n2h04!@$Wx|9tr}7xy3ugv)TuEADNRNCQ*b)< zs_x8cN<+HRqnlvUMO?h-+NMWTPAjBtUY_f{z^1yw(ZIt$<}hUh^7} zG~rdQdksQg1@c$GwlzSIb?ibkLX^YCHL;5YKw}-dR>%fOvI?Q>T?LES9Pw3ne&rKq z+3Hy)fYu{KsVr$PYg)ykma(c`twUP-T9?Fjw0XtsZ8e))-SQT&ll|?0W~acL7XW{AO3b=0$LK6%5`63)sB`mXm>l zaNf0gx5Dg2UqNg@;u4$q#3W{uy$k|`7Q48`Rsh1ZWbCFF0|6lQa~F6c^bB1fg-8hc z%Uymlm}@NNDjWIANH#Nx(cEM&J6Rx7j&Yj}V&xmhSniM!%dF&0#XW;UL0Ol~;O$yvYJHk%ScDY+qJ(&QdDvyV(;&8j=z@18fW$mQ;X zz+2q~F8RW(g7y03rDV1quKF04x9i2mm+)SONeD{{R6997wRB!Gj1B zDqP4gA%%tzBTAe|v7*I`7&B_z$g!ixk03*aydbiq$&)Bks$9vkrOTHvW6F%_XVj@t ztZWK&Ftex6pFo2O9g6Y*(W6L{DqYI7sne$>dkI_y02bBzkmY^9*oi!JHm()D_+dFvE#>(8BT~y zxw7TUm@{kMjFKnk&!9t#9!C>oFt6t5zwd>cgW6PdRySDAyxO3~?&AYen-@thwBO1ruDt4=n{2%H&TDPI@OIQMLh2HXFTmFB8?eIkGR&>R z1M6EbM+YC2FuQF+0fj(NEHT8$B#%r`$wT;LG5{sB-10yuHxx6>D5vc5%PVU%^Flk{ zjPuMnXVh~-J=bh=%>o6D(a;DLjkM1zD}52u2RRM&)f`c6kkvo`ob}5*gAH}mSZ}0t zL0prZblEzKO|;lXukE$jZZ{2g-E4+zw9Rr$|NRo#Ca0}++9$Iu^xk}YJ19vg}W9 z4SVOY(|x+`wd2iZ5_Kk8(5*2I8fv1k@;a=rzy|9P^UoiDs;$jyM7{LZSC4)59Bq#( z_uq3LKK0{wFTL>~iSO$5ss4JA`?Pu=Kk~k!|Gxdtv(GB?>4R1DLP9@X!4G_HLLgJ{ z$18|9Dp2P8ll=1Mz)nH1e%X^%1&4J(12T|;4SC?7FsMKchR}i|^k4?H7eNdnDuwDR zAqzJctPaMog(hqi2Vposk;qVhkK&>8|5k`A9IEeyMugK?B36)pEo5O%G>EjKXfSg< zF=A9C$P}%ZE-s1-i(b^?6ho3lje+rt7pr1Jpcux1Y>|v@l;Rl?Hb#(?QI6=kqrcGg ztvEJuj%G|F#{4M8Jo@pCb|j=50jb2c%<6~2f}ouzge3G6 zCOrws`E3$?qwL=VO*y_orE-0%w1_29*-2G?kd^y0-Ya`Zz*7QnmqRS2EPbiVS|%|( zhjEV*mdOw$0AQKWbf!R}Nf2t51eytnrZ$;5O>7Rtnj-P$LAI&QZ;JDo=LAVQt4U6A zf)kzV>_|Mj*-mM~)1LRl=SSw5|IcljlbiGes5%8&ka`02o#hNCLD^Z)f8sNtA>pS% z?TOHL?sK6IC8tAcvprF�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

    R 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ÎsXY2k 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 z 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

    boY>(Kr1mUF5@k|%u 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

    Rb&(d)a#QEXKj(LG|34_>p@{f>M;SX`SN)%2`5 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%0Af4=0aIXLtS4HA;L_ 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�fXSdySMvH?t{t?WZ%gQZvR^O+5Y9_fy_=Gd~f&*i{Rnm zYnkJ`s4vf!kM0~YTO9s~Km0{c2FPDtNx;*&CheiQTjawRxT~f zUGLiVdQdJs$juXZ;?p{g)2x^tlekk`t6}9DoZR9Z5p+&gcnz2Ka^%+RHeXOX`Emvk|Ss)}eqmgE4VSsW+D*NEee;ubw|z%D%an zx_uS%=qil#TSCIOPc>JSlULMouj6PbJU+r+Ou}Bifwk7d+Vf$ZAJszhFWT(=9hd1RRPHB^#ZO4kPjKPSwd=j-Y%-T)`BxL9zt$PP z@vL0Mnl0^+4ooII;WGO=*Qa%OeROQ5bwKb+=h^i;T7{F_Z~&YY4*)PAq8C=xW|Rvj zz9Ab*t@BAS3{1!@ZIt*)IsOI%e^=yGv~mKQmOO+@hzWq^c$lSGnY5viMQ0VAW2|S_ zpH1!g1v1*e(VH(9L^3fvU8Ga2YS>~;FjJ(?qgG=&Ky!~rp~isNWToS!Q&PQJuj7sT z*e>BA_H`YAnG@x6&D{4aH`H@?I6CZBgs&poc z)AsM@N1F`gw?97ncCmNGP);E8^_&*-29(*E(DvhqMOy}PJ%hy{$J7Pda44J`-r$m! zz~rH9vkiUQAb-ND4=D9JT$|d~brnlS4<`EGo0n(}Qt_!z4KCQ{XsEpN=5?=J4Sejb zLU&*PSe!YT)m4o_$5VG0kr1mhima@e8eOVeZPMUR)h_A`?}F%Qh7S|XXaX?$o)i3I z`pFaGA2nw*C9r+M6H){3Uv$dWe!ta$%B5`*&f+G|uSXl1bIRxx*TacdO@zZj_PTy`p#b2TINe1fCw$ z&70Y5iWr)?u!$O3MGV}Vx2{D{F4`vGP%hb&^1WMh$ec)9vMY{hG0{`%by4Ku=9!lW z!MMDA;I;f-QMH) zc)QR3wpYuUVRf0?*>P`?JGrUh?GEMn;gXJ(>DqZXDD~*o#a8E|H`h>Uw;o)MF1J2X6>0YYD!VTC zA%+NPj}eaYE|0OBL(-lTVxPM_CvW4*Jf2eFc=dSZu8Pc)IYYZwPv*@cWV{yb%3pac zKN^yGy7Kh%tEV5IA0g|zQ(pescdvd(*6*P8^J~Au zH@LU`kB2zk_@7Lw+zvRKw|f(C@iF4|v#ZbLZ=QWW9J>Ad=f&qY&#$j>%>g*#C=ewk z5LeM0MA3$VieiFDL(L&rAcMdF6HL`_4i#yWBk{s8g)l%Y@RY>m$P9EuhM_uAws zTQFhYc;*Og#QRooAb&MQHGsm9^Z&>cWk>@VYMF~E@(N+$$^Scr&gH2LB?eC%(fu`O zq!*?Th}Ue%rj}HWrO>sEmCX&)<$|fLo9-3N(bzdrv zf0fbr2Rx~DUv+P#J0#nH_xHT_<_9*C)u&%SV_t5pEO$IQ{Vnf(^Xf`y>tMC-b$5Mih?558bBR++HhcTGy!YeA&-Bj#l=h4O1tPf*0l2^Ay_Hwk z3PVA2qD-OGMRwExgsHc-3-Y&?C-N{`%lr6A6`z6%Yq6#%_dE7t7{xT!6kQYKU;&J; zVOySdbG@0-ik2MK_m$WySs$ysGyIsO4Vt&awmfCt5B|O7$-ryTI2s(ZvzcM7F1ns6 zl~(ziYl3f+tkv;*-g{>?&D+u8_q;d9V|%FMdj9XONj82o`#*#ye>X*`cA`=%?k4W0 zRB9EDrc`M(R;5&{^;}1%)Tm53rqn8}si)K_97iYDO988s8zkW0ozjeve#uRug7=eO z2+K(&Hw)>|CchN0_@30lotcr^Y83Q6rR`4Qa!R{qVP{H*dSiA&46e-p~&(mvZj{2uQmQ-j6DE$G%`` zzqYbO-2Xbrknbln%vIjmG|E^1y=hFaRq9s7-%;rQ#x?mL&wF34qgD8?Hc|{-uQoI6 z>aMnOo_@LdToA$kZM!7h_1jKGdEK|&n$|Dh_8NxxzwbBCyM8}tJCxq&rFc_)c&Agr zzwr=o|G|%rH+$sL1ARl;KVDCM=KovX`*8huaDLza@Idr#*TqTnwanF7mYMA3y43iu zt1rdR>%T4fXkKfdzj^*>@Zu!p&5vK}`MPM#5$9KopIE ztOk%Pa(T%Cw3ZyJ9(g;tGWS5DS`e!)s}H$4%|Mb;537zaz6>Gv-QYc1^Vg_M*}Go4 zDVHppKQyC~w_oa}7F%y>J&@2aD$`99EiBdinnbm8G@brxoZzM*%eWcktqdb1yFgT? zrj;ln^RCX8U?x(_&H#~hCy|}{ALP9?%O&SW-g`mA3$7z!d1rv)0`IIX;sKmjbg$jPYYpj6^b0{Q_&`;Mbr}&%3`Ba@xG_UjKr0y zD(cfoS*InOS`I&*cW$SFCQI}CZL}Us=w;+h2i{EO(z5T+%YLm}erwG^JHSMvU{tZf z9Eo=)wOl_5-@5X2T}WexU|sNdH8;3=te^W<)6#ei_n$3Ku5lGg zVY^)GD)T{wU(-B(I+(7T%m*Kcq$|rfnp=SkTmPq8o({w$Mi3vq^tVa^UGId(AU=KQ zZxfYs)k@JoY`yJo7YcIKEFnbfzV7efR?}l@93wlRPwM13c4h9#e)zRau`5RSraER> z^(-&+Rl?(XwN+ZRt1oa36FGyXqfRry)aW~vL zP$qx1Wuocc}ruJLY9ds;w&74s?j%_EMuD6m~V!w#$HFR9Xc)y z(hW97I7xq?y$~O|{h={oMrz}nQsT8r-J>Lq5vvu+ziWAFN^2e8L)5F%RxkiKL-&>x(SIlR>f1-e{ zb+ow^y4dtfq)zU}{;H%O6Ndd|bHKxcHPib{i=r+6^}M%RXV~)c@*S`KY?)*Gr=4vd z?+o_|n!3A~{b)m~3=hhEaCb_l`@~Rm*LAqB^RL3{aNM}&|!MLG}H4MQW?tUJXcC@SVcOwYd-aD zzMaIVp&YU&AoqQ4fZyoF3&jJAr{C3AKah32$vrSm_iH`M^LkzX@X+qU@BPTL+WZrL zy!&dB(}+jj1uU{};tua^_!M~;-}-g@^HtXt)Ac2m-1=KNe@zaJ$FwTMiGN_F&j#rjEpDAP`nFc8=Yl8a3&W5pZ zv!!v^!3#-y9px7|4|%i^Hrs&imy-ljp)mRFZg(0sj@eMuJeI4HqyJt28DWyr7n)!u zObn-Vs3AfjWf>kH^?;B2lgXf5h0XVD@uV9_4Ic!BAemO(CU5`xagrdzUSk0rV6$J9 z!!WGB? z!NNF=0D%I+ae>6|4X6hNpbmxcS_XK1v-GeCn`U$R>x`A#TaCd$#~z!2g#l^(23deJ zshNQqieVObo75V`$n713hXTBj#uU-JYRi#aHv@u+`|BYboZ#giV71~ST1qdV%U?Nlu zWF*OK0P~&;fE1mvz`QpYj~RF!qY3kbKC)M07f8g{8D^$zv{$0jEFjQ1`b0%p5W=H* z946{4PmzH)z-|x-zSq`Etk=1bg-%L9k4JF)_R6plFp~JOZFVgWs(vAupIJ3eZRW+8liIwFQy?b}-Ho-ry~?V1(~w5&baZ5M2(O z#4a*~MEocVM~R#L%{596>|(A${i$dAdAAkfL-(Lq8ZbO7O#wO@5=|++8g~yXY@K9; zNdH*Gvi$Nlkl3~zgG69?uLYziGJ=4i0Dt{o=2U``??9YS@~4Ft8pW0&KY2oH+*u-(03 zR2xAzY*%aXHsQE9(M1SbZy?9QaWFzR2G2wrDE=ce%I(1?`nMTSF?k(A`^YI|?=XP* zgGB`K?5ArQ)z~nxVW()D3}(diFj)Kp8vB2SAeY;e8B*M1a6_5t*c5Tu>>GgQyewc( zJRtoixd@&%G4f9ZG-5^;f%iGBz*IAG?9++iNJeJ56zy;UQ8fPDP-gOsqkb_rOt95z zFx8nkR5Yi@LfYTz<&@DNp%Ea)tpwE~&p(s;(8pSkv%U1hy98hLsGUf$myW(Ih_dbh z6z1R8USW@|5}JZSq+O|Y7P_IMjRZK&OgFey1G)NR0v+fSfHeSIu@9^G_S>10%}2^0 zq19HK4S8S@0GDtHmLManLvbL`gS)gE{#zQQ2&KZ>-vcFe?f^8qg+oVCp~MSJboAGp zeO52vL{Tpp%Xqc{%vW{vi7B=jaG2#li-Mn2SN$s82|a}IZt z?5KJ?`kWYioJfkB=dCR39TbHS4@-+k8xT8k4>oHCQ!g!md4KH^KiV)y3=IznN;83+ z+^mp04PaQHHmMV1Ao(JeYWRRAk+d$R?L|Y$^417((DFBU8G#Q=v52g52;KN<8_$ML ztWQNS$&o-GZZ9wf^uv;d*1nI3voY2m0|L04M+o^dMN?MXd14^|6l#DI`jhwJY+;Yu zpo8!|FmgBCVJhrp$8mNwRwR7GD;3+Nr~JnCILHD8sZmDK#p6DPuN9KlU)&j^v*($~ zc=5nr`Y<5%VGttMNC8h;Cq~;IMfG-Ak@wwd@EuGMMH?EQ_Xt!4Aarq)CRin^1vq44 zpt;)6+-~VA%pEQ?{dn18fS-q1ghsE)4`t z0=Vn~vc_nfIm7!V7*H>aa2ica8%SzWEOO}h*mUhrG`gv-sZ8UY2ZftWBLY+eEh z!~PL1X#;Mof}GGGBnot+Thj*uIwGAp`4qE92@pV7H_Zh|C9e$$#m>q_fQQ*%GgUVw=M2s#p8!B9ZVAjWdISaKtBbps7!h-U7RCp_Bv=9fE5p$B46-dw zv?U5H<&JWKp`B0y#8Cooz9!gqL%|zhS8c*^7;#BADEK5%+zpit7Qq%x>b^+O3PJ16 z#Pb#ljt;ZQ9eL0eC{vXIMKdF~u7hB1cmO181VA8EJkU>T2E>GxBUeM&2Fi?`crao> zRG#u8zmIN(KljjT_HlmDA zf=D;g#Jh7Wc~bO+$h_5q-fn>f0;xNa!MBd&t9zvRg}Lum;@mc%)F_mpI+5v6NKaHt z59xt@@#KHDL$zcjXcd@dca>bl5?aL~9w?W-AkxKx~A-WdK=^ufQkK`;@#;J$gTD^jOYKLv?h%&6@EcE8ufU^&b$-A%e z@{)3=GxHOGba_S3+%Cz7@d}jO3jl$!M56^I@&&CI`FI56Svu({9(kkq0QDDn!k%*b zm4&L{A{~W%iQ__OV&SC(*J7n4fk{@_Cs~FyDuS$B0!g^QE?g)pRvJH7#Dt657w4@S zUpQI5bus({e)eZ%$=p|%;x1gpmrsqRNWQ9w=UdvW3g>(~{Fx5rtzBH|=XsDuC##c^)8zx*;EO}fe zs3;rdNo*?o4ffbAY*&NT5s?4DFJg7%GHm$`_Goe}^7pi9k*o$cR%em@wT|i!w*Kya zfvx|?(V5?G0YKU0v0WvF*jCWLc9raE@yRzi&N;sPqmJrNwjO_XVN|sCUsPgj#GciR z(_)O`w_5;zMrUFrOt@wLR!2p5i$~|RF&9=PZvQP}KPB3eBk^Y|XxuS@({!ov-?f5f zqKtTKuR8ztwu1U3{vNSO(xk!O0zfiL^rz_G!N=YLkdG`$QsYm<-U6Vk=(lkR!`=e; z6{iLm&kKNZ+j~9tmB2>qW$iL`V%h)Ci2Z*i zI`dmAD9?UFKP8r}A7=lf74)C#D)}p0pIt+F{wG`is6_Gy&)?YkACIUB{$%R~Bjkmd zblA55jQ#!prmMumo<|^OItM>zzsc7o1%wj4IX`Y9vdY;9(&U?M(qg@tVR3><-Yh`l zvNtp)5)ujuYLjq#7X-_BvX=-=v628oDKDXt3YrhA711-0+LELk`|OpeG>PU{y30n@ z<)K;kwP=o1o^muFD>fuvmrcC~O=#J!xEHsUXuK=S^-_r#GZyy^u3wyQ4){Kyv*9f3 zO26f2cwfSnfmpVEYkfjXe5XQezHO3!T-G1b9UFRc3{Ko?nfqS!J zx_yNA{I!QT&LqC39@6LSs~Iu9qLAW>PdSsfU`RQq-Xux-RHL!+Q)SigK-$~$n%@7; zz|f%H_uMz6*{CmvkT*)1o*Tb@l5W14`gJm6^y6)7?vAmAni!YaWmS21J}1Us+Q(d( zXf6@^%g#i;5sAaphS-rC2)PR^)Kgp_CleY7)sQRH)fcA#3&C9G?>{)VZj+&5FZCDM zFue=AB&U}gh|mHu>s^Poku!4-M5@UZIaKv4a^sXnY56mgSF;XqnK*D6JZ9Ez@*c=n zgvV%Fk5Z)y4VI+9x$Lk?Z*_&z9P1&+qf<3HKOvO@-pTlhzV)3HL-x|k1n+>2b$XzL z>N-vmx>$jjB&bOhH~40RXlx+~%cZJ%uUmL}C~JXmrd+;e6Gwh?$-01q`m8lP&OmXK z{2soNphsd{^?OU|9#tiimZ{hmtXBN)#u^XLlhRuiwQd|xp|*_QW)wJ$3KCzbNYE!` zMupn`ERjGt3YBIK=h%u?J7if9)Mxl=Zix~`sd`8vvcKNqpv!NcaB8}hvlPl9wz+fX z3E9#Aj7nUoZa6xf)^S=Y5Lc;br#_RlbXq3<=PiJ5r{yT(D(wjM*&>>=3RUbq-}KSh zGRd<_?YOFca0}og4;$CekZPmH*Wmc~8%9x{3NO;`)!J>Y-j~WW=F0IiG1Bian!W2 zx56KL=&RFQ`g7AkjYs2MT}x_ln2Te`4gz!iwk1Zi@jy?0(Xj1z`p+3##6;b-*d!$y3}oU3(v4C2U^vgzZ5mG_hr z;=~}WYv;_>Z;l6X;bqYBGQh@3C);f0JTyk3jnCyN$Q-zS{^nait<&x7)n8w!dVbls zh1v;Om}5IOh{KOU*t#r;48=KcJ{*QUA+y9;H|%49x`#{TSmvZG^zc0!iC89B`NZ7X zG3(+Qm+$m|?nI>R-2lE<*#7&SU*a7qE$(Q9ku^i{J$429Iuy;~doIJIAcJ@z%~|Em z9Ji~{dr(w*RL&;*%*L3>YuCBK@eQHFg==PeyOwPZ z^KoR%#-JuI*4Z9w{di!H;-HyYdGfe~LJOgMljim2fUx?vLE9T+$hUr?9`Ttuwww`5 zBWlWL$*=q!@6Klqmu`7h8{gRDo1}S{uRH=(iQA*?^c^X9CQ^GW?QBLxyWsWqNki0% zll5ogg^4YZ%1jv-D;24^7Ujr*d8x?@n)9jSQ!nB6pN8sYOYc+Dgxfj;79ZX9egE0! zqOfnp)tg~?Y0m3BCpFjE{`38%vB9U^XD*J;E}ZAxqr|@#qv1cNe0|yy-gt}V%OiLo%(7Pu!#^k=P_vd=I4BNeJetM~DYCGwdx^2GA=*+?3j)-u6 zis9KYC6%R93ZZZIxHLEWB!3z+P@iKqd^@r=Kd5Ehp}R$MCT}cLz8ABOe7qiSJmZo7 zrr%t7W_+Ug$!-6zyYG&$o40RPXTfxdvGf0_3m{m#@ z(NZ^`gt(KA5`JQFc~TOm#Uw92)Y|qS-fBLc_1pxylDA%)5Mv~n5S)&wG$A$N{N-SY1Z%{uz+?K8(f&_^v`UX{u{JGPiq?Q#Few$UFpA!sdBM0e zsMh)d)#yS&qj%BrI8uL)>p}1Y=6F`ODwS`_(EuA+zHwEH9Z%H8sjBVNxs%e zrzS5x(MmE1jQ3oMyB{1E&aIdLed&yVjN=kHO6k6OrFUM zaZX5?>r1}yEciok?4HS#pIRw78f@FXlzA_*)*B#;-_^5YheC4tjjwt+ zl7IKJU1QpLJ2Ud$+t|;3%WHDTJAuh_e~~xhkxxhG^EC4tuIG2X&+n1RpZ3lxlDq2E zbXC)vbNB}5ZXZYH!Bw^3+zoO$l4aQ|zNbqa&uu+lK=VvrJ;jlR73ee;4B8ZK@+$Pd zo~s#Jpx$4&I=cXgy~gz}2#hVw*(MHr=U6-Cv}bdMY{l;!cXK(#c%bDxnw>Nz&j>c! zF&>&!Q&;saxXNF)YVu{$f_(M#rfTN%s-^Eq-0`aQ;%>h-C2^#e-qtbD;`)WJ68KLyS5?wrooR|8cuv$V&$e+ zX*NEUyA$h7TR8g%Dt7!J?^GzX*ir9vaO2@uh3jA4+#7cD=#QHY<>fEFryujF_h`8p zfUCDxxOsBNE$^91pVhaFd~Ss$HXK~t;PT_L>r6%D06Ai&ArRN#mv}3Dpke!uMr2uS zOiQExjwVN+rl^Arwuw!L%A0~-HD16qvj&=u&oo_J-IT1*WYtnzl-PLYVDovC=E8$F zYi61+tTk!eVPe{0-ZaqC+#=DI(*hP)tQT)>G-+)wm*_Du>1#1>e$_IR(`sDPx)TJt zOe7x9v7yFG<#eQ>bt?T-~B+k$R8Y7 zvEv>QcTaL=K;5{9b+9N;vuNc*b>e=M$k`05qb?KAwh6k~*E z?Ob~{zTxRjb8R9oG|S*ceT8VsxTy8p!sEZVO*%(}v4%;c zx3~YeO*CyHmX?2YebD^vHnFC#V8mEX^;z!kT>$R?c8c+T zc_HAhtlL&?+LtHavq08H$1`t~i8XfSHn<}hTMm=$(%G;V3t5LfexK~e?V#RQh>`Zc zZu9COVn)PXyqq@teNj*uj@21SNc1#%)U#sX%xJCNxjIjq!>Dh5k&D@%cSO4QAC%2n zrDQ<{Pk#*Y<>Vx4Bd&q>ToFLs@K({t^u zsr_7jfa(0goe27#q^+i=)>bzNd%?-}Vlr6=ebyGtt>u-hXR;1!Q_OgVG2ea$tQ-$f z3B8imvFyjxQYw0dj$P6DS=}|pOj{M1RoE#(jEt4~EWjDdnu`zeDUy-20F2B(~ zx7Fgvuk*9VwykugYi>cfiNhGifEOyr1abZy$DsTAz6aV4zP8d?wcW+10=4$P&Z*(Z zTBdkST+9i<%5Q*Q-ca&D+u!${{CL9lO?eNJeGxQ4vSW|wuIkeOzd!|@b$LrOo1M^l zK~>^zUzu)gD!U@D=z#29TjF@?m3wORBb_$8jxSuY7gy7=3c2*JNXt%`B%lN#E!ls3 zt4uL+l$ZoaFN_frj{~a@?KYy?q*2Ut_t^FERlAZ9CM(2OBHXRj)wX6M7~qp`DhpJ)O_ zF6AK8`j)PE)IQ{z+qMr14>o`&R~~A);`X$HGPfORlkwqoo1Nj;C+F!iJ^%W0w*mlS z<$n+fi#umo2fC%HiT?|M^Y4Pg$x^G2C>o5F{6!D@PvEfk!`1X_+x8cB-T2q}YCo<0 zI!|i;ZYY30Oz+zS96Ym2 z|5g+wGk$lk0H@Xb|0_|{|CGS_Pm7}d%aQQ^n-A0fbE2rFci(O^`SR?oGv?z@4a5<^ zIxpp}_c^P(5fZD4I7qh9Ks2>)MaKUvzOYz7{iSHJ(%>r#C?|FsEZi#i*ZpZ;y^diCE71&?%M z$l!hGaNu)q&{p8<`}VEAKW0>ZrLCa-Adt0@x}4!2Lj_cSPo#CEV7FSZ;mCMTRAUEc z@A~JG#EGP+DwwN}g^}b6Yl==B2I`Q(97#M2N&XPv8mS-n*cx|lO3%n7*DT1OICn5n2iD&-hpAR z@R}bZ$6wG|bQ%| zD2kDvV(e0RmndKEO`N$W5W(HET?tV7v1MAE6nrmQN?A)CI0dlQyFZ}(coZpJ$D@ld zX6S8R;LkdCoB{O|>eee(T8VQKk?xqjf#{ia*#uGBkQ@3Uhb-LJ}MlkA(w z$RVU_#jiD>qb8$v;Z|t@RCZ0IqB{?6 zw9gE;kqwUSaZ`B81*Z2|4J)!3C}$%WaD)m1r3|^Q;x5^D1Ps$scRM|9V<`=;rv<~A@+0wH9C-08fO(vXk!ci& zV7iX*6?U{JNwO?v4gKb}#erNqM;OM3YN;z^orgzUk&&aCODD9TkYaaaB>j(>M6;u1 z*qsv1OWp9-JS^aDD3Rc3sX+r+Xnv=hoV7V|5=h*cE+FJ-nPX=Gz#?XwWWl*RViP>9 zLxZYBI1{NJbUQJ0EK0)I8dhy1j+FC)NmW>zV^%(h-&wH@XH4nd98+^%qLn5IaLsZ3 z5ERnrM#_VxhZ*9D&;%)x$+pzE6?vY_SSg)pZKkq$_so8QDk7L2s~O5YX1X`Nyp|QW zWt0=;M>#6<+|lyb)^Z%84jP;fhAC|3;2B;VyiB*3#C8VWAAqa0h{$G!F)+e0mOyE` z)Y8~15G2iRlb80|rp7P8pIYvO&k18d0KmJlj>=mNA(h$!6aD#}=tbW4HKXE~O^(-; zJ87|4E!SiZCR|Q4bGu<-BvEg2RJm>`&cGH(4Dnqh?Uxy;H64g2v{EEz**%-{QZX)w zRkD3_OU1Styzdws$Yq2YZ8XE3){R!WM@K1**5Csrk0L!e7dvj8v^6TFDE9Vk!x<$q z`2C>bb257M?CbfI=wyt}B-|v54xAW^k{Z#1ubhoc2!hsdGrf8h3^XyJC2 znqu@Al$U$nqj9kS>%8P9(eD^X^%PtP<*6#?l6j(aCn5oR)-^z%4l_Kpo3&xAKyuzX z5^Ri2bYz*s5?Jxl%G@M73TFVz#&vLy@_AU0LCB=Q2RQhCI-!_{yGIAYnWS(Ev5611 zl8JS6T!8=|MkPGr;*nJJ7ia*LhnT7hd0a!p%bE2{@Rbz0MO$s%se&d6%E(K!j-V_=`6xq8rFdU zj*xN692Xrer63ORB;bk_f>R9qGCj1F1L)C?abtj9E)L5>#??6J@Wb%kr)t>*Bpar~ z1m|fuBoi|yKp`o(m5neTFTkG?tIWd>Gw~P!zKD*iqhWgiLM;_g7J^I$F^mJdM>|(R zBVs9jZ-hABB)p75(iQ?wIJg=Pe(6|(GM`XD1)HLQW)2QRLj&c8g(!WdE5N{i%p~=3zzGhamxI@#5$7oQCr0pc8u1ey7swKuW8y~`m$^?! zc`V=&3oGu8<#W*sEc_rH=Sd-jb6}4II4l=E%f&wtqJHmJ3kf|C(!?hW0VHc6GDeG3 z?LjQ0gG5X0ET5Q10l7S48ylCv12a!M>Cw=mJX|-A6h_7_(DBcOtX2+okcJVk#1=UC z2^L_BFP{`vf0~$3Ie!?5R~VD#(Z=+ z2j9X3tM_{c7K465P=N!>_yl7;1=mp0b)o%Z_MrozjuFzJ61ha)8)?J{H~}&E7BJjUoJte@=Q|}!Vno`2^b)`+r0 z61e@?#Ey;pV(FsogZgG^V$QFBb^5U3K6`B=^5&P~w@-@0ZdKitt5@43t}(vj@T*;L z@lE_EJ3OK*`kbZX=C`_R+o2-02M23Xu_S+5<(OZ9*gFE>L_-XnYBDt2IyB}8TP;^^ zW@!E+OR}IodYg=Nq_`8TMbg#qA`}}Vfs`Fh(S`E13|Yq!xuzY44T*M_cAG=v{rxSi z?P0P`FnQ08R^O8?2vw;ic+=w(4%^6%>1$s;Hs(yrwIa~I{FT6b7WXISUHm7V1~I(0Xz zui0G3{n56zLLGgz+3>9T-RE}(Tkh=o7j$bopa+=*W&cU&+z~A%p>ycp7dod<JUShmQOaI`6o6@xX7P^XT=M70Vd$-?OMj%Hbche+!+rIY^6JNXn`nuiOwg z5~paokK&2UU9wn*U(ezfU6$009rjpX^*ijq<^m- zYup%1v)c%vTlDSP3q@(EZgmu*7t(mAFrO zP5np7uKfea{j)a*^Ip!DJ!*6QQ1MJ7Y~L-rz?Sp|+sK^sM(flTy}Ij#o`|rZ6VhuWU@@G#Bton~MQ~#+0-xq5gpLZXBR`2&p{t@EL z>rWj&gI=8}&ozt=dn7vc=(U#&BDg0=BDrBCbk$(P=(%-A^4}3^!y8}6LR^#}O*8M? zXpZ6Vw|DtgyCf&BIUM;ucKX`6uLA$Yu1#;&+$J6$sr;nYGE;YF_~D0J?@fP<)VDuw zp7z}9wLIUW61%)$p*6HTKe*N2_4C8MqSi0>j<&ac?VC{x|8&Kr%6P0J{>ZPXyi3(B zc3q;pwr?}NkIzldUE2~dceQ-$$0gxheuQXzROLJE%%vZxeqz5(g3-l1lmRyqbC!bm z_9YFoo#`lD#Mxm`+Hr+Rk0QTzK;6-76A|dAfZ10sKg-Z0np0TX?y9@+Mz=nNmpf5) z4`V*mw(sr^h})`5A&PdFnge6ZAd@=Rork<`K@#5cOXc}LN{hQ%ckjN?7+qM4ur}69 zWA0Ju-zWPDacyT(&E8|ng}%C{w10J-Pjb6xSNyuP=S2+NftdbCDde)1=^AcwtsI+% zzyRBnW7!G&qHA=^HP&jHcgs3UaTk0Arn8<<-d^WuD)b}w~;Z|Eu<)P>q`q)c} z=Ki9KN7Z*8vAX%JI&QyDxYSAl^59d~TS?nIK0o##n{4lHtF6I}w3LWdM{L;1ShudhE$vp_;njKuy9=F{jCY@lHBUXdZq=*%qU4fWK9@f{WBN!A zdmd=mF!I4`!@Adxh(YVRW1UaDka520_JDi-rjK{gj%C>#M}m+!0?kZEWBES}Io2 z7mzxbvAOPP#kIy6Pm>=n_{q2GuJ8PC{NRt5&j&YJ8CUuR|2p&d%`Z>sGzvRltKsWu zMRLP~`-3oVRsJpaGw08;W15#2hsVAS){ci$LJtZ}eoPp$CjI6@dz}Rn`#Rk>Toj(x zd3{^jf5{kg&i{P;qqpWp#ogUDfk^?1SCzId^>(}%UPxXZ(-PS?^*anMTxnSzr=~RB zJGSdn_N(OyqoJn3GlQS0E{R$*rl`Rl_-FTeDLgy9|!hC zNctrtDK?_@7${qgrY09A2SkA8J1A86PCFcrY3)lr@W@O6Ta{9bb7`sfX%$n|WPK0CT+?`a2W3?MuMt-W|ML!7)KSIDZI za3H%fP@ZIzhZo9E2)idFE%Qmt1oRdL%87&2WMKxFcqSEY$5VEuql_uIAS!4k^au8% z5*$(bTy!57A0py|W^DM}#Hy-ZM6xu(v=ed3F`?=`+|vu?!$D@tBHqh{q@jUu>+=2+u~84l#L)1U?psqjGKP| z)1}89_$`is`H98<;*&yH2u~`?gAWEVu#04jCgXgDW5PNPY!!=`$p+QgFij3ppAV`} zdIeIjU&vTxGQxu%rO!dOXkMTIfDQnvQ$Y$qSLebE*vKu+FeNJ7SqKzT@K9*5Tnft5 zK|AI-d3v%L9n|N-)B(0S8HzS=Tq?VlC8o_rc>tg?AJpc7N^F?Chz--`p|-GqJQ`7# zfl}qej974O257_s)fgAouwiQi7w0-q#R6D<}Zaq@lBDm`Wi^os3WbhoR1BmWn=-kbRnq2M%M^1>Hso}hAszyRaA5*g;c>u zD^Sr@6s!m9ycr!^#7F0`F*+Dr4NFXki>csXpEIB{DQuMhWyHo5@G)5e%oYZiMZtyf zK%Cg+_ks8(GBKQso?+o9L~L9y@A6M}(kTvXH5=5X#CWrbp5MYl7r;{hu^#}w(a#4+kAu)lA`M?2p3i3Zt@N*peC>Q!yAT-m6!5r)l zHtrFhpiRLpP>8K$%qlu=f`Ok9V1oF>etw|_Kzd8Z&GRt<&>FiqR+EZu;gS;gpgspb zA|Nc#a0LuJkAm;zq6_K7PBQ5&8`jIicW?;~EMgy@2t^)O1=tS^{0tAH&CY#kqufV@ zL(kJMDR?9o3bKe3Q>0}s<^h-RibsH+bp1HQ1P*JUGeus2yGI31u}MKfLY;^XzOlh9 z2~c7ze7^vuBgm)F(4WW{BpWv@fW6~m!i0pAWMTl77{bEWQi(YLa)gdA;1QMh@L4_@ zI+?4X0_&jQgoOs!Xh`waO$ScWNeL7pmjd)~@M3(lfDKy(Bv8o2bSfA`Cu#~&!yKSM zi0|j(@6j-jtqcj^1vJbj7RCo4)eFm?a50T!ypWB7vv4Ckkj`-TeyIp0J=$a<)Dh`& z@k{Ka_b*7PTudRC>(4~`3yG;5(1Wqrn*-~o;p&WtnSA0aD&Ykam%|{^1+WS-QIiIr zq+u1(eEf&Kmc3G#w=3NNGK&`!$`DB6ytXk3a8*k05DZZ z7$##q7-er6=zA30I2(m!f~q|1OAhQ5>v!UD@L@v~Vh4-RdqHdr2h@rvxOoaj2M^Fxa7((fX6k}`t+?1;EgOBx{wGw6h zJ|SJd1k0n4RN1h;7jS7Zs6qyZtskbo$5iqN@ffxp8{Byx;UP>`=AyQc!P}YO0WJy> zbxD)cUD@E{1jMaputQ?N4sYc8Db^v!IBPyk{|zjU*B(YE)C*xSJ-(6fNyZM)Ef+NR zL#hFQ28FN9FUe3iqo}8xUy5{HLdo>r7IA5rxlwIh*G%m<vn z^r>(UHb`PXc_|q5Gg$0Fut(+@BMLuxG1!$1h7iu=d&N1C>gk~K{EYUhTZtA=zvz6c=c zr3ndd`s~po8!O+fGG-&3sEi*3z)eYTl!|JLr$sxXaD##;Ze!YJM>)@94{%4>R^$w^ zkoudtC7E#9Uh&G)+XSe?VLgWi)Hj!ok4rUJc>LOyQnKRcF11@oajod6jO*U7x5VUM zheyYsGb!+v%#8KgcW@$i1?xzt+{Ve18zuuprV z{hFDE!I`Gv745&YJvGVIgOZ-Tu4~uMs6Co#Jm)(9WiCN5tC+rQ=^7aP?UDXCvi>+f<>klE&R^WF!$Bri0=_&Rx%df zea`3>khXoFHoGz8=Cbvpb-;G<#rL$g2W#wqgueZFGvG&yzFTDb!ufzDqoIlTly6A^ z-%{q5dIOfPZgtDA{@(cTd-uwprCYxjx_o~Y@!fKMxk?oAqt)eyP5uwv`S16S?6^(( z@!sW^$@X7X0lx$(zvilc$w-SnMC=$H8ePg4eQy^nzZC&6nt&FCBq&>C(nq%aeHQhC zl-`l9%-!#xS=24(5ORCQtkcwY=2>Lz8MnQncepJ^ZtsL$_6EP zH%TwFOj5SY-Z!OU)FQ!x$5-E zb1-*H1WL*Iv)9Aoo%!2yoxdD^T6wrV?)T8@%X*RD+n(Hmi~hs6!$m0NLrcuj?j#kf zyhGnkOx(}i8n1l#`>E+Cl`g$`hkpcpd~-Vjt#V{Jcwzj0zE%E_pP^qrylIbDart%j z`@C(>N;$aKSjxD>tlchmtf*$YLO|DAE2T4IHC8GS2m@=?c(q#VRVfw* zHtHGfwKkghF$T8U*NbXxS66o#?9gi%tKFgBj#y{6wpZ<@-MS%*bvrjabHBOM@NLYx zUB>T=ZtgOf>sn{OWpV7L{Z3kgUPm`4S*{oDwv_Rx+ij&1yMB+2W^vt~9s1qt z_wFAtd}h4f!6gE@ z(a|+t{g&g=6w8fH9vL3DoQ~zkZglp(UVO{hr@DLNLBEFaTL%N$k%or?d(|5bogA_> zJbe0@N5kPWZ(|LQguE|qI1)D3ZRirdINsnAA%YsDNVG;HBbv0`$Te2Rv(YtPCCQTTT#aDsa6_I?w9Ph8+%-K@NDwPaEUYa%sN)m{l5oI{q zpR2IA`b~@B@bsI3hQHp7x9&|Cxsz~bJwLzW#EAiihV6je||oE<6BP1gft z@DK6Kj{c)%ckViN_0$S83y8Kp?p>TR==2`)u}x3qW*r%Mf4%6+ah=Nr$)VE_Z4LR@ zxIH2rAAhexZ9a~E5Aw0KeuZf3{{uJU{{RG6tLLVaO-u34VwY7r!u`KXb+UCfb63Dj zX6L$^UtnnkhY$2!?v_0`;t{WSbh+k$s?4U^{puMZ2Hm=EHJqb0d}ebF=xd7Kq^%2Z z{B$1}!#^3+8Bn~spO&M2YQXx{)A<4yz1)~?l}&^V4trL;-2cSp*axTn?e@1NpT(K_ z)yFZ8&7R)v5vKd~fcLt}FRyaWPkeSbkytSwfBMtw{ljLDL`q$+ndmmBGF$8KQX_HE z2}?ys^v(sBq#CSQ8jEuMaCv<9tMFU8d@KFGFN^-Wy2ZZ% zg1!2p<lKs*OPTk}A9A?tVXAOmT%Hwh!j7dZ$`>kh25@c+uP zC4AAf<4AWN`D=_(n$k>*%g3FpHQmF zJucE&7576jP-^WBH?3VayLYHnzGA$#c-STZtiHB(g-gY|Rke-1wqtAAmowj~YVO`+ zy}dT|-xd#YK>Ul9ce;2UYrQ(IY&CPtXwtpD_SlHOp2r7i@#X>QRVPuiVPvJ$%hnwm z=N9f+9oD{@uQj+aKSVpK_+-}dMb9f`PKiN5>y#r`9@~`jJU?dmr1i~<-CdWP23!VD z+N-HgtaZsA$U1P`Hs_Uvm4rOs$?FqmXXRvOTK0J->-rpzpYIFRBA=d#{hS*}*lQaQ zc<<=1g+1vUq#orN&Kq8O2lvp`!v>!68!{`75U0>wBXl!!a6ZgUx2o==+s_)*X6(_|CetyJBN5{9Zj>MAOB9X%FA4pfJ;zD|xI4P>o{ zBdIZp0%9J)CW`dyMO2hz8^xN{{6rk|51qL1p{p4rb|uT&Y?#<&9k5NVj`?+p(X=)j zZ4yMUid~;>_821`HHj|#vh%d^6l%VSyxH5&s5qR^sA7EqM%B)KW;1!jBU;YcFsSd7 zE#6dU@V@e7CJ5ufNkB)GSX8D4gy%Z5evV^ePN`m`z$x^fA{;r^i;ovlF8f!uu*1$XwmgZGcQzhhMy06mp31*pb+W+ z0LpNh`QYa+M9y{O3yrfcHXie3gJmp?wlKnj6`A8;@63yGYY1A=r~^eHYAnQ2C{Sj@ z6zC`g$J10t6vR>WGllG#>f1od8Cm z*{_6RO?vP;DDJ2Kj_iG*$X*D8N)lZ@T$K#&Tmt%e7%YFCGX?q|3e3?20eY~pApVXc zio}OtHcSrjTyJFgd7uVIiQ*M~sKB<-mM?Hxt2Y$Pmd0Y6H;< zY*@~j_;Ld@SAba!!Ey|ADH~(J-nI34Pp4Xw>bWpUBD9Bcvu8^ni0B|!a3Li z)`fB^sh@_uA%-mBVFRfM8W#hlV(JWZCizcWz{0$P@>4o$6&v~zqen;S)6f7Jlf%M5 z18bg5SWf|1!NqiwadK>Q6$7gap!IDqMNrrd0O~hDnlSjQwazpEOtJ8)ETVvp6Oyr- zbkrttgtHLTg3`_MKpLGn>fM5g)zZQAz_vavuB_u=mgFbrk0NHfN)g`svn{w z*`zW6{g#7!#lodhN$qq}50Bu@C%hy>uLITpHRH-Q^a2etNydT9{K-V5PBO z8xsmY@##WB0Ef`PBlwXqODwVH0vt?$ea(mYbBM-Jsl>&-76NK4u`np=rvvqTLOO-y zBZSRDVLlhvOb5*PD0wdCH4|J(!3~gs1~yJih`YzZ4?$QhjX14EDie|Muei|C9;r-- z52O)&ScDE1R4Txx$+)Y1Cs#OYYw!r=!i(}kTrVvptur&6hApCFb7*i)9u~^Fy*Wth z61X3Q7$yWunS=sz?o%!yg9#NTpt6uqD+KcRL=OS1kBNucvQ8de9;z<5gmf;plZ|=G zKtE;R=LC4DBsj$(lnDVYG@I8%Jjp>SPzX(2SRfmg#Uu8~V8$ufw+zsfRf%EZp<(&4 z>sSF)UGXt*x#$-ZoGSNs)L+OFtE3ZFv*1s-*g=2<;rD$U>?1z<4Hfz>1fCF7Bjfv~ zSOwGCVh}q^!;G*oBq6R(#DQIJh|q8ZH9GU~v1H@-cu!@1t5LQ0r;vUigc@|uQODN|6H7tTW z#LIG!I&`oJAj;D~U-s{w07=8WVPgaga2=}>O3Qg1*lrGz#KpW20*8S7r)-=$v|%WS zU(IBH5G2jeupKNs5(+~3m?vEHszd@0AgZuoqjVh9GV6tD&oc3&9J~rt4RC-?0seD> z#_aBdqt@7$3{nG2Y^An*M<4VC#7zr{3IhB%fPczI@mQozDshMd7C?aTHz6*Q0Uv>o zZZgpiKm>3}JUURz!DsOa!f&Wi2B8-!eE?z=2dEW7wG8|X3wqCE>}bScD&dU)Y~vAo z__;ZBq8e012{2F<)yakVQQ^ZENUk!Nc?P}>%G-rr2l(J49=?uCssYeK9v-?fFW?ix zSwtp_m;ex;a|k0;lt0?_02_UN3KK?!_Z>w80t}P_=K*MSmRKgg)wM73vpW_VcC2Hg z*D%~#9)q?vm|Q-nP5Yg_%W?4?0!#%9VMM{!39yJwQI$NbUN9)fCFC(1(#cTZk8)i` zRRB1sgv;fkp$Z@i3jak=TS~|Ja=XhZxN!FG+`pcNQK6yp_@EKLYmG47n%9)Y?B;U8 z8Zy3&j$2@fsb54?&4Qi+Y%v>?&H#gHxEd<@Bpc%=z_{`;zEpH2fU7Y=iBH5gvG6@C z%=3)RiE;=%DtsL!i*D4tUsHZ1_xGIS8W#O0!tFx>Tzfm%=#7Vf-!1H9IewBZ7j@YP zYRFM#Y#d)0A(jKzAtN+-F%&jTmxodlfW9n*HY-LCNM4SCs|jJT$B<*QpbsC^U?351 zes_<$bT^qTi8=lFM_e>iD2MUySKaHOpi;EC_Oq|yY8?L6S8%8chh0VTM2n0}A+DB! zt6}1T7+qCIQNEP3&ODUab;O?1)2AKdbI^}pZafv}hH~Je{P-RYT$C?6xt5Mqqo9oV zPaN2OT_quYY?LijsyeRLjS>_~k#w?;XrI;n^~YcyNFx^FXoD{YV>d-@8h-GR9Vu4sCkSi>3t6A^Gm|-*MzC~`%%{&yIPICMRx5vGXSBYo}8E zCokpMu&bt$2B+@DylXn35m7ZP;t$`^)Uq3tx-l$M?oM@^*9u-E;gM%YUpw>KQ|gxT zj7hBlR15DZ0zm69nEc$;*UbZ9)wm6O57SA z%T51aS@WSxd182%q+r+Vo2s$Sb01%uTE9CdaaLKUe{lGI@<*Qq+uI*^9-aS~RjWFF zZf;unqsaf`L*>~q;)j_$>Z82(D3TDa?-%|{Yuu% zol=Gk8G)0RkIZKrT~Mjs_~Xsm{v*>-YqThHCpL-1KZ%m|4@e%Ab(=D9k=W$&$?U1%*B-K0KW|PQY+t7B{uzGm=V4L7&&;hqjYfXnCH?w1X!KP7=W~(7 z=+MtYHNPg`{+ww4IX5Jwo&-<(?6tZ4uz{U%n#bLY4dOkAUOb!_j5M-YKwx?A?{PNAPwP$_+}i_qBH|v% zZkYOAJe-r1+dr}Yl2g6gnEdbJ!R3+|V#mfpr@CWLwz=ad9}dO`PE{psTKs6<5;ik& zdS-F%K*#Um0l&6g0A`RKMM6#qBOR}Y*h)83n&{Qf*X$CXMHkRx= zAykvdn#R6lOR1s7knChBOPf1=_jB*L_nzN9=lA$M?(Z+~Fw1A&+w1vMzDQ&ue&{X` zhNlYqTDlJ;$FxF^LU+i2Fs@qCPKlael?Q&b!S)Y|-A}5m*j7IGm6FNr7@2!pt z-G;92m7+^09+Zkkt9R$cxg~aYWrQb2$`$%oK9-HGuzsrK`Of-<^4U^MQS$N4r!nuow8;d%X#i?8$ zo1JrV?DmEGk2->EqMuF$U8L%LEWh~J8+_>E|KELV&0=CFq)w-ypUP}o?M{BttZMQs zeAfLyuBnK=rAaBjfd_4Gcn&U{bb#8WwqLzS67VsR7w3+E4uy|@^*<#p*c}-uqx%{c zb*c?-xi8|_nj}BwTbr!)7kBYOyAj}8yP`Uux3S@NR>W&+Xg z^xm~vyGrFmAL7-RPphT1S$kD4#0UB~p|re2^zSf#h;xGeY@=?1UQ~kI{U8dZOyW#3+)@9=T~0qunYbL36l4>T@4jSI;@i)BM)1+ zsWiN7@Y-3K>O14HC)jH6?1B9`GAf>rt_&^x+`s?fF|-fgUWeAfr)p1y(0;!P-aT7h zS8ck7B9BQqR^w8YJ5=uQN549&KD1WdN0Zu>E#@#|29J4_?R#yjw(Wv(YQuc*gX`IQ zKTeyyZ5Rf9@#{7}w>AXJRXgWLww!y}&|6a!^7spVXSlt=l2#SZ+9PjM0Zz>INY>YT z<{_Ah(HeOX7TPvCOl>feKO1QJe4;B2WS%hc{^f741(>|V`Vim^ZlKL_ZWBVeW>J&5jb-~ znEtAA>+3~>b5AV7>R%Te(Y(KQVa}$oez$`I zsNVT=Vx{bg*>9AvUmRz#J1spDDiu2xX1D9>M~0fdL_hM~^;`GBD7*MX&8fykp?$&c zU%_9WE{DIC5Va{gsp(>UqHXC~!@H7o+p@;7?7cVl*o*4jPw(Yi&W-1v ze3ye18^=K#TCJJL6uX6IV8cO~6wdDA%Ih*G}Z)!e%dlYeN!To6P=-ZF$TOVSlK=4N?SQw3ANflib~@nj{Kga2%T=8!eqY9x zhMaYdo*6p-`}Kn{#id8cpCjjWC-2<*?OeD0^F+kxtCW=YJl~!-ym~&I@ch0!t4Ny# zdN1BfD&4mUrjl5_01JMT07SBST?qYT0*@O3)|hQzrdJL$ckYee@s%ZD)GT3>c(X`yvuIKCuBv9Swr26y&ATU?CB8QAfwtfUTO_4h-cpODlBETV3`~bAj-PE& zwN_mWZZQKtSE$HStZLykV=3$5l_w2&R$KO+&U;DWLnRaTS4i~+;6O%(&k)GggQxQ} zdv&!NTg(buc`vkRG`8Yf;tt0YTCKI>E1wX2q+-d0gVJr#vL+*+0yWpRrC)<*58t3<3%w}V5$@&I7x({5kXezvOJ zp{?EVb^E!=cBik$$Sbwy1#?w*bZka7xrFCx8FkECb+}hZEL`pQI@03hCgD|Nyq4Z^ z(IUs=b;tDM4nM&?BBn>k;vJGN!7kFBA9?@HM*kon36cHZZS*xHEE3x-(&G0VmNhMS zT-2TP??ef0=D6oDF$1|r?rwIOR+YZwZuI|7fjF$0GS<9dQ`Bl#b1#>oiaCV{!rk#$e&x4C2qMzr^=oB zMvF${?a%%tO6dFeQEOXMUqj88F_ulv`hA(dP~i9MQ7x`ropu?hq**?zpMTr%;G(CZ zQP=txjwX2fnnaVH6`oLBpf%9+tT}h`!;9NG)klW6Hk(@q2F51szs`Xq2mOBuZN3YD z%SI;Lf-3%r5*o{ohYQ;NS8w#AaDr~mb=fH{mqk-jZ97V7-aLMqQ{F40H;Uc7b*nwn zt)A7LPg9>t*?s!DV(PSQu<@{5_CAjU+SwaNWaRUEcHgndw^RrcDmZBCy` zg{OL-%2Xfrz9%12mR#DHa3}fx@qA=iWZBN)IiE*U+k#4#Pac-#V*#}@;twsy%anjd z@bo;n;fSVk25VQQ(&O&Y3kugAhwC${G_A~&s{7Aw&fC6|daH7J?1+%x)5(;JOLa5F z9cVUsAE!qg=DF z`aCwM6)kVV=tSC#~mRfrJ#I$}v7aX7TRRekc=5(jRSpK@g zDdMBNOzE7I3&jS6jbJCm5PT zd65a5MwnwOdrvJm|3Uu9XqHAF5epTG0RFkr@9H>MzYC_!vi3{j0l-TI% zJo0@do`NpIBTTytcC9iZEsCUsySt2aR}&a9MbdJn-DYQ36Vsr5pVUPk3zDs!Ef@v7 z012)Uu$ly&k2&6=20vZQAN=rV>V`|5qG>ZdWqX&ICxh~W3VWOi`XQs{-reTBYb~wo zPn58`*F$$LeK4l@K#6JJg|q*WDB*v`6&l1qlmF@p&3{##@E2F;{Zm&6IH$x_Sqoym znP(vl&)pL$lE4B08Jq=`M5t#gT+CAow_t&HC;|jo`O5;QyYTn*g)UrhJc9*Z?-fV` zDErecC;2v>EUV6k0hJj7{s10I`r`^k_FFQ)JrMGSIciJ}@l><4tir8yKl>LBL!;{|dlurnpHDc1F0a>21@t@ zOZ&&s?iD+BMX)Q9aQaEp9I-@J6+{+S9-9gu|h>w>Lpg_tT!?T;8>_pG|%< zOO6%wF#rBfhA#e|s4GYo5f{!0xnMugv_l z;B~TL^lkt8(}rJ*3^aV)=)d7Q_2%RLUj+@c!SHWDUhVQy-1v^M zrR}2WBcUg?c2qILd-gep-x_F~(i+(65B|NTJ4Bs1zDGHLZ}&%)nyYj3;#W-`<@7(e z{dIm?FIcDZ<4f0`TT7YvS<1ihiXH`yL3aO7N}L_)>0Qz$ad?q1qkr*={*P2c4XO4! z!Wu3W|2$g!TZsc3Ebn^l+ngKB7@ayHADHdoE1CH$3Ys4|?Xd#yg(yoO6l z;8G2RkX)GIW7iF&ghlP25~l_X&-yze_9poL?1J0^l z{`=A5$G?_1|D{yJLd?s%R7d^GoKQze6Hb`ELVlZ&plzv<4Zp3IvkfU~d_3Z;qqYrw zmy_f~q(Dl*gthdATB&fgA-DFJ1N~`6Hkxk&T&)f9A|3IB-)Ze}=ATXWCLH6ERT9k< z>fB<8L&-+`#_H*>lWi<%Ry*yX3X^e%Bx&w(ws%Zr(rsezyC(azOuD5Xe`bF-m4F>_ zO|-X`DakQw8JWtJUevjpcl-k{kbQnvhG71Q^{B!^ig6HsVYES+pm8L9zjSUO_JTr= zmzm-P69a%aK{t}OnY%COly<>d7ua4NuW$)3Z7t7D@A*Se2vU4x`k-D3sExa@V4N&< zcfl%sbT+Rt_u!~P=|zc)i^dfhb~aD<$sSXR{27P3EIupyYkKa{3y!FJq;>wbPE zkhFU1a#YsV)XP!X@3)>HY=WDLGM!ZC8p=N&ntzVU`mw8VyR2aa{4_3k6_|eb?*37_ zx!6}{ljnQBy%DDUYJN0-{fGD1fN;pkPjBaMt|&_6hO9R48#}`KFm3zWYwh`&ZHtZb zIT=&mxkrm{&hlWl*{>t0BA*n0q9FS3m!JPq-nUs zGEB5(h9aZFu1>_{$!jZM8FxaSNbLlbw5tTIMF00ki~qS2r&rp=ti-hVL0-}^REvNq zxl}C~NG3SAh=9V}&WP6=MRS-aJ z)czY&$;HY^>6U>W{eJ3V-V+w-Z+eRl{?Va#m>51m?yzl{?UNTRGuFS7Vl!lKdC*z< z__ftj)^o4>59Vv++{r*5n&;h?e9i;S;;eewbakJU*EG*DN!F=+p<~X{#OODQ!G>E5(KhBwd71pXK-!S0c~GT9-B)|%|Fr0agekY{G<#E- z@vXw^Z#wk)Zx6_^<&NAbSN-U>hXH-%P8&UMo4$X0M8Q;$5a#TbT|1+nqK5?UCfeau zuFHe;kPUZ2dkd4PDIt;x;U35Gi`)(#%lt+3Ao($FswnQ<#A(KlLYG}y8ixYCuI2_P#G2f= z`}&ruXWF3;?Nd+W-?uNhRx2fk$QPw+CNTaSEslry%Phg7Z|=RwSbvz>sNW0&79R5j zFYgslkT(%w>Ldq;c*~4u%ZJpsz<~p`cxP|0-5rAh@*R^r$sMJ^t)^ozR~Uk~J9KBB zT0*|`8+26YeUqSM68=PYtCy(Su{$oLNU6r!PlIV);d$xnPoHG{Z%Ul1BDoW@W)9zn()z!4o$i?xzIayq$u8o~70%gs77srxMz6V?5VL#rS6af2zImq~-`}#ZPyeDr7yH5PvV2;Z+PCmO z&=UR+N*o8T>?!{Y@Y3viTqDOfyL$cO^e$Rv^)WI2hq%2tVv#k-huA%49X3x^9*^#l z*>l^XD|_^EUh>Zaa@&acF7wLOwyK-=_jNihTo70*Dt43%>|mPsS?qYebTe%ug4a$AMs~dCJj8i(e(S_%ImNK*?n6MhP#6v z5fA)u;H-+}DE#J)j!kpDrSx$@)%)i3TA%;J=WWkCTdFvc~M?J31RyQwM%d9opU+jBol{a`M-l!}fRfK+}57 zz^|p??H?Sz=i8f|Cx5SIZvUJ)`TYCaf#16%#J?U1+-C9Sc;~)q`>oNuop%w-|NC~^ z_9%?9TaqHKPib_g?4VL``IMr2xA%`z)Id~92L-E7#W+)GG%6~eifpEax=V9ctINla z=}#@paHjSxXQFC<%lkZxJAyakrk z5fN~iF(hg2oeifu4@*@U+28=qKW_m`HaLf?6!ovUc8ic9~q)CD39LqbTIB ze`bfNNbHby>}$K&HvzHiwAj(w*!S$%kDIX^ZHsP^aDFM`2}1a(#3)^Dn%#_rk+w6m z89FT=z8V=moNWErE@b$C%!YP22nqv4eE*iOt>Q~z+`p8(Ab=Opf}Z}Ll)QV*vP>Qqv`35l9psJD2jKw< zxa;4W>ispR(`khu&{|lml7F4*6`Ez64VO|pK6Tjq%T(`ah5bD*;J~35|D5VAjqJMq zPe2&zZzZpQlG8ujj(?l#{oU>OcaV4KUrOHU`TqhySowMGRPV>e@~0QMC%HMDADe6Q zV}Hx($p5h-c>Mu{m2yJ=Vnz6mPW9k2iCudSyfN7uqdircE2y_VQy52pZ#kj&za~v4 z7$doWusPmwE+9;u3kVy7IdcJF)?7f?8|QQxE+DL197nL@bx#Jg6{a$Lz=-xta?{9E zmjC*jHf~PGp6hl5Gs1-^aHLmm_|7{SI6(e#dqIq9Mrl67`1?q1C|q&&UYd)s_dQ#; zx!IC<-?Iv(+?-BYX=p~-T-hy!QTf6OZceA%vGlC&{Da!WdqyJ8jX}pAa&tO9G`Bn6)k*Z3IRfhsaq19}U^2Mj`ug@>maOg@)wg2I%-pN4EW}bm=r~t2) z@>DbSxW(rdw7Jhpq5`*y zD(Ub25!*fUV?l@|k+EwK)y?sUmny_DTfB4on6vaaWFlFtIDBW!o&36~F54v?lBpXg zuq-$A_nDc(cNf0T1?Bh8msWH-rOr3%9w}X@y>WVW%wD=#-~$Vi`(s&T|5D9|rfKPo z)m}dRpKCi6#^;1GFXlsc7!e*Fwu|*srGH~z+81JDdju;#`7HiX$Z2BGmuEYH8MW2# zU-u3-{m5{L_xSa54@~E~DZ^nKJ|i$YzVEgfJ`bEL#e)6G>7Zk}jCTGnb2^f=hxb}` zO1a%u^%?JWk#7|p;`+!N^RYMV40bOsM-b2jxa|m&HmO}JktP-e{P`aDWO~Y@0A@g})>~-qWV%X=TJ%D_l%URD?5FNW)XrWX5K7PSnKGHfnK_ca!me8x=)zxOH8Tly{CGk`-Ri@)l?R`SS`Y| z*X94Csb0^^xS+pI^^ThjCH{4)XXcq0de^Mtl~5>y8RK z+IqLJ&F7tRHXb&Pc9n_(%`?0W6jo9rqg zym~_O(dYWze=T{}nUN;>HC=N?2qBuALPpV+lg z+jZjMmDCsSr*t>!24f%kmzaN;JG)WO?pxaN0v9d`QN=`5 z)L}=Xzsf;^z|*mV?o5`&IA7oEjILT~=~${$A63IFOw3*n-1z zY2?K@taNCzql#)Lv|IDsSZ{>0x z=-0F;Vr1~B@z*?|b6z4ZpaXzBHN#sk{a~)b&8RE;Pt5qZnCu&kK9#U`$|&3GumtU8 zuC3dBo9qk3v5}XL)~jNie|eoM*?JV0E^%7#z4w_2_FKyY%{bq~KF(v%;aHDrl?pyb zT{?HMBk1d8>CarfA(RHCtG&f}Et7=&^kxUz=2PmfM#F;gwmM@iYe`MFz~r@z8U^^i0Be5i@|e)iap>*ZmSCxss6&n8 z;Pabpe13wFp5h7;(*bS!Raev0^u`d|%?ItP54#upixC}eDK*~Hz8hTlu510EM#Hs` z&(}7xrpzAK)l-jLO@DRh*(WI6BJ!i z{H@Bh@tR}A?gg`#-`NVce2AaI_H1}ryx4O05A=Muq||wc5*D{sIGz3F2IA+~tx9;8 zUE2Asvum;2Xq5vhA?(0*m=HbG(EzWZJ1j;@S_6_qy;yH zH(f0fe)dI+ud(6WmyLRjv~^e6`YWd&J*ru{wQl$8S8CW%&O5%Q_4eDpPWe3gIq8#0 zN-iooS@5N6_SEyhH=YuZ$6u0-{m%dRFnwhEile4ZsAR+Zh3Q7N!!N;y8tuQ#skcY_ zoU*177Oa+mt-2)Dsd0_5ZKjt0ws3Igamv<)Sn`DS;Eh`kiUiPXO3xbQ`1q}HM92w! zs<3>B1(jOtAM(V*XBZx;DS7+3@%28BP|wcmE5~osC#iFCp$Eo8eIr5#o(5`q1uY2s zD3_l5cS_#0aI@NQ3wF5WW;juVX01&-ZAY^WpxLF-&eqZ#*)*q38cBrC13u&uxu@|A zeQib2%Zpz2gzmRVCyPW}(T?!9i?|*T5s(%UR2y-V9TBn_K@o}MiXFr4BIyB7s3$}ak8Ky+I)`gQl6O&C1nx)0$yp*@@ zjLCQ$GgvDcs!o2@-&_GJKlG{B8p^~SGotKPUtySS-gb=i;u-68IUnfD( zK4E8If=GJ8uDS&AkpzjY1iWaXlun|weWGk&qI`OyVqK#0NTTXiqMB%ux=zvo`=o<` zN!r`#NxF4OdLv0kwvq^<$p$*fM)t|ZfypN6$!2xQ79+`)TggP7=-N)f%4ymo|6M3k zfivF6uHLI=IC|143w_qoStNw?jWCD zc=^g1`zspb>5%61vm#JX&93d4^Y^`R&4y`KqM4OCnN{|gPXjY+(=+SqG8;!So3=7p zqFJs<5HJYZ5}0+^YJXQo**kYQy$;M~r)Q7WWxpTEexQGWZ3@-3 zRLhy1HTCC<+t`=@UTI>Q7m`t)!US^^O)9Zf zlgtkI3#-TA-D1kzliSIPs-7eFO5(XkbvAorwcX~pEk;Adeef+M99K89%lcuVnb34Y zl)l*AM&iqNp4EsCJ$`uE#TQ>vH#3aN4cFmiD$StO17t&3;i%euKGZce-U~NM$*aBEuh1I3^#)Z^ z-MZ6rGNrXkW`q5QwLZ+h+P}6?)pOLauk!V&Oh?T((pUQ`hm=pmxV7m=VUney{4Wmf z{g=amP8FhLz;tX)J7sx0e`D-+X2vJUNvZJpA*sDN-~KvPw-18`PKcfOt$jS=e6r55 zlp9N1(|Hl9-_3b7Y;^BC*c5!9t0-4ZkYRSJ{#dAb>vw1&Zt`L968P{$!|s(N4I z0OHM+!NXw6YVXwpe^3BNe~u3AJxN?1_Iz~f>+4HgO%NwyAXuLreW_{ra_&D|L0lBT z2CH?^M5tr!Wr;$L)P+o=96-ZHGCb39UvHJ`R$LpAvhtn!ajCnCp0do_me2ByY(=fObR%K>Us^{;ZK07=#8g427rMg(>^(AjdXYUR?V8a zKJM`@#@)_CBiIzWYlf3G-Rp5M{l2A2-dgtZYp(uBbl__L6ZA~)l=`^oc6iA5wcKA8 z#YbVgi0+CSdB6#;qx0Q^^fww=Tj5@ZqwrRN6G{1N{&I#&x&r}a_`K1tCAtFN!>ze3 zh5^n%?_`g)L{U}F?rWtMT!KJK!RdQycjb@C1P$C;P0E>gEq7>ki&(Jqx@4tD-rD!V zP-_3_oPBBfI_`=mV~0p3=zFEcyMu27L zs>*%xO1kkqz3%*XbJZ7T)PplD^U4GY%=*fRDrFYClZ7&UPqb=qTa3i*4vl*jDxZC6 z3~ntp!4Uzf&>Ot(5JbP(Df)}O)MN=^!54nhc6-Kd7;0i z2&}EcOf;9PDl84+kufpY^M(V)PjXtexQ)3)9tdB{0f`0>c$6(%`1fqKm;%iZYeqP# zLHoH}01J*qLovde?F5t1or$bZ5d z{yL)t#WN}?Nw`p*K>CIvEc5>I`bM=i-EU62PMlP>_TL@Y(Zr?HcQv4 z3+y^B(CQ7cMmne~#lw5+pH-93!pMlwN!?mG?@@Ra_Md z>XwrKJdzJAaF!5pb|D{jg@oLAC`jE~G z`mnnAeZhSWbQLz`=OHHSA|YhMJ>+u@^p|JIk26kNZz%VRCEy|wI*oF>PMEr(bu$}4853@)5)hnaxGIx+6-SZ5Av^Ps6D79| zNf1L^xPeGG)-KpkI~*$#RtL5)*w|K$IBw1eDlEN6f|r3mamr+$OK$O;l&T~c2VMP%io-V9dyD*#Lqmz2vF7P6F3vQF@ok<`Gg z)Jgp%9qabx1U~&p9BOT^C2o5A-eh`tysW3YVj6{JQ zC4f~4V6HA_lEHgP7IKsUmS%wW;31L>-g!L4h5&wy$5!WI6PRE-BFu&uUXz-IDFsgw zkfKc3h3jBx%57-?tO$Tx&tN*qXbuA|%0Trl1IgQJ2(dh{G&h*bfUi9T7WJ62*<)&)`uUt`mxZn&hA+@{$j!1;y1m{i@3y zugQID8ZzvieZUlakd;4V7cZb&An1)4!evYaqBtDX_%a+|#R;17eqiz?lfiNTYK?$Z z5)F3XECKg(h!uy`VciRM9%0H-+hz=qr zaj1_tG@d{MiJ-SgXq)YW2o4$5ghTR?(ZfvC6cDS21V2wl%@BBH21|O?FoD{^Cy?l$ zj7R})Pp)0ySBqF=!pOuzE*9fWos$eFAH;)>t_K~$A>!vyKDBUZBKY?Pyo-p64=Tmu zv-jbGUm&y70CXi2`w6%^kr!_`Qs`d`Dj;F27+9a$WIjS%TURbH2wx;14CNqQH&9(n zzN<60hiYLTIj8{=MzIs!v<###!$pZ;IkoagJl|qBl8=QNry#2-yoboyr{R&W?&pdz zP~#lbr$M;CHcc9joW!GM02DG0{f_$`WJ0d0aS!1zJpj0vgQ}$PH84RCk(-vA^xxYY zB#eU|rXXwaMP-4xs+`hl96B2pk}485PDYKA(dvwcc(sb_T>dMbi;s>Kn&qn?#Jn_# zABxI7q*e}?gSE}D>mv^;Ntoef-~su;A`a<5`DY`y2^R~t^`~?ZaB^}5zdD%Fwf&d&+hGtPP0RS+J!#*d$wMk%UVxu+y zoLNSfDfQLj3z{K9l12(u{z(awH!EKAtFfKv4orN0X{7L6?`ON?YCXGXmF);

    9+`9CDBy9&&N;zBC8UWn!c=z;-N50fm?1 zm0?TFuw_73&7U8EKR0Fd+>hz`_M>SL1wZ4%H?|D%M#Cj>$aZp37lYUFC*~CiQKx`W zj7F#r_Soe0FdKVJlp(&H(%@xK&N2q$f#tf$u2X>YJghLg=_nI^l!$8Qf`1A9pYCT& zaS;?0tV$znaUMKQ;e~KX3KSkL69ph)7v#EZNbSjLeDeU}nQUOhde0-}7hW+i1VqM0F6r(>~|~5`qi1%jm2fJWfnWwnzI4Xzz_?W%oUvfw_g>QZt-7<�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�@gdzAN%CsF=xl>#FIPCW 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 (yk7$(eTc**G+Z{_<$0eM>XxFqek9X{4jJ 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 /dev/null - -KURYR_DIR=${KURYR_DIR:-./} -KURYR_API_PROTO="kuryr_kubernetes/pod_resources/api.proto" - -# If API_VERSION is not specified assuming v1alpha1. -VERSION=${API_VERSION:-v1alpha1} - -ACTIVATED="no" -ENV_DIR=$(mktemp -d -t kuryr-tmp-env-XXXXXXXXXX) - -function cleanup() { - if [ "${ACTIVATED}" = "yes" ]; then deactivate; fi - rm -rf "${ENV_DIR}" -} -trap cleanup EXIT INT - -if [ -z "${KUBERNETES_API_PROTO}" ]; then - - echo "KUBERNETES_API_PROTO is not specified." \ - "Trying to download api.proto from the k8s github." - - pushd "${ENV_DIR}" - - BASE_URL="https://raw.githubusercontent.com/kubernetes/kubernetes/master" - PROTO_FILE="pkg/kubelet/apis/podresources/${VERSION}/api.proto" - - wget "${BASE_URL}/${PROTO_FILE}" -O api.proto - - KUBERNETES_API_PROTO="$PWD/api.proto" - popd -fi - -if [ ! -f "${KUBERNETES_API_PROTO}" ]; then - echo "Can't find ${KUBERNETES_API_PROTO}" - exit 1 -fi - -KUBERNETES_API_PROTO=$(readlink -e "${KUBERNETES_API_PROTO}") - -pushd "${KURYR_DIR}" - -# Obtaining api version from the proto file. -VERSION=$(grep package "${KUBERNETES_API_PROTO}" \ - | sed 's/^package *\(.*\)\;$/\1/') -echo "\ -// Generated from kubernetes/pkg/kubelet/apis/podresources/${VERSION}/api.proto -// To regenerate api.proto, api_pb2.py and api_pb2_grpc.py follow instructions -// from doc/source/devref/updating_pod_resources_api.rst. -" > ${KURYR_API_PROTO} - -# Stripping unwanted dependencies. -sed '/gogoproto/d;/api.pb.go/d' "${KUBERNETES_API_PROTO}" >> ${KURYR_API_PROTO} -echo '' >> ${KURYR_API_PROTO} -# Stripping redundant empty lines. -sed -i '/^$/N;/^\n$/D' ${KURYR_API_PROTO} - -# Creating new virtual environment. -python3 -m venv "${ENV_DIR}" -source "${ENV_DIR}/bin/activate" -ACTIVATED="yes" - -pip install grpcio-tools==1.19 - -# Checking protobuf version. -protobuf_version=$(grep protobuf lower-constraints.txt \ - | sed 's/^protobuf==\([0-9\.]*\)\.[0-9]*$/\1/') -protoc_version=$(python -m grpc_tools.protoc --version \ - | sed 's/^libprotoc \([0-9\.]*\)\.[0-9]*$/\1/') -if [ "${protobuf_version}" != "${protoc_version}" ]; then - echo "protobuf version in lower-constraints.txt (${protobuf_version})" \ - "!= installed protoc compiler version (${protoc_version})." - echo "Please, update requirements.txt and lower-constraints.txt or" \ - "change version of grpcio-tools used in this script." - # Clearing api.proto to highlight the issue. - echo '' > ${KURYR_API_PROTO} - exit 1 -fi - -# Generating python bindings. -python -m grpc_tools.protoc -I./ \ - --python_out=. --grpc_python_out=. ${KURYR_API_PROTO} -popd diff --git a/contrib/sctp_client.py b/contrib/sctp_client.py deleted file mode 100644 index b4a5248bf..000000000 --- a/contrib/sctp_client.py +++ /dev/null @@ -1,31 +0,0 @@ -# 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 sctp -import socket -import sys - -sk = sctp.sctpsocket_tcp(socket.AF_INET) - - -def connect_plus_message(OUT_IP, OUT_PORT): - sk.connect((OUT_IP, OUT_PORT)) - print("Sending Message") - sk.sctp_send(msg='HELLO, I AM ALIVE!!!') - msgFromServer = sk.recvfrom(1024) - print(msgFromServer[0].decode('utf-8')) - sk.shutdown(0) - sk.close() - - -if __name__ == '__main__': - connect_plus_message(sys.argv[1], int(sys.argv[2])) diff --git a/contrib/testing/container/Dockerfile b/contrib/testing/container/Dockerfile deleted file mode 100644 index 13f0ff698..000000000 --- a/contrib/testing/container/Dockerfile +++ /dev/null @@ -1,3 +0,0 @@ -FROM scratch -ADD kuryr_testing_rootfs.tar.gz / -CMD ["/usr/bin/kuryr_hostname"] diff --git a/contrib/testing/container/build.sh b/contrib/testing/container/build.sh deleted file mode 100755 index 804c51fa3..000000000 --- a/contrib/testing/container/build.sh +++ /dev/null @@ -1,43 +0,0 @@ -#!/bin/bash - -set -o errexit - - -function install_busybox { - if [[ -x $(command -v apt-get 2> /dev/null) ]]; then - sudo apt-get update - sudo apt-get install -y busybox-static gcc - elif [[ -x $(command -v dnf 2> /dev/null) ]]; then - sudo dnf install -y busybox gcc - elif [[ -x $(command -v yum 2> /dev/null) ]]; then - sudo yum install -y busybox gcc - elif [[ -x $(command -v pacman 2> /dev/null) ]]; then - sudo pacman -S --noconfirm busybox gcc - else - echo "unknown distro" 1>2 - exit 1 - fi - return 0 -} - -function make_root { - local root_dir - local binary - - root_dir=$(mktemp -d) - mkdir -p "${root_dir}/bin" "${root_dir}/usr/bin" - binary=$(command -v busybox) - cp "$binary" "${root_dir}/bin/busybox" - "${root_dir}/bin/busybox" --install "${root_dir}/bin" - gcc --static hostname.c -o "${root_dir}/usr/bin/kuryr_hostname" - tar -C "$root_dir" -czvf kuryr_testing_rootfs.tar.gz bin usr - return 0 -} - -function build_container { - docker build -t kuryr/test_container . -} - -install_busybox -make_root -build_container diff --git a/contrib/testing/container/hostname.c b/contrib/testing/container/hostname.c deleted file mode 100644 index e31778261..000000000 --- a/contrib/testing/container/hostname.c +++ /dev/null @@ -1,129 +0,0 @@ -#define _GNU_SOURCE -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define MAX_LEN 1024 -#define BACKLOG 10 -#define LISTENING_PORT 8000 - -volatile sig_atomic_t running = 1; -volatile sig_atomic_t sig_number; - -static void handler(int signo) { - sig_number = signo; - running = 0; -} - - -int main() { - struct sigaction sa = { - .sa_handler = handler, - .sa_flags = 0}; - sigemptyset(&sa.sa_mask); - - if (sigaction(SIGINT, &sa, NULL) == -1) { - err(1, "Failed to set SIGINT handler"); - } - if (sigaction(SIGTERM, &sa, NULL) == -1) { - err(1, "Failed to set SIGTERM handler"); - } - - int enable = 1; - int result = 1; - char hostname[MAX_LEN]; - int res = gethostname(hostname, MAX_LEN); - if (res < 0) { - err(1, "Failed to retrieve hostname"); - } - - char *response; - ssize_t responselen; - responselen = asprintf(&response, "HTTP/1.1 200 OK\r\n" - "Content-Type: text/html; charset=UTF-8\r\n\r\n" - "%s\r\n", hostname); - if (responselen == -1) { - err(1, "Failed to form response"); - } - - int sock = socket(AF_INET, SOCK_STREAM, 0); - if (sock < 0) { - perror("Failed to open socket"); - goto nosocket; - } - - res = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &enable, - sizeof(int)); - if (res < 0) { - perror("Failed to set socket options"); - goto cleanup; - } - - struct sockaddr_in srv = { - .sin_family = AF_INET, - .sin_port = htons(LISTENING_PORT), - .sin_addr = { .s_addr = INADDR_ANY}}; - socklen_t addrlen= sizeof(srv); - - res = bind(sock, (struct sockaddr *) &srv, (socklen_t) sizeof(srv)); - if (res < 0) { - res = close(sock); - if (res == -1) { - perror("Failed close socket"); - goto cleanup; - } - perror("Failed to bind socket"); - goto cleanup; - } - - res = listen(sock, BACKLOG); - if (res < 0) { - perror("Failed to set socket to listen"); - goto cleanup; - } - - while (running) { - struct sockaddr_in cli; - int client_fd = accept(sock, (struct sockaddr *) &cli, - &addrlen); - if (client_fd == -1) { - if (running) { - perror("failed to accept connection"); - continue; - } else { - char *signame = strsignal(sig_number); - printf("Received %s. Quitting\n", signame); - break; - } - } - fprintf(stderr, "Accepted client connection\n"); - - /* Assume we write it all at once */ - write(client_fd, response, responselen); - res = shutdown(client_fd, SHUT_RDWR); - if (res == -1) { - perror("Failed to shutdown client connection"); - goto cleanup; - } - res = close(client_fd); - if (res == -1) { - perror("Failed to close client connection"); - goto cleanup; - } - } - - result = 0; -cleanup: - close(sock); -nosocket: - free(response); - return result; -} diff --git a/contrib/vagrant/README.rst b/contrib/vagrant/README.rst deleted file mode 100644 index 4c780eee6..000000000 --- a/contrib/vagrant/README.rst +++ /dev/null @@ -1,99 +0,0 @@ -==================================================== -Vagrant based Kuryr-Kubernetes devstack installation -==================================================== - -Deploy kuryr-kubernetes on devstack in VM using `Vagrant`_. Vagrant simplifies -life cycle of the local virtual machine and provides automation for repetitive -tasks. - -Requirements ------------- - -For comfortable work, here are minimal host requirements: - -#. ``vagrant`` installed -#. 4 CPU cores -#. At least 8GB of RAM -#. Around 20GB of free disk space - -Vagrant will create VM with 2 cores, 6GB of RAM and dynamically expanded disk -image. - - -Getting started ---------------- - -You'll need vagrant itself, i.e.: - -.. code:: console - - $ apt install vagrant virtualbox - -As an option, you can install libvirt instead of VirtualBox, although -VirtualBox is as an easiest drop-in. - -Next, clone the kuryr-kubernetes repository: - -.. code:: console - - $ git clone https://opendev.org/openstack/kuryr-kubernetes - -And run provided vagrant file, by executing: - -.. code:: console - - $ cd kuryr-kubernetes/contrib/vagrant - $ vagrant up - -This can take some time, depending on your host performance, and may take -20 minutes and up. - -After deploying is complete, you can access VM by ssh: - -.. code:: console - - $ vagrant ssh - -At this point you should have experimental kubernetes (etcdv3, k8s-apiserver, -k8s-controller-manager, k8s-scheduler, kubelet and kuryr-controller), docker, -OpenStack services (neutron, keystone, placement, nova, octavia), kuryr-cni and -kuryr-controller all up, running and pointing to each other. Pods and services -orchestrated by kubernetes will be backed by kuryr+neutron and Octavia. The -architecture of the setup `can be seen here`_. - - -Vagrant Options available -------------------------- - -You can set the following environment variables before running `vagrant up` to -modify the definition of the Virtual Machine spawned: - -* ``VAGRANT_KURYR_VM_BOX`` - to change the Vagrant Box used. Should be - available in `atlas `_. For example of a - rpm-based option: - - .. code:: console - - $ export VAGRANT_KURYR_VM_BOX=centos/8 - -* ``VAGRANT_KURYR_VM_MEMORY`` - to modify the RAM of the VM. Defaulted to: - **6144**. If you plan to create multiple Kubernetes services on the setup and - the Octavia driver used is Amphora, you should increase this setting. -* ``VAGRANT_KURYR_VM_CPU``: to modify number of CPU cores for the VM. Defaulted - to: **2**. -* ``VAGRANT_KURYR_RUN_DEVSTACK`` - whether ``vagrant up`` should run devstack - to have an environment ready to use. Set it to 'false' if you want to edit - ``local.conf`` before stacking devstack in the VM. Defaulted to: **true**. - See below for additional options for editing local.conf. - - -Additional devstack configuration ---------------------------------- - -To add additional configuration to local.conf before the VM is provisioned, you -can create a file called ``user_local.conf`` in the contrib/vagrant directory -of networking-kuryr. This file will be appended to the "local.conf" created -during the Vagrant provisioning. - -.. _Vagrant: https://www.vagrantup.com/ -.. _can be seen here: https://docs.openstack.org/developer/kuryr-kubernetes/devref/kuryr_kubernetes_design.html diff --git a/contrib/vagrant/Vagrantfile b/contrib/vagrant/Vagrantfile deleted file mode 100644 index d7be10bda..000000000 --- a/contrib/vagrant/Vagrantfile +++ /dev/null @@ -1,50 +0,0 @@ -VAGRANTFILE_API_VERSION = "2" - -Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| - - VM_MEMORY = ENV.fetch('VAGRANT_KURYR_VM_MEMORY', 6144).to_i - VM_CPUS = ENV.fetch('VAGRANT_KURYR_VM_CPUS', 2).to_i - RUN_DEVSTACK = ENV.fetch('VAGRANT_KURYR_RUN_DEVSTACK', 'true') - - config.vm.hostname = 'devstack' - - config.vm.provider 'virtualbox' do |v, override| - override.vm.box = ENV.fetch('VAGRANT_KURYR_VM_BOX', 'generic/ubuntu2004') - v.memory = VM_MEMORY - v.cpus = VM_CPUS - v.customize "post-boot", ['controlvm', :id, 'setlinkstate1', 'on'] - end - - config.vm.provider 'parallels' do |v, override| - override.vm.box = ENV.fetch('VAGRANT_KURYR_VM_BOX', 'generic/ubuntu2004') - v.memory = VM_MEMORY - v.cpus = VM_CPUS - v.customize ['set', :id, '--nested-virt', 'on'] - end - - config.vm.provider 'libvirt' do |v, override| - override.vm.box = ENV.fetch('VAGRANT_KURYR_VM_BOX', 'generic/ubuntu2004') - v.memory = VM_MEMORY - v.cpus = VM_CPUS - v.nested = true - v.graphics_type = 'spice' - v.video_type = 'qxl' - end - - config.vm.synced_folder '../../devstack/', '/devstack', type: 'rsync' - # For CentOS machines it needs to be specified - config.vm.synced_folder '.', '/vagrant', type: 'rsync' - - - config.vm.provision :shell do |s| - s.path = 'vagrant.sh' - s.args = RUN_DEVSTACK - end - - - if Vagrant.has_plugin?('vagrant-cachier') - config.cache.scope = :box - end - - config.vm.network :forwarded_port, guest: 80, host_ip: "127.0.0.1", host: 8080 -end diff --git a/contrib/vagrant/config/kuryr_rc b/contrib/vagrant/config/kuryr_rc deleted file mode 100644 index 06682b54b..000000000 --- a/contrib/vagrant/config/kuryr_rc +++ /dev/null @@ -1,4 +0,0 @@ -export OS_USERNAME=admin -export OS_PASSWORD=pass -export OS_PROJECT_NAME=admin -export OS_AUTH_URL=http://127.0.0.1/identity diff --git a/contrib/vagrant/devstack.sh b/contrib/vagrant/devstack.sh deleted file mode 100755 index ac5e2ddaf..000000000 --- a/contrib/vagrant/devstack.sh +++ /dev/null @@ -1,62 +0,0 @@ -#!/bin/bash - -set -ex - -echo $(whoami) - -BASHPATH=$(dirname "$0"\") -RUN_DEVSTACK="$1" -echo "Run script from $BASHPATH" - -# Copied shamelessly from Devstack -function GetOSVersion { - if [[ -x $(which lsb_release 2>/dev/null) ]]; then - os_FAMILY='Debian' - elif [[ -r /etc/redhat-release ]]; then - os_FAMILY='RedHat' - else - echo "Unsupported distribution!" - exit 1; - fi -} - -GetOSVersion - -if [[ "$os_FAMILY" == "Debian" ]]; then - export DEBIAN_FRONTEND noninteractive - sudo apt-get update - sudo apt-get install -qqy git -elif [[ "$os_FAMILY" == "RedHat" ]]; then - sudo yum install -y -d 0 -e 0 git -fi - -# determine checkout folder -PWD=$(getent passwd $OS_USER | cut -d: -f6) -DEVSTACK=$PWD/devstack - -# check if devstack is already there -if [[ ! -d "$DEVSTACK" ]] -then - echo "Download devstack into $DEVSTACK" - - # clone devstack - su "$OS_USER" -c "cd && git clone -b master https://github.com/openstack-dev/devstack.git $DEVSTACK" - - echo "Copy configuration" - - # copy local.conf.sample settings (source: kuryr/devstack/local.conf.sample) - cp /devstack/local.conf.sample $DEVSTACK/local.conf - # If local settings are present, append them - if [ -f "/vagrant/user_local.conf" ]; then - cat /vagrant/user_local.conf >> $DEVSTACK/local.conf - fi - chown "$OS_USER":"$OS_USER" "$DEVSTACK"/local.conf - -fi - -if $RUN_DEVSTACK; then - echo "Start Devstack" - su "$OS_USER" -c "cd $DEVSTACK && ./stack.sh" -else - echo "Virtual Machine ready. You can run devstack by executing '/home/$OS_USER/devstack/stack.sh'" -fi diff --git a/contrib/vagrant/vagrant.sh b/contrib/vagrant/vagrant.sh deleted file mode 100755 index 18af30af4..000000000 --- a/contrib/vagrant/vagrant.sh +++ /dev/null @@ -1,25 +0,0 @@ -#!/bin/sh - -getent passwd vagrant > /dev/null -if [ $? -eq 0 ]; then - export OS_USER=vagrant -else - getent passwd ubuntu > /dev/null - if [ $? -eq 0 ]; then - export OS_USER=ubuntu - fi -fi - -set -ex - -export HOST_IP=127.0.0.1 - -# Enable IPv6 -sudo sysctl -w net.ipv6.conf.default.disable_ipv6=0 -sudo sysctl -w net.ipv6.conf.all.disable_ipv6=0 - -# run script -bash /vagrant/devstack.sh "$1" - -#set environment variables for kuryr -su "$OS_USER" -c "echo 'source /vagrant/config/kuryr_rc' >> ~/.bash_profile" diff --git a/controller.Dockerfile b/controller.Dockerfile deleted file mode 100644 index 125f7a629..000000000 --- a/controller.Dockerfile +++ /dev/null @@ -1,32 +0,0 @@ -FROM quay.io/centos/centos:stream9 -LABEL authors="Antoni Segura Puimedon, Michał Dulko" - -ARG UPPER_CONSTRAINTS_FILE="https://releases.openstack.org/constraints/upper/master" - -RUN dnf upgrade -y \ - && dnf install -y epel-release \ - && dnf install -y --setopt=tsflags=nodocs python3-pip libstdc++ \ - && dnf install -y --setopt=tsflags=nodocs gcc gcc-c++ python3-devel git - -COPY . /opt/kuryr-kubernetes - -ARG VIRTUAL_ENV=/opt/venv -RUN python3 -m venv $VIRTUAL_ENV -# This is enough to activate a venv -ENV PATH="$VIRTUAL_ENV/bin:$PATH" - -RUN pip3 --no-cache-dir install -U pip \ - && python3 -m pip install -c $UPPER_CONSTRAINTS_FILE --no-cache-dir /opt/kuryr-kubernetes \ - && dnf -y history undo last \ - && dnf clean all \ - && rm -rf /opt/kuryr-kubernetes \ - && groupadd -r kuryr -g 1000 \ - && useradd -u 1000 -g kuryr \ - -d /opt/kuryr-kubernetes \ - -s /sbin/nologin \ - -c "Kuryr controller user" \ - kuryr - -USER kuryr -CMD ["--config-dir", "/etc/kuryr"] -ENTRYPOINT [ "kuryr-k8s-controller" ] diff --git a/devstack/files/debs/kuryr-kubernetes b/devstack/files/debs/kuryr-kubernetes deleted file mode 100644 index f5267c266..000000000 --- a/devstack/files/debs/kuryr-kubernetes +++ /dev/null @@ -1 +0,0 @@ -golang diff --git a/devstack/files/rpms/kuryr-kubernetes b/devstack/files/rpms/kuryr-kubernetes deleted file mode 100644 index f5267c266..000000000 --- a/devstack/files/rpms/kuryr-kubernetes +++ /dev/null @@ -1 +0,0 @@ -golang diff --git a/devstack/lib/kubernetes b/devstack/lib/kubernetes deleted file mode 100644 index 403479c8f..000000000 --- a/devstack/lib/kubernetes +++ /dev/null @@ -1,245 +0,0 @@ -#!/bin/bash - -KURYR_KUBEADMIN_IMAGE_REPOSITORY="registry.k8s.io" -function get_k8s_log_level { - if [[ ${ENABLE_DEBUG_LOG_LEVEL} == "True" ]]; then - echo "4" - else - echo "2" - fi -} - -function kubeadm_install { - if ! is_ubuntu && ! is_fedora; then - (>&2 echo "WARNING: kubeadm installation is not supported in this \ -distribution.") - return - fi - - if is_ubuntu; then - apt_get install apt-transport-https gpg - sudo mkdir -p -m 755 /etc/apt/keyrings - curl -fsSL https://pkgs.k8s.io/core:/stable:/v${KURYR_KUBERNETES_VERSION%.*}/deb/Release.key | \ - sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg - echo 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v'${KURYR_KUBERNETES_VERSION%.*}'/deb/ /' | \ - sudo tee /etc/apt/sources.list.d/kubernetes.list - - REPOS_UPDATED=False apt_get_update - - # NOTE(gryf): kubectl will be installed alongside with the kubeadm as - # a dependency, although let's pin it to the k8s version as well. - kube_pkg_version=$(sudo apt-cache show kubeadm | grep "Version: $KURYR_KUBERNETES_VERSION-" | awk '{ print $2 }') - apt_get install \ - kubelet="${kube_pkg_version}" \ - kubeadm="${kube_pkg_version}" \ - kubectl="${kube_pkg_version}" - sudo apt-mark hold kubelet kubeadm kubectl - # NOTE(hongbin): This work-around an issue that kubelet pick a wrong - # IP address if the node has multiple network interfaces. - # See https://github.com/kubernetes/kubeadm/issues/203 - echo "KUBELET_EXTRA_ARGS=--node-ip=$HOST_IP" | sudo tee -a \ - /etc/default/kubelet - sudo systemctl daemon-reload && sudo systemctl restart kubelet - fi - - if is_fedora; then - source /etc/os-release - os_VENDOR=$(echo $NAME | tr -d '[:space:]') - if [[ $os_VENDOR =~ "CentOS" ]]; then - cat <> ${output_dir}/kubeadm-init.yaml << EOF -apiVersion: kubeadm.k8s.io/v1beta3 -kind: ClusterConfiguration -imageRepository: "${KURYR_KUBEADMIN_IMAGE_REPOSITORY}" -etcd: - external: - endpoints: - - "http://${SERVICE_HOST}:${ETCD_PORT}" -networking: - serviceSubnet: "$(IFS=, ; echo "${cluster_ip_ranges[*]}")" -apiServer: - extraArgs: - endpoint-reconciler-type: "none" - min-request-timeout: "300" - allow-privileged: "true" - v: "$(get_k8s_log_level)" -controllerManager: - extraArgs: - master: "$KURYR_K8S_API_URL" - min-resync-period: "3m" - v: "$(get_k8s_log_level)" - leader-elect: "false" -scheduler: - extraArgs: - master: "${KURYR_K8S_API_URL}" - v: "$(get_k8s_log_level)" - leader-elect: "false" ---- -apiVersion: kubeadm.k8s.io/v1beta3 -kind: InitConfiguration -bootstrapTokens: -- token: "${KURYR_K8S_TOKEN}" - ttl: 0s -localAPIEndpoint: - advertiseAddress: "${K8S_API_SERVER_IP}" - bindPort: ${K8S_API_SERVER_PORT} -nodeRegistration: - criSocket: "$cri_socket" - kubeletExtraArgs: - enable-server: "true" - taints: - [] ---- -apiVersion: kubelet.config.k8s.io/v1beta1 -kind: KubeletConfiguration -failSwapOn: false -address: "0.0.0.0" -enableServer: true -cgroupDriver: $cgroup_driver -EOF - sudo kubeadm config images pull --image-repository=${KURYR_KUBEADMIN_IMAGE_REPOSITORY} - args="--config ${output_dir}/kubeadm-init.yaml" - # NOTE(gryf): skip installing kube proxy, kuryr will handle services. - args+=" --skip-phases=addon/kube-proxy" - args+=" --ignore-preflight-errors Swap" - - if ! is_service_enabled coredns; then - # FIXME(gryf): Do we need specific configuration for coredns? - args+=" --skip-phases=addon/coredns" - fi - sudo kubeadm init $args - - local kube_config_file=$HOME/.kube/config - mkdir -p $(dirname ${kube_config_file}) - sudo cp /etc/kubernetes/admin.conf $kube_config_file - safe_chown $STACK_USER:$STACK_USER $kube_config_file -} - -function kubeadm_join { - local output_dir="${DATA_DIR}/kuryr-kubernetes" - local cgroup_driver - local cri_socket - - mkdir -p "${output_dir}" - - if [[ ${CONTAINER_ENGINE} == 'crio' ]]; then - local crio_conf="/etc/crio/crio.conf" - cgroup_driver=$(iniget ${crio_conf} crio.runtime cgroup_manager) - cri_socket="unix:///var/run/crio/crio.sock" - else - # docker is used - cgroup_driver=$(docker info -f '{{.CgroupDriver}}') - cri_socket="/var/run/dockershim.sock" - fi - cluster_ip_ranges=() - for service_subnet_id in ${KURYR_SERVICE_SUBNETS_IDS[@]}; do - service_cidr=$(openstack --os-cloud devstack-admin \ - --os-region "$REGION_NAME" \ - subnet show "$service_subnet_id" \ - -c cidr -f value) - cluster_ip_ranges+=($(split_subnet "$service_cidr" | cut -f1)) - done - - # TODO(gryf): take care of cri-o case aswell - rm -f ${output_dir}/kubeadm-join.yaml - cat >> ${output_dir}/kubeadm-join.yaml << EOF -apiVersion: kubeadm.k8s.io/v1beta3 -discovery: - bootstrapToken: - apiServerEndpoint: ${SERVICE_HOST}:${KURYR_K8S_API_PORT} - token: "${KURYR_K8S_TOKEN}" - unsafeSkipCAVerification: true - tlsBootstrapToken: "${KURYR_K8S_TOKEN}" -kind: JoinConfiguration -nodeRegistration: - criSocket: "$cri_socket" - kubeletExtraArgs: - enable-server: "true" - taints: - [] ---- -apiVersion: kubelet.config.k8s.io/v1beta1 -kind: KubeletConfiguration -failSwapOn: false -address: "0.0.0.0" -enableServer: true -cgroupDriver: $cgroup_driver -EOF - sudo -E kubeadm join --ignore-preflight-errors Swap \ - --config ${output_dir}/kubeadm-join.yaml -} - -function get_k8s_apiserver { - # assumption is, there is no other cluster, so there is only one API - # server. - echo "$(kubectl config view -o jsonpath='{.clusters[].cluster.server}')" -} - -function get_k8s_token { - local secret - secret=$(kubectl get secrets -o jsonpath='{.items[0].metadata.name}') - echo $(kubectl get secret $secret -o jsonpath='{.items[0].data.token}' | \ - base64 -d) -} - -function kubeadm_reset { - sudo kubeadm reset -f - sudo iptables -F - sudo iptables -t nat -F - sudo iptables -t mangle -F - sudo iptables -X - sudo ipvsadm -C -} - -function kubeadm_uninstall { - sudo systemctl stop kubelet - apt_get purge --allow-change-held-packages. kubelet kubeadm kubeadm \ - kubernetes-cni apt-transport-https - sudo add-apt-repository -r -y \ - "deb https://apt.kubernetes.io/ kubernetes-xenial main" - REPOS_UPDATED=False apt_get_update - sudo rm -fr /etc/default/kubelet /etc/kubernetes -} diff --git a/devstack/lib/kuryr_kubernetes b/devstack/lib/kuryr_kubernetes deleted file mode 100644 index 759257f94..000000000 --- a/devstack/lib/kuryr_kubernetes +++ /dev/null @@ -1,1641 +0,0 @@ -#!/bin/bash -# -# lib/kuryr -# Utilities for kuryr-kubernetes devstack -# bind_for_kubelet -# Description: Creates an OVS internal port so that baremetal kubelet will be -# able to make both liveness and readiness http/tcp probes. -# Params: -# project - Id or name of the project used for kuryr devstack -# port - Port to open for K8s API, relevant only for OpenStack infra - -# Dependencies: -# (none) - -KURYR_CONF_NEUTRON=$(trueorfalse True KURYR_CONFIGURE_NEUTRON_DEFAULTS) -KURYR_IPV6=$(trueorfalse False KURYR_IPV6) -KURYR_DUAL_STACK=$(trueorfalse False KURYR_DUAL_STACK) -KURYR_USE_LC=$(trueorfalse False KURYR_CONTAINERS_USE_LOWER_CONSTRAINTS) - - -function container_runtime { - # Ignore error at killing/removing a container doesn't running to avoid - # unstack is terminated. - # TODO: Support for CRI-O if it's required. - local regex_cmds_ignore="(kill|rm)\s+" - - if [[ ${CONTAINER_ENGINE} == 'crio' ]]; then - sudo podman "$@" || die $LINENO "Error when running podman command" - else - if [[ $@ =~ $regex_cmds_ignore ]]; then - docker "$@" - else - docker "$@" || die $LINENO "Error when running docker command" - fi - fi -} - -function ovs_bind_for_kubelet { - local port_id - local port_mac - local fixed_ips - local port_ips - local port_subnets - local prefix - local project_id - local port_number - local security_group - local ifname - local service_subnet_cidr - local pod_subnet_gw - local cidrs - local _sp_id=${KURYR_NEUTRON_DEFAULT_SUBNETPOOL_ID} - - project_id="$1" - port_number="$2" - security_group=$(openstack security group list \ - --project "$project_id" -c ID -c Name -f value | \ - awk '{if ($2=="default") print $1}') - port_id=$(openstack port create \ - --device-owner compute:kuryr \ - --project "$project_id" \ - --security-group "$security_group" \ - --security-group service_pod_access \ - --host "${HOSTNAME}" \ - --network "${KURYR_NEUTRON_DEFAULT_POD_NET}" \ - -f value -c id \ - kubelet-"${HOSTNAME}") - - ifname="kubelet${port_id}" - ifname="${ifname:0:14}" - port_mac=$(openstack port show "$port_id" -c mac_address -f value) - fixed_ips=$(openstack port show "$port_id" -f value -c fixed_ips) - port_ips=($(python3 -c "print(' '.join([x['ip_address'] for x in ${fixed_ips}]))")) - port_subnets=($(python3 -c "print(' '.join([x['subnet_id'] for x in ${fixed_ips}]))")) - - sudo ovs-vsctl -- --may-exist add-port $OVS_BRIDGE "$ifname" \ - -- set Interface "$ifname" type=internal \ - -- set Interface "$ifname" external-ids:iface-status=active \ - -- set Interface "$ifname" external-ids:attached-mac="$port_mac" \ - -- set Interface "$ifname" external-ids:iface-id="$port_id" - - sudo ip link set dev "$ifname" address "$port_mac" - sudo ip link set dev "$ifname" up - for i in "${!port_ips[@]}"; do - prefix=$(openstack subnet show "${port_subnets[$i]}" \ - -c cidr -f value | \ - cut -f2 -d/) - sudo ip addr add "${port_ips[$i]}/${prefix}" dev "$ifname" - done - - # TODO(dulek): This hack is for compatibility with multinode job, we might - # want to do it better one day and actually support dual stack - # and NP here. - if [[ -z ${KURYR_SERVICE_SUBNETS_IDS} ]]; then - KURYR_SERVICE_SUBNETS_IDS=(${KURYR_NEUTRON_DEFAULT_SERVICE_SUBNET}-IPv4) - KURYR_POD_SUBNETS_IDS=(${KURYR_NEUTRON_DEFAULT_POD_SUBNET}-IPv4) - fi - - if [[ -z ${KURYR_SUBNETPOOLS_IDS} ]]; then - # NOTE(gryf): In case we are missing KURYR_SUBNETPOOLS_IDS variable - # populated, which probably means, that kuryr-kubernetes service is - # not enabled, but if kuryr-daemon service is enabled (which is the - # case for multi node setup, where worker nodes should have it - # enabled), we need to have it filled. - export KURYR_SUBNETPOOLS_IDS=() - export KURYR_ETHERTYPES=() - if [[ "$KURYR_IPV6" == "False" ]]; then - export KURYR_ETHERTYPE=IPv4 - KURYR_ETHERTYPES+=("IPv4") - KURYR_SUBNETPOOLS_IDS+=(${_sp_id:-${SUBNETPOOL_V4_ID}}) - else - KURYR_ETHERTYPES+=("IPv6") - KURYR_SUBNETPOOLS_IDS+=($(openstack \ - --os-cloud devstack-admin \ - --os-region "${REGION_NAME}" \ - subnet pool show ${SUBNETPOOL_KURYR_NAME_V6} -c id -f value)) - fi - fi - - for i in "${!KURYR_SERVICE_SUBNETS_IDS[@]}"; do - pod_subnet_gw=$(openstack subnet show "${KURYR_POD_SUBNETS_IDS[$i]}" \ - -c gateway_ip -f value) - if is_service_enabled kuryr-kubernetes && [[ "$KURYR_SUBNET_DRIVER" == "namespace" ]]; then - cidrs=$(openstack subnet pool show "${KURYR_SUBNETPOOLS_IDS[$i]}" -c prefixes -f value) - subnetpool_cidr=$(python3 -c "print(${cidrs}[0])") - sudo ip route add "$subnetpool_cidr" via "$pod_subnet_gw" dev "$ifname" - else - service_subnet_cidr=$(openstack --os-cloud devstack-admin \ - --os-region "$REGION_NAME" \ - subnet show "${KURYR_SERVICE_SUBNETS_IDS[$i]}" \ - -c cidr -f value) - sudo ip route add "$service_subnet_cidr" via "$pod_subnet_gw" dev "$ifname" - fi - done - - if [ -n "$port_number" ]; then - # if openstack-INPUT chain doesn't exist we create it in INPUT (for - # local development envs since openstack-INPUT is usually only in gates) - if [[ "$KURYR_IPV6" == "False" || "$KURYR_DUAL_STACK" == "True" ]]; then - sudo iptables -I openstack-INPUT 1 \ - -p tcp -s 0.0.0.0/0 -d 0.0.0.0/0 --dport $port_number -j ACCEPT || \ - sudo iptables -I INPUT 1 \ - -p tcp -m conntrack --ctstate NEW \ - -m tcp --dport "$port_number" \ - -m comment --comment "kuryr-devstack: Access to OpenShift API" -j ACCEPT - fi - if [[ "$KURYR_IPV6" == "True" || "$KURYR_DUAL_STACK" == "True" ]]; then - sudo ip6tables -I openstack-INPUT 1 \ - -p tcp -s ::/0 -d ::/0 --dport $port_number -j ACCEPT || \ - sudo ip6tables -I INPUT 1 \ - -p tcp -m conntrack --ctstate NEW \ - -m tcp --dport "$port_number" \ - -m comment --comment "kuryr-devstack: Access to OpenShift API" -j ACCEPT - fi - fi -} - -# _allocation_range -# Description: Writes out tab separated usable ip range for a CIDR -# Params: -# cidr - The cidr to get the range for -# gateway_position - Whether to reserve at 'beginning' or at 'end' -function _allocation_range { - python3 - <> "${output_dir}/config_map.yml" << EOF -apiVersion: v1 -kind: ConfigMap -metadata: - name: kuryr-config - namespace: kube-system -data: - kuryr.conf: | -EOF - - indent < "${conf_path}" >> "${output_dir}/config_map.yml" -} - -function generate_kuryr_certificates_secret { - local output_dir - local certs_bundle_path - output_dir=$1 - certs_bundle_path=${2:-""} - - mkdir -p "$output_dir" - rm -f "${output_dir}/certificates_secret.yml" - - CA_CERT=\"\" # It's a "" string that will be inserted into yaml file. - - if [ "$certs_bundle_path" -a -f "$certs_bundle_path" ]; then - CA_CERT=$(base64 -w0 < "$certs_bundle_path") - fi - - cat >> "${output_dir}/certificates_secret.yml" << EOF -apiVersion: v1 -kind: Secret -metadata: - name: kuryr-certificates - namespace: kube-system -type: Opaque -data: - kuryr-ca-bundle.crt: $CA_CERT -EOF -} - -# Generates kuryr-controller service account and kuryr-cni service account. -function generate_kuryr_service_account { - output_dir=$1 - mkdir -p "$output_dir" - rm -f "${output_dir}/controller_service_account.yml" - rm -f "${output_dir}/cni_service_account.yml" - cat >> "${output_dir}/controller_service_account.yml" << EOF ---- -apiVersion: v1 -kind: ServiceAccount -metadata: - name: kuryr-controller - namespace: kube-system ---- -kind: ClusterRole -apiVersion: rbac.authorization.k8s.io/v1 -metadata: - name: kuryr-controller -rules: -- apiGroups: - - "" - verbs: ["*"] - resources: - - endpoints - - pods - - services - - services/status - - namespaces -- apiGroups: - - "" - verbs: ["get", "list", "watch"] - resources: - - nodes -- apiGroups: - - openstack.org - verbs: ["*"] - resources: - - kuryrnetworks - - kuryrnetworkpolicies - - kuryrloadbalancers - - kuryrports -- apiGroups: ["networking.k8s.io"] - resources: - - networkpolicies - verbs: - - get - - list - - watch - - update - - patch -- apiGroups: ["k8s.cni.cncf.io"] - resources: - - network-attachment-definitions - verbs: - - get -- apiGroups: ["", "events.k8s.io"] - resources: - - events - verbs: - - create ---- -kind: ClusterRoleBinding -apiVersion: rbac.authorization.k8s.io/v1 -metadata: - name: kuryr-controller-global -subjects: -- kind: ServiceAccount - name: kuryr-controller - namespace: kube-system -roleRef: - kind: ClusterRole - name: kuryr-controller - apiGroup: rbac.authorization.k8s.io -EOF - - cat >> "${output_dir}/cni_service_account.yml" << EOF ---- -apiVersion: v1 -kind: ServiceAccount -metadata: - name: kuryr-cni - namespace: kube-system ---- -kind: ClusterRole -apiVersion: rbac.authorization.k8s.io/v1 -metadata: - name: kuryr-cni -rules: -- apiGroups: - - "" - verbs: ["*"] - resources: - - pods -- apiGroups: - - openstack.org - verbs: ["*"] - resources: - - kuryrports -- apiGroups: ["", "events.k8s.io"] - resources: - - events - verbs: - - create ---- -kind: ClusterRoleBinding -apiVersion: rbac.authorization.k8s.io/v1 -metadata: - name: kuryr-cni-global -subjects: -- kind: ServiceAccount - name: kuryr-cni - namespace: kube-system -roleRef: - kind: ClusterRole - name: kuryr-cni - apiGroup: rbac.authorization.k8s.io -EOF -} - -function generate_controller_deployment { - output_dir=$1 - health_server_port=$2 - controller_ha=$3 - mkdir -p "$output_dir" - rm -f "${output_dir}/controller_deployment.yml" - cat >> "${output_dir}/controller_deployment.yml" << EOF -apiVersion: apps/v1 -kind: Deployment -metadata: - labels: - name: kuryr-controller - name: kuryr-controller - namespace: kube-system -spec: - replicas: ${KURYR_CONTROLLER_REPLICAS:-1} - selector: - matchLabels: - name: kuryr-controller -EOF - - # When running without HA we should make sure that we won't have more than - # one kuryr-controller pod in the deployment. - if [ "$controller_ha" == "False" ]; then - cat >> "${output_dir}/controller_deployment.yml" << EOF - strategy: - type: RollingUpdate - rollingUpdate: - maxSurge: 0 - maxUnavailable: 1 -EOF - fi - - cat >> "${output_dir}/controller_deployment.yml" << EOF - template: - metadata: - labels: - name: kuryr-controller - name: kuryr-controller - spec: - serviceAccountName: kuryr-controller - automountServiceAccountToken: true - hostNetwork: true - containers: -EOF - - if [ "$controller_ha" == "True" ]; then - cat >> "${output_dir}/controller_deployment.yml" << EOF - - image: gcr.io/google_containers/leader-elector:0.5 - name: leader-elector - args: - - "--election=kuryr-controller" - - "--http=0.0.0.0:${KURYR_CONTROLLER_HA_PORT:-16401}" - - "--election-namespace=kube-system" - - "--ttl=5s" - ports: - - containerPort: ${KURYR_CONTROLLER_HA_PORT:-16401} - protocol: TCP -EOF - fi - - cat >> "${output_dir}/controller_deployment.yml" << EOF - - image: kuryr/controller:latest - imagePullPolicy: Never - name: controller - terminationMessagePath: "/dev/termination-log" - volumeMounts: - - name: config-volume - mountPath: "/etc/kuryr" - - name: certificates-volume - mountPath: "/etc/ssl/certs" - readOnly: true - readinessProbe: - httpGet: - path: /ready - port: ${health_server_port} - scheme: HTTP - timeoutSeconds: 5 - livenessProbe: - httpGet: - path: /alive - port: ${health_server_port} - initialDelaySeconds: 15 -EOF - - cat >> "${output_dir}/controller_deployment.yml" << EOF - volumes: - - name: config-volume - configMap: - name: kuryr-config - - name: certificates-volume - secret: - secretName: kuryr-certificates - restartPolicy: Always - tolerations: - - key: "node-role.kubernetes.io/master" - operator: "Exists" - effect: "NoSchedule" - - key: "node.kubernetes.io/not-ready" - operator: "Exists" - effect: "NoSchedule" -EOF -} - -function generate_cni_daemon_set { - output_dir=$1 - cni_health_server_port=$2 - local var_run=${VAR_RUN_PATH:-/var/run} - mkdir -p "$output_dir" - rm -f "${output_dir}/cni_ds.yml" - cat >> "${output_dir}/cni_ds.yml" << EOF -apiVersion: apps/v1 -kind: DaemonSet -metadata: - name: kuryr-cni-ds - namespace: kube-system - labels: - tier: node - app: kuryr-cni -spec: - selector: - matchLabels: - app: kuryr-cni - template: - metadata: - labels: - tier: node - app: kuryr-cni - spec: - hostNetwork: true - tolerations: - - key: node-role.kubernetes.io/master - operator: Exists - effect: NoSchedule - - key: "node.kubernetes.io/not-ready" - operator: "Exists" - effect: "NoSchedule" - serviceAccountName: kuryr-cni - containers: - - name: kuryr-cni - image: kuryr/cni:latest - imagePullPolicy: Never - command: [ "cni_ds_init" ] - env: - - name: KUBERNETES_NODE_NAME - valueFrom: - fieldRef: - fieldPath: spec.nodeName - - name: KURYR_CNI_POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - securityContext: - privileged: true - volumeMounts: - - name: bin - mountPath: /opt/cni/bin - - name: net-conf - mountPath: /etc/cni/net.d - - name: config-volume - mountPath: /etc/kuryr -EOF - if [ "$CONTAINER_ENGINE" != "crio" ]; then - cat >> "${output_dir}/cni_ds.yml" << EOF - - name: proc - mountPath: /host_proc -EOF - - fi - cat >> "${output_dir}/cni_ds.yml" << EOF - - name: var-pci - mountPath: /var/pci_address - - name: var-run - mountPath: /var/run - mountPropagation: HostToContainer -EOF - # NOTE(gryf): assuming the --namespaces-dir parameter would not be used, - # otherwise /var/run/$crio_netns_path is all wrong - if [ "$CONTAINER_ENGINE" = "crio" ] && \ - [ "${VAR_RUN_PATH}" != "/var/run" ]; then - cat >> "${output_dir}/cni_ds.yml" << EOF - - name: netns - mountPath: /var/run/netns - mountPropagation: HostToContainer -EOF - fi - cat >> "${output_dir}/cni_ds.yml" << EOF - readinessProbe: - httpGet: - path: /ready - port: ${cni_health_server_port} - scheme: HTTP - initialDelaySeconds: 60 - timeoutSeconds: 10 - livenessProbe: - httpGet: - path: /alive - port: ${cni_health_server_port} - initialDelaySeconds: 60 - volumes: - - name: bin - hostPath: - path: ${CNI_PLUGIN_DIR} - - name: net-conf - hostPath: - path: ${CNI_CONF_DIR} - - name: config-volume - configMap: - name: kuryr-config - - name: var-run - hostPath: - path: ${var_run} -EOF - if [[ "$CONTAINER_ENGINE" != "crio" ]]; then - cat >> "${output_dir}/cni_ds.yml" << EOF - - name: proc - hostPath: - path: /proc -EOF - fi - cat >> "${output_dir}/cni_ds.yml" << EOF - - name: var-pci - hostPath: - path: /var/pci_address -EOF - if [ "${CONTAINER_ENGINE}" = "crio" ] && \ - [ "${VAR_RUN_PATH}" != "/var/run" ]; then - cat >> "${output_dir}/cni_ds.yml" << EOF - - name: netns - hostPath: - path: /var/run/netns -EOF - fi -} - -# lb_state -# Description: Returns the state of the load balancer -# Params: -# id - Id or name of the loadbalancer the state of which needs to be -# retrieved. -function lb_state { - local lb_id - - lb_id="$1" - openstack loadbalancer show "$lb_id" | \ - awk '/provisioning_status/ {print $4}' -} - -function _wait_for_lb { - local lb_name - local curr_time - local time_diff - local start_time - - lb_name="$1" - timeout=${2:-$KURYR_WAIT_TIMEOUT} - - echo -n "Waiting for LB:$lb_name" - start_time=$(date +%s) - - while [[ "$(lb_state "$lb_name")" != "ACTIVE" ]]; do - echo -n "Waiting till LB=$lb_name is ACTIVE." - curr_time=$(date +%s) - time_diff=$((curr_time - start_time)) - [[ $time_diff -le $timeout ]] || die "Timed out waiting for $lb_name" - sleep 5 - done -} - -# create_load_balancer -# Description: Creates an OpenStack Load Balancer with either neutron LBaaS -# or Octavia -# Params: -# lb_name: Name to give to the load balancer. -# lb_vip_subnet: Id or name of the subnet where lb_vip should be -# allocated. -# project_id: Id of the project where the load balancer should be -# allocated. -# lb_vip: Virtual IP to give to the load balancer - optional. -function create_load_balancer { - local lb_name - local lb_vip_subnet - local lb_params - local project_id - - lb_name="$1" - lb_vip_subnet="$2" - project_id="$3" - - lb_params=" --name $lb_name " - if [ -z "$4" ]; then - echo -n "create_load_balancer LB=$lb_name, lb_vip not provided." - else - lb_params+=" --vip-address $4" - fi - - lb_params+=" --project ${project_id} --vip-subnet-id $lb_vip_subnet" - openstack loadbalancer create $lb_params -} - -# create_load_balancer_listener -# Description: Creates an OpenStack Load Balancer Listener for the specified -# Load Balancer with either neutron LBaaS or Octavia -# Params: -# name: Name to give to the load balancer listener. -# protocol: Whether it is HTTP, HTTPS, TCP, etc. -# port: The TCP port number to listen to. -# data_timeouts: Octavia's timeouts for client and server inactivity. -# lb: Id or name of the Load Balancer we want to add the Listener to. -# project_id: Id of the project where this listener belongs to. -function create_load_balancer_listener { - local name - local protocol - local port - local lb - local data_timeouts - local max_timeout - local project_id - - name="$1" - protocol="$2" - port="$3" - lb="$4" - project_id="$5" - data_timeouts="$6" - - max_timeout=1200 - # Octavia needs the LB to be active for the listener - _wait_for_lb "$lb" "$max_timeout" - - openstack loadbalancer listener create --name "$name" \ - --protocol "$protocol" \ - --protocol-port "$port" \ - --timeout-client-data "$data_timeouts" \ - --timeout-member-data "$data_timeouts" \ - "$lb" -} - -# create_load_balancer_pool -# Description: Creates an OpenStack Load Balancer Pool for the specified -# Load Balancer listener with either neutron LBaaS or Octavia -# Params: -# name: Name to give to the load balancer listener. -# protocol: Whether it is HTTP, HTTPS, TCP, etc. -# algorithm: Load Balancing algorithm to use. -# listener: Id or name of the Load Balancer Listener we want to add the -# pool to. -# project_id: Id of the project where this pool belongs to. -# lb: Id or name of the Load Balancer we want to add the pool to -# (optional). -function create_load_balancer_pool { - local name - local protocol - local algorithm - local listener - local lb - local project_id - - name="$1" - protocol="$2" - algorithm="$3" - listener="$4" - project_id="$5" - lb="$6" - - # We must wait for the LB to be active before we can put a Pool for it - _wait_for_lb "$lb" - - openstack loadbalancer pool create --name "$name" \ - --listener "$listener" \ - --protocol "$protocol" \ - --lb-algorithm "$algorithm" -} - -# create_load_balancer_member -# Description: Creates an OpenStack load balancer pool member -# Params: -# name: Name to give to the load balancer pool member. -# address: Whether it is HTTP, HTTPS, TCP, etc. -# port: Port number the pool member is listening on. -# pool: Id or name of the Load Balancer pool this member belongs to. -# subnet: Id or name of the subnet the member address belongs to. -# lb: Id or name of the load balancer the member belongs to. -# project_id: Id of the project where this pool belongs to. -function create_load_balancer_member { - local name - local address - local port - local pool - local lb - local project_id - - name="$1" - address="$2" - port="$3" - pool="$4" - lb="$5" - project_id="$6" - - # We must wait for the pool creation update before we can add members - _wait_for_lb "$lb" - - openstack loadbalancer member create --name "$name" \ - --address "$address" \ - --protocol-port "$port" \ - "$pool" -} - -# split_subnet -# Description: Splits a subnet in two subnets that constitute its halves -# Params: -# cidr: Subnet CIDR to split -# Returns: tab separated CIDRs of the two halves. -function split_subnet { - # precondition: The passed cidr must be of a prefix <= 30 - python3 - <*-@<}{fxQ$I;E@6YdrAZb23ZFE6!?UeMmir1 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 -
    Network Policy
    driver
    [Not supported by viewer]
    Network Policy Pod SG driver
    Network Policy Pod SG driver
    VIF Pool
    driver
    [Not supported by viewer]
    Network Policy
    svc SG driver
    [Not supported by viewer]
    LBaaS
    driver
    [Not supported by viewer]
    Policy handler
    Policy handler
    update_vif_sgs
    update_vif_sgs
    update_lbaas_sg
    update_lbaas_sg
    ensure_network_policy
    ensure_network_policy
    get_pod_sgs
    get_pod_sgs
    get_affected_pods
    get_affected_pods
    get_svc_sgs
    get_svc_sgs
    \ No newline at end of file diff --git a/doc/images/external_traffic_to_l7_router.svg b/doc/images/external_traffic_to_l7_router.svg deleted file mode 100644 index caf90638e..000000000 --- a/doc/images/external_traffic_to_l7_router.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/doc/images/fuxi_k8s_components.png b/doc/images/fuxi_k8s_components.png deleted file mode 100644 index a30a5e23faac7a73cb10fc6d25eebb427b904fd7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 28274 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

    !qyYzp} zc4(tP^-sQX0u{HQGv|+fo$otzfKzV*K>(D5|Jr}@_4Yx}OaC-v#Mks1Zhdlf2DlR&E(TI`KJc6k{vRYe8+%A5Rt3Uv#AOQ;P~y8@ zqrcb!{Cl)kG55)T>=~G))TKWHXivFjX8AO1EZd(mGhVQ?gT^g84391u^}#5K*$xKp z85;JgD#Y5S9gPxuarj`Ofx!p$@6Wx}h>eqv-lBKa>dYQ1wOFJcv$cVlK*xF0#R_}a zfr%~7lDf*`&Q?vgdn#xc2Ea?@f|g7-I*=i%J0R? zr3aRQq5!Oaryp(>pq{rn-&- ztj@?f1i%(`^dpIR_OTo8IUfVe>DkHtO)_?XaH@b*etZxRSMamjiINMZkAKxh?3koH zx)z)WNG`ML$h!4aXk3!KwYjlR9S*3E^rBf$RV5aY25`ZcOr*D;*zbdZHA{K{&}mNs z*v;%Y7y?w{b=tN30;Rh!p2k-IZ)B)W8?a9RlB97q>3h zyx7oNu?Okdon{wCMRDDC!CEBZS;%w1Adm2wHF(L46lji&h$kHhgZoufxt8`X5ei znk2(OL2c={pVJI4ps706kDV5BHwF-{OxK;>R?Pp53{Y+Ds|9&<`B1nL$F%AAO@3?Q z>8O`ed-Yki?sW1t_jA(6xNiC??VkTFn4nr{B7YJv*+(RdAi1^MBwJI97uN_IDhAjC zbw=TE<$WJpWgcCnlApS2<2i2b0ov6Ds~~3U%&hBOKP^Aas8TOww zJFYLPE5{1%_c2TcOO45cMp8USS9ey1CQ2Sx1BhXCMXBJf>__S8hhY5z##b`8=fuw4 z)jM|&rbHWaNk8q_no>@c2Oi%U2o{#N0tTgX7Si4WoV!Qj%jNjIa24_&G%+Oar3{Ewo?Uv>jUKU`XKpbw$+x2Iy8FMe8BadP1J9KD12 zwTAunzeCU@5`uumArO=xh({L!AqRr8HMc;WfdD6a>rMy+0SFRLgrEgs2m-w%5O`h( zLEFS2uxKF&bhUi_Jec?=rv6=O8&s13Lw|SbpJmJy;As6N%m4VrVd-rncy&?w>mm48 zbov)Ex1Gi-#GtKord=dZT_1Ja6|`0Fu=9F_d8h((O51-?Fz#FUW`4lq?b<(HPoIe9S3 zHe~&Fh~GFskJ!}NrskPEx$n~mADh?E-!2EthzJwG#_DlDr0lLMH@V6TP7=htp?XKsr>x-vcHWXj6-u@nT zWu6u-@su$9Wu_l)a}?!isUvDJ*}h!7W1HhJ8r~%UFW;qgcRJ)(I9rFmBHgea5faF2 zXKDV5CDgC9@h;cS>(}ZMxBZlBSo}y=dgo2)9jYk{M=y!){pF14FFzd@N#PUHeXc1j zx4h?B;N^NXHmXE$t@dOEKqQyh1=deW{b;!fa2rwsTf1dy^Ji*0jk5vt_2HuVq# z>U5@a{ai5SDj!(!@x@nJ%#K+hAXeL-{Kff3ji;Z2{RX1!f$U*f(o zq_{rkcVi`W7eX5HE)^V?Yaq3E7=*8l&Pg(qFxI|r3Y#h!rxZQg=ovOB#vQ&$Mfn?R zpI4~e#8BI{F3i&}`DrMF3>D@2*@auwdd8=m`#1a;P3omo`Jguca1?5taW{jUM1Hux z7v|hL7+U*sLcOxbUX?~m*_P2x`u2G?J$Rl!pdJ z4OL|w$=~s7iLgw2F^d^c0+&tZDiuN9{%$_asx0W`%CNi!b%sC?37QPCL`+MC2RP+j z8D4z_kglFeg)VZl+5%1SMc$Z&&Q%;gT*9&Zp~9xp2QgoQQhVC_^uSUXOFtCa^Et~o z0vXYInRj|s(z$`r=rt=`tMKJ^x<++gf?tOBu;Gv!d6=N;2AROdxG2juf(rYb_ORk8 zIr!P4{9Fj(rJyW?uU3&{9no*j@mihlAo!R>)p#wiKdj?)aOtoOHLE{?D5_?KuK=M_dTGmSjptfP;R#J9;Wf>VXM?071||;`v6#W` zFtYCmL>_Dy*h$JJ`9}|^q~fz=AaZ9ud+UITRfQFqXK>YAU7^(C)(pn=Lh`Cg#BFja zMSx`g@KXdGIjm4m>9Axo#fQ`9)sQ!mZSXRX^P|d-SI}gy+DZMIEAgr~TBSmo`s&Ax z>dUUPeDTKB@!uLWTcr3gyxflX4+&?^RZ+@I+=~9AtY7{1Fqyn=X~SqMY!#C^M>Z@x zNaUi2G4gEu*ZL08WKnBwxhzT*A=OjY=(7T|HuOPLPc;+I?eYOv&B6*^qm2!^4cZR` zbxRYCm_~jYh44;m8F=B*=_)`XO zwpU?dY{x(CV$_xtUzS_P0TYKM<|6D_`)Wg{pJ*3Np*2}w26On@T;UuPP-)0p5%|;? zQdjN`bxwQ`a z&vxZ!;-d7qGkQ~1t*yWlh_xcl2J`U@54=#$Qu{S8U)ovBjGIBpwK5FCyY{$Do{=k- zx=MZ9wTC(=yD46d|Hw-six@8P;aXiQ28d(L)|1yahuk<@4;_Qv#4Q->e#H=~nRZ68q&$08cb#p&gDVh5TC<@j;o$Mrflj5I2c5P3d-@riqg^+H&gVp}y* z0q{(0Yz(y&XNF-@p~#YIWkIt~x`_TX;-Hy<+ScId(q1gi0{>oEFcOap8y`#drn^Cz zN%@|Rop^~(*kuiKYe1p-5`{04x7R>z#BFlzWt@p+lR;D=DSxFl3Ozi(e_=7mVZ`T700M#o&u1 zWv3rSt{-twMV4;82y43a6GtMpBZO3 zgd>~Ek5=4DlDnhb?llZypD9`jP?`&d#^xT!Q_c%T%G#E$L~@3SmMb?h#!`%JQ>V=wVSp zT%fO|n*XX_lK$(BsxrO8pF2iTwX7F~fvCf?ZvIz>@-4=zCs)(U&PouWJc|Fr-3% ztRx9;8F;03fotS8`)i>4+JrHl(cs)?Dcadn#6I*u4eiM=xWGGJVH{@V6KC!29^%bu z=~Um~0M7cBbPHM%*9Mj22yah%w|XWbYq0Ec80kxlV)jB0@3O}Wg0Gpd4>Va+OR zBZEImRo9-bWy^(y*hdQ=%+1yD>MuPdG<7&;6&vUkT@HxRcR#bo^C}K`&x}xxUKvl4che@(l_)}yRJ95X5lfbi zdUJZPGq{%vI=K!&yW_n%L;7&rhCyN{Ozj&S|FC?jFm0UB>D6$aGCIZ!l=68Bd8izoa<% zF1Q(Dhx3F1kS82Hu*4`vc$;9R+)kqvx_=VRi>oh%QYqTb+tu|V6~X6n3^Hl8iW__1Y)_HZC_<{jh?u|f$h z_puYw@h&o@AG*uWO!$2sUagy>ex}*qxOFRCE-{QwXdj(G57Mrf-*SzxDJ0~@yeqBW zM^s7(^P(+{QLfa@tp-muC1J%aO)oiT^p-8M9$m|j=_LD@D94(D{D^+Ye5Q@y7|t0p zYm;$a7AaqyQ$x41RQ=7=UY@9*d1~eDT|%>8Gx~U7nIfS~*j;{{wny82_z>NF#rKNP ztl|a2d)1g+yfQPApPJLmm{5{9)_m{EO40tj!I==<<-)i=AaSEv#DD=dYhe1!>xi7U zUV%ddWr^oOg)`yr^HdGq*|r`|FewSjpB1&S12VHW>;=gz^G3UwvVND{eoa~bUUqu~ z_|4Zu_v;W*bhR4xCU6uUG6@Md`u}>bkOcwqB$QhLka zsd6oQzfA=hH$3%g^8MWq&cj#gLm^FjF+1ol5^-DT&n4pDNd;EfxAc8ucm#s^Yk=~P z?EE&|WK|ma8?64%Qh|8)O~8#IUnIO`0G$5csfW2u`EShkI~+jOP1=z3qprG>v3>@s z70v!(Q$5XO#+-OD@_+&MOUybEJmvX+`QxKURG}Uy@@306!SwZX42~6Q*(Y(eZ8(%UC&osi~K`Zosx{13;+O7f;H3(000s=06_5L z1~L8$7xhIe{tuzgBe2m8{7=XY`w09wDOAJE2LQMYy!r)(@eSX>Uu1gm*z|>=r{fF% zXWk9~e}8{bXAf5&yJt`bQBQBD^eshZ0Duz!R(og^kg+inn96G0a=GK*&vS}3#evPf3Nk+p$)&R<*qvLfExu3gF4F8r_a-M zV^!mMz}H8#Xy`|F_&+E3O9C8r@=Ac~d&3GBUZcbdIL4@8y@HNA;K!24@jtDDQEajP zL9FpSg-6j8a515;3~Wr1JO>pIHHS14V^{ZO@$brotiwEdH1ooA*YW3t&rh}f^e3T! z^MVbAtZo8QFz-IbHmq2DWRBuFD00=C3`hW;QsVFaO_PCoBT2>o5XA607@&1^C$~MR zTRl4GGUX%tTb_dtKOFq-9#C41u6k+2=gx}*lCM8PDU@`&+_o0Mq<2N*uxpu|S3=;c zTh9}Dz#$gZAqnj(DMrK{d=K7KV0 z4|q_67;OIzSFiBo1 z>H~52J=uG|R-awH&_M7XI)=T5>Ob}+IwvULpU4bY6xeE7Z`Gx~7LlZpgeb6i!>+gh$Rj) zMPJD!5d~;CumUzK=Jom_VQU*l?#Lph$zDKN&#yA-aoPa33aJXYd(o+@atx+_1S*22 zNv5f$0hXG(;b#Mmo;EX4?y!f=`Mu+tHR)9(%pJH*_qFT&13B0>FX|omYGy@Uc&ZLT zt*L{{x7WzwZ67}A!7UJD1vO@s-sjXHSH7Bb)4&okH?rnWyJuXOBV-C9SR$(MCm*1V zpsLT-`oU{t?>M7uS*P+_VXiQz3#tsFo|3&Y5F*E5GUWE(GRheuLy{o45xr}&<<#T3 zy8-<70G7mO@CKNnv?nLTY)pdwSUtU+3r|mNA`rTL!iR07*u2nr-3G3H=@0DVZluur zu!DSv1ghOTm%3d$)NsTfd`ynD`alieyPui*=0b4Zf1@15NS~uLo`Prf@M}^b-M`Aq zJsVd@NQg}vZ1hQ<4AbHO`~)nEzf53xZJ}^OMC{_PmFG~x%C#@aS#|(nyrI0j2iOup zgOrm#&9uQ=BD{NRHz+MNUWWzVCTGlOZ!nXeMU1|w88RO(zj}tf?(C+eZw?(+ zwdzfq46Q?S{5Q05BwbtJo3`(vuPK6+7=CGKh}A0@a6mQ4+v5vJkzizjf)dS@w0;<@ z?E}m`2`a72gdL#to2W0*Hb!cvDyqb~dm&HfWeV;sXSDg_61F-i)sgU(d z3`e_SyX?#s`j@lKB!gUN;@R=lhgaGn7-LZQt3KK;vdALXH;5B|CH@JA)&u`~Wz0>C zD$}GDWix4jXx>xO0H?PXqJ&f=naqdNw{80~IN}roZB#J~NJ^$M!<*5>jp?BBT%2|B zC*$8RP_kcu@YSn^H(SA>3C}q|qC{n%&rXNli}$&VvAa8uZpe0mwu%!WwaH7Cekh4 zJrhKB!;gUFoxl2*krojQbJv3=5|m%gP|C{NP4OcZB5;=uq?@{&R#jv4^wmeeQx=Zw zD=~0M0=9I|k$MPK$(nPP3XTd+JG7Z2c@}tAu-{OF8qV@DNf9_sn5w;@ulWAh-{L{y zUtu>*hi%ZM@pY&EOr4jdY-%BL6|(iTE#s`1R>6KW$=RO)Z7L)o+^W_%+bf0Q5ugrz z69vZw+$quVA*s|1fV8)EBbSmo>X4Dq6wuT{RM-3}ig2z_ZFS8l68AG4;R?s4H|ugs z6CDzc5<^zrY!j9dmvPGpZxbybW)Pc^46~uAwgnZ$-r8>`F09M^(X-!5ta?V_=^#b@ z)jg{Dqk_+)oM9<4JXYb(NX~mK0n80=#-&S*(M`iu0+q&7$*5o{4eiroeFrx@r$p=k z)1jvbAGp8M^I*A3E}v&?=T3qB+q!ZWoV4F!Ja^@3WaNTDp1jI8%k@rYhhynv#QXs= zbR6>>KLc8tqlspd2A7+V;z@NQC65S;+j_k$5AM=0b@~y`W(*$smU3L%#K2!hJyJ*< z!Ib@Tf0YU$+Ciy#6_n;e>6)S^9o8C&i}cE$fvFM>w*fE0kCp#o&mw9~f}Vxhp9 zw6~?1FQ^1S^-gXUT!MN^8ib8vyttMbdDQulHip6`@APe2r#;4P9*4-qGGcg`v&?@f6~c`_anB&RZ9cN+Lq3ci4J3B9CfJN&z9U$4@WlCcarm zFf`z3ffrd3BTJ=F;YwqRQmhFceu!8Cr+C%6>g zAz1|BB`^j~*EtcLUluLRD&bok)@vE;Qn6M|?3K?-?X>LvBks&dcfkb}wa*uI$AeWt zPWQ?=DJ>?udaUl#Ogb>w#8PaEa(3<3bsJ%va!o=sC6iN|&za{HA{9Ptc>c7v9o!dM zSM*fjic{t(@TwX7fV?DN$*n170K{?^{TbTa>6HkV0m{%P&|D1K!#3+^D}(%Se7HdF zO5Rc6a2vC1r%A*g@;Z0AIq)E6YhJT;>t0L)u!rvAqIIw=EmW&dS1wGpQ!*$PEO!aP zb^fuMuM*d69xOZo39C04Wr_Qm9P{YPbp#E0*jv0m$$?PY zr$(wZOu8K_OfMsi?X3gT>1vtpZ?8^h?;Q!Z-Y-(xG| zPQ|6_+Et#?XKYo(SQ+Na85U<1yqmYVatD)0K!d`WjDDY7uO|Y8;MK9(#_?=VRRg_V z9aquzmy?~{4zT{#_zGnu4UQ?O@47$YP1AYj5KyT{hg6(j76cb`KX95*aAE3A5lFap zVO?}QN>rBUHH1-OL{ghV&0pJv$RER^4jCiN$QYf&_tM~&AuH;Q_)7O$8>|*8vn`NC zwIsjPDQi(TjnGohqi4wLUfZ+{EasylJ>;74i7)7Ya69L>N%Vy z3y{;#|1v~>dyrtNjlO&)ZVzm=)WTLl3AIRAa2Y0%tOEKJx}yScKFFFV#w{eG+lR7hi3d#)gHd-{W0v1S(kx9RoB z?T}F}$dm14@7bFeex^y^NiS6E2zZP8(fFT%&*o2`AUh&PhH6_?&C^Yj&~86xdvJL9 zB`Sb0vrti5X{yEXl}$BF0AGd4qb6U$*!$EpzK(C1Ts*>#x7f8`X~IfJ6A8PjVXY< zyNv|R|MDy=!Lr58#CChlqXlhEfBalC-4)Ri`3u&7gX_^vC4N-h#2jp*UcPERti4dd zyufJMzWnhHg{D@Z-HybQ-o3;Ou=d^Er+!#Cv8%!+F8jxq7xl5dzlXcJc~$`7?>&zd z>~CR-ZSz;cBPU`G%8FWOWr>b?Co~PqGriBIqsBh!5SFX}i{tudRSz_p1T3|uHK^<1 z4RhL)>qj>8dH0|0Iqs+Ul_n)Gqm2#wCtt@e7d=`W z%1UPbgd_s6x6<6OHc5<}GB^$Sa6>t(#OrKKZKkb0>h2S*Wm?ftCvmt_zJCeAzn_nVuQc^X!D>Gh~gs%ZvX+a>R3^ zpIZ)Km0%i)D`Xs0$e=sK?gg4Wn=Kbnyiy zTmgOXh(qCGA}>$h_e_C~EY7KS{c^eGtux8{F4kaO-o|Xxw7Z4r_L}r#p5a4aYM8oa zFm<5bFecf(pCjhqV)HxhBFmyY|+L1n&?L+otj-B3uw5eAwnobOhLBWikvyi9v_nhHx8lk6P_+?KGuWM9I$+aJt_9&!cZI7aAgzz;ikznamp{j? zo7{AEkN%(uW`ilITASVa>IRxl`t($)jlV18Y8r2niI8Gu!M@Uyw%;MX=o$yD6t4va z8Q-AW2gkQ|o*ksi?fVL^&Taodn!wW|&GKkIZH7mjYqpQbdir1<*_afewUZ;$3bKC6 zjxt345x5xDuYe$jS4X60`k6&8vRFrqd4zr2*#lF-`2i$@DD zXwf@KrHp$vT7h}XKLyWzhjdBgmrjPXn3wgq-^5E@t^&WbXpoVIQ0qjiC;y{Wp4u)I>Jx=k z$sBZgpjxT8Mv3k0t?$-X3gv5B@gi@`aCx>yy|E|nBfD$gR`;)bHV41z*QR*9ET-jK z^yF2wQ)eAr;Imif{;!ytkDHQ&Pa`;0!1c}HrNdj|Qa&wDOsMdjuYJw?SJQVH_(wzw zJ~$Bc;)aI28aw6Grz%QzLgav3i3@CRz>I@bD3b**^Wk4Ba@j z>7+f(dGO`LLz|~ocb`4&e#Nu>zJ|Vn^q}{*`=})bt@a>P8VhA9zqaC*tz`=LI$nsM z^2|3c`?L*fJ8qSRQK|Yk;V#iZc*fhwAB^kcvS+*(lvL;}X^(Q>_l*#y-QK z_;BDVp#G#hm;<}OT4qczt6JjiG1`c-x<8_*O4wG7Ao&b~RN-}iN5C@Sla7kGIL<DJ5|(y@rOjx?S6WLh)4@7bcvCaUl{eHBmN_1K zT#R%@C?UpZiUawbSUaHVLz94GrQGeD!~)gGxVDKu4yA3|?5`y5(nOhG-rumZfRDE4 ze8>ZiFVyAlWhXaAf1a-aYc_Es5`Idbe4#aiFnSuP!^|cR4gMGt%H)#+uv2b7OBN4J zO`XmRXOcVM#w)uQlL;V2MWawi>+8+lKW>Bo_4{OdD^VMBHA#Ia!m^MtM8NwrCV|lO!!U0O}#=Mrj*Vg62Mt1_Sa%adeYS*H8-f{FceJ7{tn6OZ0JI$~o9 z6?~x7te~2nhEy@XivDx0>^@J0CIhxPHHDpa0x{{n`zkU^$L35pGyGGIhbn1AmZTZr zk_|R8_)vS6^w1Z5)!^Mai^fq0v00NJred!03jE=m6A0E~3}Pt%>)@)-0Qs>qLia-hO)wS;&#kDRC1E98==^pQ;?}Yha0?cJ9o2?`R zb|7XWCF;WLpBVjEh(d2&_OEPZG5*?ejSB68!eF zfuus-UI6Q16SwcJ1n8jobWuPzX{koo4b~-qa5{3up2eM$VHk7!0j*J!IKu6RuY0IX z+I>j%x8w@7Aso;f*yO}@SLMC>N09xE1zqbM15;-tDupM zGc`{0+oqtEWj6^_BvI7dSpULRAy~nDudGj8cPM1-MajYUesgyPFosSpk* zi@tmidu2Gb2)TyLM1BxB@8wd)u5A9IaPPYVQ5suk!aSMh%S~2_IC6!6&acwH1}1{f zNq^t*C%^$=Tw4Xz=A`4o1>zYx+JZ25%_fmftGb{D^+=JzpdU#nwF7gZ%y5hMOWkB+ zhD8cuW3)I;7*F1eIm%!5z?_g(_h38rODAK*_M1_y=aO$yQ(=nt!jCyePkkaV_xj_u zy)ZgzQpHw#;Ou5UF*CB}=<#(KG4hDg#=hRlen?ZaDN%@Z0SO*K4wHLR!gb&l9)JK9 zY<1LTEa%}~>0uu$dB{ss+KiMvgbZEqt=b_el;8}n$ZrKL>}fGy`OXcfv1Yo6h86F1 z?C^ATfP`b=QyR6%QNYHh4h5wezqH5GLT#@;b0&oYF^{T}Ji=~_zZvJKoxm-3!#ClV z?p0a3X+#@OTglcL}xEgO|p|C0UP9MU<~MI zE15qjR=|cE6Z}A_yPT1>SkIbtgHjRF(Kz!P?<&>6b3TG9Vr#!6ssunPe1~7pg|QFV zR;chvv<=meF)xeRSFSmMsQC!IMN{fY{pzq&ldAiUsErJ*+U|0N_pL3==u zkJbK2Tl^xAV0wA@J?jzGxrpl;^1wf3)PKa z9EmNFt5Qt4B81CBv5xoUXf(oCD0YXtFHJAxL0hr+rV1VI_EO8rHnckeE(f-f(OF5j z*xq$ZS`oc-zTET_cXGzuAepB#sp)j>tEwchDP1{P4M($?<2n9M=aoaaXvceb`4rIV z(fY;ZN$z(vppw`yg#pG`JQq1dq?E9 zZrP_D7k7M0PGzQM&D$cr`G&)@r`%*m^a;$$L~c()vT7N`M+;H@y5x~>$n;Dm(W)Vy z`kqXy3K}rV4^PwZ7%;K;3kYL&)H^?bD_#lhCN@v|aZtkI+}kSq3KcciEX>uV{#17U zgzny(<6jv^QR6FcA;DM|K6;rO{fi+pB^QQ)HQyz9&^O1t4VMNe%SsimtUlVdi9PU$ z<7WkUn1SD|RmU1_)3nw2WdN|*vdvni{o35W=2-sEpDRtTPZpcr7)xnAFH!STXoqxz zhU@7XXtkU#g3}rUDR{olB&iou{A%3J$G?@?z#pI}dvAP@H)T!ULH_7Y{fNwst5xy( zF!w3^9NfH}+cuD)-#XT*;L*e(9M*z{J-3^`pQW$R(8)5=D zN&`6ECmN?nrPBUhr@A*6`xi=(s8nJ8q>U8Kq{poyL!hJ4MKcxl zF~-KmAgOKe`0{tHcnTv=n`Qz7!xlD$yCq*9!}m~w3rgPJV$n^{ezud}|wbMRIL zm(zcG0eD(8A_wObI0BE-KC*}7%iFHH)Ak)-4bZh$r(nTMJPRy9Up~;$ehZ~a9Y;2@ z7_s9ZnK(M^i2P{)Z0VF`#H%kksiOm5g6aqLJ1F|Qy>*Q)KHCFuMLd=Jf*r4lm%y6wg1ckvw z*gc=Dl}Po>k{~x5o4;0p3_ZbcRR!n_7@36Z_F40E%_cmELH4koPEpAwxzW>TB5g6O znBTAc!f76Q-6APqRY|zp_Ee|7txl|_i`>ouelE@GrG zkSI|#pBA`Y+-X(Skjz0RWK(^cOrW1f4lqlSN}!lNh<<|9CGAK7I*rIehX4J-_pYSR zTYf^2e54)t+6}1rRw`8qSqZfp`G(#GW$+!W_Q$dF+t}qq>3);4b7mV68+S}J@`u2R zJ84LH-;R-F9>Qmfuna7#%pMT@HjIi-?)Z!wYZ`6$IP_ZYuGQU$#8PD4O* zx4LJy(c9y$gna7MyXP=+`j}Dx>w}WMk`4)%6#lpSyQnsxM=$gv{bx1elmTU6T2x@CgQ zL+T^hk@7lLo?M+v?}1@imE|mkG36v~A1cdTx{xFqRCiqqjk5!P-jT}W0I?=QXE~vc z(RMjY|1yABA8O3#2|w+((mxggR4V-O0kZPFoZSY7hIe-vT7api*r!HhTOF3hd$}{w35#@nuup!gzXx{zMQ#TZpuUd)lY)Ha zn^DbG5xOL9RE7@`t&Z>8a$wBk4~f~UoY?HZ2}?n>XSJQKS-^}8=ot+@6Yg3{!5&{z z6>z_$?!N}G_DqBvo|P_>74E1!T(p%gZ7N#7Akje(JYX5Pk`zgEau3653mac4uCb_% zbiJRQwmvhuSldt3s-Earw#*bX3eLE0wVdXGCXrkK*8uR9b>UGsVnfjNTVo!3# zt17L(XOS8>VEcFx>_fFGy_NCAY?c!6)C@<$?&)ukM| zCIN)UhHRVtpiED!c^kK;qS$k6$h9lKc0djk@Zb&rmI0F*wFx6>rf85~@lpgTCi`Rs z%I3t>MJK}8x9X8Xi1iN}PPrccS_p=@Dc}}P0$5txT4%{mvs`%N$x};P@Mc?4zOg~D zL2CJEBgLZpA;u5Wf!sn)B5Nu4hg?$VaGUv!3=QJp>odTOpT7hCWmnu+u86^G5m#se25jM5ga=lw=Vt z6C4ppA`}oX0-v>pQ4o?!z}<0lz#7sWQ4#E-*~IVD>QUcPcQibZ^dX6qow4J(luzP=Acg-z{Z;>~iSRA(s`2ihg8`LF zt72{Xwp(6;4!q{z01-+$4!VZ1e?Rb~o;DcZvmmNI?Gsc$%nfc?n@o_NwTFlOPi!)* z|NoXe{_oSU|9?h*EBpU9VEuoYFSmzQfmiKWS4Zx^N)f$GmX)K;BqTGpKijU!VP=`f30J_At^OrX8%$btV`8Xl>e6 zUo^fG43p$rV@=#ss>n{9>GtEVL>IbywL`D|$$sPx^x^3qcK0LdbDlplLmnVcklTp7 zK#KoF0J&CI9Z7;8@XaeAcn7>6{z5MNn4{=+~6ldHD7gO};>kA{sq@J+iPXiyNYKa@kQ-2O_EW9|B*e|PZ9S&)UqkK_DvosmtG z3atNGFD5(iEHpC8B3?EQyg1lokN8_bc)**sq|dyN?vr`@|iY?0b;*+25m^N~~V znTWpv@)9q))&4j&#Js2?$u9<7vL5bf71cBr55nTL;5iaj^jrZ-FHMPBo@h=vpeVob zBKzSTYYn5u5Nm(}KEzX1ulU zLJOri$wBjJDI9@zl=Y;j(AP|-l-#cldMiyMDbYsMEMONLA>Nw5dn`!HeCf%yLS4@Y zr5bs)HFt^_BNzjq`3V+kXe_TK&!Az=A(YsiMxeFi`qXv~d8D8er|%+&{gf!pZv!Za zng-$b512-uen-F02Qtz;gv!WKG$2mDq2IUDR6l0f(Z$5(;#*^q;C65h=>VO-Mgq;(>#Hh@8U0#6#cS3;K z(; zzs_C0edTf1ER4|Ax#n_nmmpoht^3fACrAsp8F%L8jgPp?4Ry0t#RCFc-tB10eSbEn z$t$}?<^bvmh$Q+!i8=c!8>N-S-x`&;t@DRsD8CW#OS^qyd1<);=N&pnZ9MY z7l@fsi%_&@v+=?7-Le$x46v2j)93)y*9QuNkT`^cZ-x(<_~_h`*VKyK-D`a^0+azf z^1P5K@FQO+1${&jv7YnW*Jj9Q61HWA zUu|-;O=IugIrGYPgG!)@ zJ&wcaZf};WZXkRv^>sWDTFgce!q|}0J2sPH$_l+ldzgPhLHjFH*N8BrB*xWU$Ea1y zHQ|tX185mHn4iVy8C|DX`9&I~5SmM8A{5nOx$pA z0*-*5AZ5yCiKQ>!ML=OXXMHmU9f5#?%Coj3C_e#5@mZkzyu$Q(p;e0){R!yM7cncmaJAXP?iwL5@ znsWts)Y!Hs68F0r;r;hd=pJHfQZeREtW1Da?YT1Fv+GR^;K9$5+i5GeCF@}LJ{Fbw zzd$(xx#5xEGxagq3(9$gDu@}|F?q$qm{9g7!h|PKs?g}SdCMG}1k=Jrz0CnE&|4=E zw%M1a!@Gi*NIu-;t5lj_zmJuvGsvfh7G)>9u9n&Ru^@c8*7oeJX;S(~CNegX`gWkH zKtWj52W`Uf?@HQsLRowF>c8$07b&jPF{}|!(Mg(Z^YW!$!#4Ac%qd5|pc^?~Wg=;y zOM)GKB(oYu z&4{4z&+Rhr8TBY`pM$`3*szN7?DzwM$?>ED&?57RnGi{CWZ{66}vF7}aN*~r`T!0%lPG+bxra+UqR zqNt*@;@1W`{=8H!p3=Lx=QN2Z&pN$?g{@5>k`O8P<;w2sNyv9?gV&z&W=AvR|xyg1+)^^yQSedND8 zPl|1NZ^a%0tbBP^MB9(m(*EYoQ>>+9=lbFRlZRpd{3wF{%32?`MKAYl)uMT&(md5A1AB+ z4m`qtY9iTQ&lmr#yB@mbg8ZOX z`{RQRfvJIdE!oc8C!S>yZR}toU#PmY=t%95w#auWqVvv+J5UPo^_?VwGULtg_z4IO z@`a*yc+{7_wa1U2Pow8fJlToj{^{%A3JE8Flqo=k^{IXn+dk^tr~4rU&KnI+^|h`^ zXFq+J&hAnbF+aGdCs(R*NCJx=hLmX3@N`XKU6bWQv^%pq(I0MFgS7`Ygtk0!diGGZNGHQNhp}?KkzOsCi9iz7&AWYNG+}^Wmv@DCy1;=rNo9JW;=I+ zveov(kOD~G7a3yjt}uWRoyA9N`wDWUF(!Dv>tZIv9^!36Puf&ly8nF|oK5C?XQobQ(uXiC`fZv?dA%4l78C}Bz%+T3NLMaS= zA;fYx2O>t!7j*$geU)uG;y~fT?=0fBgdJ=t{q}ilztL?fhWXlsez!M`m9XEVDxDWa zRT6q-K8N(9wXK1Bk#zC`p^;W>6Y*vS!L_VAdHb^FTSSS24CBHlKCC;JgU@XxH0t8} zvIzWnU@`n%Hs@r=e%>%@jCnR{U~Zhl)PSXSHfX)_V6<^E%DCSyR;j(lxzxNw7;Id5 zIO!@GfW^3k50n&Sg-xzd#DA!UYqX!Yq@A~Mn{y^*FOB!f9XcJJx%Y9+2U=+{F!^U6 z6GGMRCa9#~x4=aJ7d=EI-~Q&!=%!a=i1w_5YM0sjXx#$Ay1lfOg@RpOK1Gw_gBA5U zJ}p>9Lqc9KZkgN3Ihzc8paNbcpvXNj5HbihAi=l6=p81tzSy4qXl(K{DN`v}!gE&9 zu`i>Ogd-#_(3Bc;7o&mUK(5}m?(?LD(nHAysE@gralD%(C0aZ$RYVU{;&Z)EW{+As zq{b3mfa4;Q>#8;Thl2S6a zc|y%1b!V2r@3D723g5^uO+RPN%Xrt9y?Xu(v?>M$qy>ZxX81;|DE~OarK;5&xPX-t zm?ere>YUx{mkW8oODPi_^zXK7rzg#xrzH#QnlDsEZ9ZMB%{8x*zn7evcjmk6|AqmA z`NwHLsShm({m1Q60G3$uGCvym$Ma&4W{^^~l$| zJQxh?ktgTrr3cycN9k53eaM$Qv+Kq~o>~SixTN_wOup2bw9#9rU(Co=^T_Wt1G4|7 zm#*?96?s~3+WNS|-PZHjT*a6)Jq4}9bbMItYd;b6>gL|3GCYhE9NiGMYt;OF-^nex zk~@;F9+yr=%rmd^{sYP;c-TS9)awXWdCwc*Fvi8rBYwdLMq{VLJyg%?8(~C z)}IoiZ4v8dFORVnC2ky#AfFnjtz&`ZzsCQ($yJ9QI;CDQu&5z%Dy95SF8~QvEIxtt z%Zt@-<*;+tu=926>S^!p+7L%cVoGMzW<9#E!D~VGaNI8K{_1*UwhBLlk%L0mZH|r- znTEQYDy%ZKpeatE78bn5^lO~#m)*qYg`US&TiolXwbewy?!D!a?;sNtIli0i=BK(( zsZMyj(i8f#-))sPgjvMty}WODC5O(3%X{0;9|FgOmYV}>avtBuYay6z;(m6C&gr{W z4qyS$XRo=P6XRbRwGb_Amrl@s%d3>|J@ku!-Puhpxez_tvBb$DHhgF0?8E)lq40qm z_3DFBiyi z!q?VV>U4P;(e(3JK3fyD1E`b&fQIx!nTOWzFJH7kNkz$eF6@^EG{t;EZO79}(@)ko! zwOFQJn=*y4V41NF$$PWh!79nTYA@G#KlDA=Ah1;DL0oDi?xdwGojJg_Y4TjC`NGOu z_AcCWch|M|7dJ^OM$KKt{WeTb_g4**(?>=|yk-Me#92=3N?qDG8ZAw<7`E^mC6_K>-T&mRFG9~jn%Xl*=BVz833r%_v zd}P4h;YMQlzQd~T{HfDHm*obkRoHE~I`K{B#+>>x@g4U&KTWg+u{erDe1k8_Q8I2zzWDS ziQnK_l~kms{3O4;Fq2ObUl?L)J~*8+j`;ljM5KZvub*G(K{qJ|1RoM8Q}p`c@VwR9 zUAe_mhG@&<*(EKqi>N(OM|tx%MBr<)P@+`NnB}%Gqx};PDTqBOU2k+*Y)`WiNgnH1w1_%4JDPLW$H3bpOcxv(S4r+?^eT z4Il+-v3#;F3E^YFvXTW;PWQ}ojeqVuj2F?2@7Ms|a}EEYf_bf|$MIOZ0e;-H#K=p9 z-QP@6ydhPv0$v-q@ z4nQsdQQax!`~7>@frHubNc!yOzt9OS(Wb77WX+i!p)l}8W62UiHDYnMTg|mPx$o%% z#Uq!_Q{?SzUdt|T0fJyD+t- z?+$pRldiFWZz{F^5)NM>r!32 zoF(LgM_3}#+yy*u(RQfBtGLp)h@H|3u?JV|=5HR zO;UcqQ=iqhztNeRc9-}Z+>n3je;p0?Yd>ee}>oIMf@-YLg zT6tTEgF8qxoIk=Tzz3d*lB{k;`p2s%5X_K8*|AnUjtXm z6Jf395-I=uH1q6~Ii(AyX8hf9WI&fU6Ff`Vti|(+gr@)ThhXKjt&(}wjRtLe4r=m) z)AQDH_ePOBr)#sCA*Rz2zj0HbRA7k4{)U@}kms$b$A68a9G^q( zgdNGDNKXADsH@*JRK0k%yS*5j=vT=#F;>cx`xRWMZ=VBZh7E1UZoC@k0pNqDHWD>CIZtB>w3OYZ-}d-$Fx> z%`1r?ao*G-{P3x4YkCGcl0{h$UrZ`j84NhEtV>k&lUfkcK73QY+AOnoLoPEGiWkheTfH;@6CKz@w|7k z2XNG!91$A9&`!zQ>em`Wq%1Vw{6A%NN= zrvb-QJoo_{i2MeYkpC*Rkf|HzG6~MyWl?MZ_xbNB>fvaPx!i+W;)$*ejSbu)fDudu zWthBO7bOS-ZBQthWV?pxmV9oJinUYWEDCNhK^_#)(2rPD2g#-nsP)eY@V0t1wrC}m zE60wO+GbLm-8d%NLBDxtPkr?TIU_GGc=!X3w$WLAAB!)={c@tEHPIW^$inSkHm_WWbNimstxq=d+ZSj~&=TKtz&81KpY=_1C$UYmly zP!5Rsk^JSExzY#BBx2_;@{G=@kanVvRId9T$?&zeHs}NK8QU@az#~!LZGW-`6Hz$ejt=NHag?8$U%eKey4%Px=JC8;yqkLjTMLrVt@LaS!)k_Q& zeKE34Qgfydmbv1aKj6K4+TG0U0xG&()Q(RqpGA(0P(6w-%GF!@@xisstXH+B=0hZeg3hpD#=i+YRNhbIW>I&{MT zN+Z&xGAcs~l9GaeG=kDGqelTH4ny}5L8MC>hEWluML-%xkOt|lcaP_Jp8xy7FJ2e3 z_iydJ*Sgoe?!C5wP&l8kG%dKBQxL{N-{S1dh-$im%vBlaItfD|(ETK}Wb$OD{EXNQ zs23w@q32}sPL-<#g%CO-L8EkwKR-?)B@OxP)Zn^;Kw&G{)`q8}z2gQe(%$dtrf8|y z-s#*Ak_@N2QDuX|VCnP52jFb?*ZB~I<3YPihU@g`{MpNh_klvR#QWCRy!{n8p^S3I zSEf>6Z;NuKPAy%hWX<$N+Mi9M=kn-LZ{D}>7R~i2p+X_j%yG5#JudtG4+s-&wceC! zaQnUp_p*(AXgH>K?rZjkYUW_9u_%Cfg?=GqnTEtswdG0l$x@Ykonke`9DbAtJATE9 z5{8lnZFs3C?_tr1D;jPLj@w`+<9X})soQ*;c4c$f^;H-9Q6zJs^kDyRG2yYsDmYkK zp&vcgSco%07h;g5T+{XDZ~y5)k-Q)K*r9gb^G$M) zZCY&c^=Vv$IQ68)k7r(*%;<2L!aQ)9FoV(v(Gq3UqC=9ywAZnSr0hSvsEl=PLtDvy zoG-nNV^a@N3Kh;2Vql$F@ZYN&Y?jl++b#A=F~ zhuU{W=rCr1#RT28SpLWC7S~JiT9^n$#bvW6GZ*S78?E7EUaG*6@SU54V(&{#;Lc3=QZa(uKnp?FCLI?58Z_+as-HV6lS5r zAsL7|PV{F?!+hjDqTxObx|I8qWy60I8;OHxjU>iRL?KHk=^B|Xd8(WnOYmAHdH7Mc zt#(ktm3!|hru-%z&x9W2)6tQ8c0YD?7C371A0KVN*JH_uxmZn~0mjf}VwC~)bG?`e zG?fu0{nOj5&{PkOqno7LwXU#(iev`}98CJs3EqzWH~ixKmTka5?k%gZqxnqVJ|&QT ztH@2*hwY^4X2$iT_N~EexN4)WK&+>o3kv#vUf<9d;UoeX4lyc{F6FcLz6` zVjI@kt1#1Zi`BXfST6(TCON>m-p-XW%25wpN|E>omg!f&+Wz>MD?Y%M$FCHZ_fd+K;F|+?fcp{l!W{{hH*ZtYsqNA`bi_KIpB|-+ zgfl2o2eiCk3xYc{p=R}6<}M(yFqY&6GoqxuYr#)|B7}TX?_XL)?n4&Pw#a5!0C+so zKTKA}H5bN%negzM&tF~JA{o-ez5zf1?%fMdA`>4?tJOGs&qL{^UyJINY%2NY76!>A zw}OkiETo>}`#}dV=Bu-v1y+9!N3tpvwLsBmp7t`=r4ouYc!*Jc{m>0i?Q2|U4M$zv*naDqEIiGW)#k*g>X86n~HC87(m}fV*Qu9 zF{>Ckv|d!zU@$C0j_ET4{t&hyhGLfUoKy_kug)P7Lu$Z>4Ab7gzS^Gj?XOvGwao!} zs#S!pi0IlRj2ac|L8F>AnQdH482b6tsnfqs>3qGe=+71qxxLSujo!IO+y;eyPXxQ- zY%W>-ou7gz6q<|r-rWJ8fT=K^bssa$rw7xA$MwP9LA_uGFgoaloNwOu6c41YN;Yt-FnskPivC|qZqPE}c19$9QQdZtqjS)&X|S!x-iQ6y)h zJSD4V&`KnzV~1>dMX-%@Nrd~h*JRICy_vmb^~|V=wWb<(3U5ZkpH7G^@dj3J4QcB7 zs-QPuu;f33wP^O(OGH_Q)j72aJvml}hT!$ZreqHfJ1;~B=KJ}NH%`;czmOR|d-7Lh zl?U6VZq#oflfAC#+-LroS|b&TA4^YE><{VJ}*7|!yEv$CQ6)q9KU;R`jq>eky|@9!J(bXaK` z_a2E1*yPK`)7$VKl772ey9C_2A#gB89s!ukDvcW6G8Lvirz%L;tT4e0d1Qz(3d|(S zgou~;@nMH3bCq%4E=P_E>Re;xY&jLkS$nz%c1c(6?9 zZ3;JCk`rADn{8;Dx<2!36opU6zrkmUegV;_>V1D`B%&}z3sMK=WGsGm&++zZl$50x z+!@|&DyWEBf#5)D3Qm)983q0ak}5GVG%9M@%~S~7Tue*?hq_43Hwx($o=fhn?N1QT zRp^H%(fz6YA!>YiVb3wU-!Eo_@86>-(MDnsE?53+xE8>iu3c)z%1_g=(L|HVm)`tO zAyIgiPIsF4UO%}?d6TWe-$VectKJ_Bba1(X)LU^AHGX)2VZ{__*QXezWD zMsk=^8!#|^0Mpk<*}^_YeeXZWizz2LWlx0wBvlVS9xTntE}}wC=16t^5kcd6e-RS= z7r8Tl5#t347+zQtq`V-;3j$gl(v;l$zdzYwsh}BnC+f&bpdeTdUjkR3Vta_AcsXEt zF$tJ7mpM%|3O|phG>QBllS$b^nqY6gx66}dlh>9LP4@II(PU?qNJP*G>1_~y=DHyw zP%rw>XQ2lFsiaCP=@p-)Mo$03Wyu~uub4+P!O+hqWdK9vy*lDzBRkDnB3D&Z!f|-Z z#gp*d-u(^g;6^7yoQH01k`>*BUQ$ha?cL!mYI~naJD^Q9SIewu;R{JeJlMSN4P2@E zC1k(J#Hl7ByYN0J<1m#Tz?}WyE1s%KK~?D0o=Fu{vi4smJSN!U%h4_-QTo7I^FuVF zt#Yj971AHyN`)q=Uz0w%r+m@*9)_i_s zy4a^)^-rGPL-`6iZ+35cA9rp7Dp&5~XOmZ;@04FBXvvxPq#wA^2sm$k%myTy+>`Hk z|37@oY!5t@H+6-g(hsV7-owC}X8=>EU4@FHx)S*qTh7qF=p#_+A>1X<5T4kL6(L24 z7QniW$sv~)Ejrix@LIV7Y5m1QC{gI@6%b)$$rU;4X%FG?r=&}$T`F}-Mbi2Lj*ox- zDlh%1hBR9}r@5?>pV2m86($)c9d6ml+NtG!F`@fcWm9OY$3b*cc;L6|0}7ySG0GsW zQet;7GMvx~4JRW*bc_h%-p2?X!&_~VfR7@c1&JWiQObHMwnVN8RyJ}VbGzFnM-Cx} zflvj@+E#Q{-=y{?_g2{qV>n0x0j@IdCE|*093+G345OjPIl~@6QO~ht{lCY=sy=gKQ!b7glci*g(HiY+(-P?7W zrxkJE+Jk&oz?3V8Mz^cJ1K0LymI>Wxdg_Tqm}C;mOqD?(OdgQ?32Mt^=_hI_Q=9Al@mTcLkD$p=eF4Ugnp-V*z)sIh zhKJqURS98Iq%toqVgLzIeJ9djrem<(Pl)kDOTq+ATi4Wjlu>W~HL}saM&XC`Z6nEu z!nKe~QYg`OPnYr$2*_XbJi}?J;W*7fDc54S8I$bOy29(xz+&8;FOg4^56~HCZkPLI zKI-0_m9IcVxWB11;Iq;$ezL%jB+(}pn759h^So4(y`zKDf<03;`i6@8){8!7K^!0Hzj<8aRrdiD-OXpKhdA0?XUxR7-hzlx%d~m+Vch|JWb*13 zHZR?txG0fRk1_)@t}>@uwbd{pbL)7$X$S7G&9*GxwRhTB#$Ua4OBC43awW?o4+c-PH--UYUobjKK%K$ z*Ti4jHlaJ^>pl#(eB4_G-q~^IyR`AGgUpm2yB2LV63;w)H!_+*?&Xx(YTERG=6DHC~$48 z(?JNe^5|t=3oh%CAb@+@4~gEr+-6z4*TUYc!ZJykX{ZHpqtTW;JA$1sq`)fu)JEt zl=5g?qI)#-mX%1-shV^*ZW^ECqDviq0}-TC$!e-mqBr{qPRRql{=0+n5 zg-b|6M7j@KNNoye55+d;SYj<;s@XF8WR4@a{D8i_=3&K{B<1GST zZqgT)UM|_}_fLFerJHU(26ofICG7`sNEbXi_)WE@rz_8kG+z9wdnn0w%&0-7N_|S6 zYVnax2-Vd|EYpRob$z!Dn1)V^5;&gXZP6Fqk0gQ^AlQa8BsI6Q4yUZcH1C?E$pI#&umD4k5G)}q(-YV*K>`=X^}Zw zt~ZHSPagZO$d9hHa?EFCux*#; zZh%p3ZyG}T)+Oo|apYA}YEl~^?R&JOjn3eM&1sr%Z|6vvT?G|YSL5Romj<&RzqOlF zX2{&{v{(NvY2Em(&dXMQ>>-o<_#;73Ak?&;GK%ADWK8O7hx>nfLRyVLnLHrs#U=Of zbXC!;M(0PWgv97dsVHpUMK4?bK_f3Col!YI*2jmm0&yip0&ODO zz5t!uR=r5Uv$H!?M@k{5BF{fv;boNdqLSemTi zZ%h1hJoK90xje)ns$b9z`dQ{k3$dPeq z_Z~95Q+a;8DwPC)*LaQC%u%1e;`)cdfaB$YG{wgP5!`;}$CcI?2E0Z>xe%&Ik?6y% z_P<`;myRdtn@8mC`ZhLz-Lr^Mlii=9LQv932ljd3Z(olO*8E>B0A9(Zau^!HNaxm1 zmMk_q4g#pd3dtIEw6JiRHU7I0_YAot$q%R>zj~Z_ksXMDHFs>^wfwf8b@L)qCEYck z;R5Or-L$JOWcyVUlFV!x2vnZ+KVL?3$h~5<3BUlEiD>8u2ANUJe4ibxF{vyOh6-6; z%vgm6LBq&Sl^oppD|lmyBV?KIM^<%0f08}IZH|P_DNHq*qU{woSm1?{JBZ+d!TNE9 zmcQn23Jf%{R>)V5`*VQF8oztjViOmQYcBm3_egV(20fo*n;m{}GGi6IUUN$*OxB}z z%t`h9a4yqFJAu|*1>z6uu01BhBAAIPszM<#ru;&d8@nw!7uZ_*(UkV=*3c;C zrYo-_zejx;ht+eB{$n2F%T^({=`${K7HOhN2hFIRwE=^NS|n@js$bF-?oxBE#ixS0 z>aZ#KAolibHG(d9yq}`HfH$WlIy;FYsNuQv6enIqBD0z|{!}kK&f+HG;f5Y3C-(7l z0={SV)5Ei0HQ~rzdR+Qtrad-kdPS~7#r4B1G;2afsnIVrkaet2M79s(-bP{jmzhQv zn2JOF`N7Iw3c3Fic%79;3yd@C_i$#<+-ykQA>ZEKq5Iu(U{gD4|C-TCzA5M8bn*14 zU++~eH|^;McZEd6mFGkhrsLOr^~d*>nNYOlqx-^@7;}cx{%8kT9UWH7^9GI0%Q3}I zl;N5f6!K<0v>tPq>(5`oH&@?@uZT_sGdih(ZLx!l^{IW0m(x_o7QPctjMJ6ZYLh|2 zIe(xvzS?3LSUQEtfq(+_R$c2^%#G({HQU&REKmsM0+i^orrF)&>~<{T54IZ046+*$ zpX(KJ^@nKO2)AzXa|C4L zvgRFVvB|YdD5Hpn4LT?z->TA=p^@#3T~{obik~$(N9RDTCVOfQdPZ?6pSncDo|_*v z=_iNg(05MN`C2)MS15TSHpw}$fNZhJAUs*vutY z3@_QV_9<57H_&~l!eTntW=vG+!Cb!saiErW-<;q{jzy^*qR>?0O<5Ut?ThYYNT2vc zGy5iQvJJ|YXR`xVX03HGGmR7o`Ye8;PUH0ft&rMeixg!QcreBV>s|uA^w_L zK6*!iZ(R9XOR#^eO4;bGwo?DDD+PIsc>1$QJ%Gr%ik2w}%E|mrptB3*_utso7WBqL z_6x7^RcM>&c+YaRU}REt?^xnH#|&0Rim8y2)aVBZ;wLx+WA}?>?8~JMh3Ly>q3kg& zab|)+Bs>X|7f1$>f}rN_8R(vKP_F6hi<%8$!s$1b?}@RdgE*F-)<&{p{Iv1`J`3*< znB96XBDFuA!J`(GDBlQM1BCYO@2QM_^%H9Q-0?b$SrN-Wh{7qNNa0F`byQNtvherO zdvzK>)#TGIGm>CxQ^=kM02(>2OYq%qyc!-ae&Phx;0rpeX-C*85F!Am5|?SYb+s=T z&GcwZlL&4c^z8R~`Y|Y9OGp=KtwVFNr&d)?i#$;4-Pp#)y_NB)kZo+!v!#qCewnRRueQJU`nMJR z3x;0vYraerstjE zNoA-4{V0g2_I|B+1#9K_!O3;&WQS>laNZgBVZD9)aB>=+y|e+Zwpd=RZC!A@iwe&W z3GZ^*n2gXe@{l>oNO^(h#}6Xsy8y&S`|}-;l$XzBkkRp2l^t%^uGdby6G4-k$g%)m zHxhU>C7fMbSE-z zt)6S-a{En!4?!L%qz2k@^b|ld>4LV;gPblAa(F--Ysy1n`PAJ?u#Oinf~0-ThUOto z9Ay-|BLGy!SN&l1S(BYJHV6%clKRav;=bwF(oc(i6HUi^;nAsgk|WRDCJh7)~39%A!`=q{d~zIZgaOD3}{mQtSM zR?@JLKL4taFZ5(zZ>A*dHn@#aMy&5Sb|t%qrkNKug|#1Q;?>)_GD03EhvN!fIH)ib zyM?DZ&Ikw`1$!lj&TqpEp9O^z>^V+oJV{kaCrM5zyY;lOLmcp^yp1>)_mBU0vUrPa zvHy&sY|p=^4Lo!5a#t~!~=C#vJ2J|hX3yXnM`r_92k6U}WEDcJ^)Gl}9zTiJ1bEX$|$#8uB zsFj;Sk=SnsjGfbiQOw$Vfc3`Vh44qea{dCPj<=Ta0sR`u3nyP=MNCg7b&Z7$nTNCV z;JWFi>)>LW>&M)o*I!=mTroEdi$W$yGS`;;gGe$7tpTmMYOaVeh~4)M%r@^;DL}h< z-9k9sW1$y|lpSWE3kXkloM99gINo6SJ(zHrLMcJZD2LA7z(qu@7urX5%p~@YQL@M} zsR4xc^3O9cP8=D`jA@;v%xU@8MoOFWGcWjaY`NY!;G{!B&e9$%^rQ_(&$(4Q^}1GB zl!A$I|B3O9y!2vEEw@E0wd=o0i3LA&j}r`r1(J9hCvUy{by%wOdq>5NS%AGsLRvMj zoDOsmf^X9)6&#>Hqq8D4QWE#j19)@01jcCtK#m;f*|BYM4A?xltf#{#4>5psK#D~! zjzj^U@**(jm*|Ac;JblucUNE!UIZ^@`F~d^FcYIi8FdiJrNrN|_RRXe(-1%YE-_Zo z>Whs~i`XM3@0-b6B;H(xER8~|3cl)XD{~L7-ntes$oT)E|*@}DEgKQ>Uqev|MN z%rkds5(_)+Q}-3i=xOjYZ8ZxGck9Q}DV(VPlp>5m`2{(1D@xg`<;S9{6&AoAcMM{LWT5+{jT;@o`u47N`_ZtX{Np3qhT#XTUB{ zoVHQsTh}m`-$(%8@qv0%6L^wq1eo1=w8ONZuukaf_u@XB74<&+V5`v1#t*#)!O~ck z7HpW$aBvA*eOJr30}vpTe^5^v|ZR>^5-9rH-n+C>KL_y9pTb?Q6e_BM<^aEA!P zQt0Qi$rOFFvA$F?sVz$IYrZnoJm2(eDP(RvX!3_BWWJ^unk;`cJ79t<;~jM9i+ zWq$i|e2@5#DkMzFS`@09XwIC`%Zu@~d_1E&hiO5tyEGoN+1(<9*;O-6OF6o4H;TYz z2iqec3FvMNbC)-0l-=c_BrtHox0UUuycq*5!iQ}MjWp_aun4*eVzrS{$R$+>Q41p3 zdb(Xi60e1*U&P_zliB159MjnyZkrzB>B7UrhHC7b}oiH zM*EX{A{c*^)`)D~Rdr;DU$RbFQfWpg;ixNN$20=$zfY=w7QkJ%WFHWoLDOmPHWqhW ztk#?O4Sye1Q+5DYL*i|*^5tv1VBo=&zZegmMqv`aTHcyHo(Z+G-Qok5ils{zLd_>z zoFB;u+&EUKYNd-I2{TzpU4WAhjLs=KG70cc0Pp!dr5vu&HF}^VO+CuUmLgBAyZ*1qlWkG?cjK9c@S{t|j z2J~BW6=Ou|yc2>POb1J)So>LYJ11&sYQiIUf-ZxSHOh6#gOJOjACD+}NIXAXF7Vv7 z$BkJSpw?`OHFVJRAjPO0W^1Mx(V9(X_T`ZuLO5AboH^wx++ zsHuUw=bK!cJn^HwBJsfn$(1!bic>$)Hxra+TYM@$c9>LXgeQrFZ6BXBz(axk7ViCW%7xs)rQh+)wD=-ied!j!71` z_(UO+i6`DNX;`(lyKdnriZUYmvvkD6+{=yUM|8`~SBS@ppHo$6Q+0$g9gcugy!F>#BgFFv6LN2pj70 z2mRuve4;lobA5MP?Vm>m(p&+>p8%JNsQF=UXKN>b|MY_DXe%zfXCv%DC0V?4@i8(? z{cQIhxRxVzJC-X=Ju`i2D&o<}2e(eo%P6Bi^l(>x_GGC02AmegG9TERf#GGQ*W|9y zh`)r()}nU4FVj6*pj39EN8l#hw= zm4Gi*DjUosiCX|XTW$~LoF~Sq z9|!UH!?#p_T_zaXaT3;l;riPC-qv7b8Uf@jU`h9&o&q?CP$W(zjbbo)m&8f9U+;pw z`+I&W6n87p=3Jm#i6XaWe!A0vw{uhhZvO6txx7f1iF1?ZiZg|D1$ss*C9F-fwC4oW z*Ah3?K}>YEAiCX{jn8QsUi3__OQb=TrTj{nU~C84!NnZ!t)?h0=Or+LjjOMlD$T$3 zh1C{l11oE^V!Ap5;@=FTGD9M z8-gdl?j3}OJiI`zEEdME?ic&%f~rV1+gNzO@l3&R>>W_#bJt9?|7}PJbgtTEEgEqd ze=D8A(?&U7UX_^x$?m@D=Md}W2i4C#`sD+w&;Q4^45Gp?Un0A4z$bB_1nv{5ax&&; zX$YMJ53Aby4D{7+6RYi3Ztx4l0dgZG3@4C%VaUMzm(gdIPD;*CV%;QRx9J_P@dDY<8Higp z0QSsI?eu_2G3ogPm4g!~xt7f-3Iu=Lh_)K0q-Le_N6!##L5u(R>jq$M`5gy^cq8%@wLSKDo% z*_R0%ynyAo(bhA*pvxD;yWn7$TyJDl{C5GQ1Ddtn?4-^DAAzhgMJ;_{KM3JuGo20A z#*Wuw@WXg={AGLlyNwoY!5!CUe=fX03Q>XG^g;p66ov9pfjGnc0Tm$?=?t-j=0N)4 z7g{{66kG0uo4NVM>KtfWmIAQ=!-+i>64_&pbLLkMa29@ zST4!#N$0vFCL@aZD_}BviA-i`#?8e0+I2KzOrMWNqt?XG22RU7AA1}APCd)`eFIdd zVYyER-;0k-q@;3(gy251*onu3t{3fNpCOvqgJ$#+4H#LinBgp>01^ji!lwLnFTQDV zS`r)z0Fb*xwh(KaX$HhAQX+M$kv5-3U|Ge?pjKB)gj+RFsUnj~mgON~#%(n}Am{Dh zJLd*IOmZqt(V=;rq9w?};%m?Ny-6zM20n{S`Lq>H`IrNatwd*+Tz`*oMH@j&tSub= zYCshF{8w&sX+vf|m5n_@FDZ<~y(f9hX;^y~tVWHa@<98QmM?ZV`W*-63bt$19s; zlMYBe0`Y}`)5U+;Ac82?Zw<~o)*+zmO~%rhqwfHpeHBzmJqgx;6rG87cn4Xe51qPK z9LoBqdWSH<+vj==ced@2M0RfULo!UnK-^m=?zJN3J4_V4PsCsf!X0${$2{+LA$}q} z?=-ac$Er4%@@01_uMRvH8MMH&24-rIA4+VRW*>;k2TAcAM}^ zqE?pV0m$88O7M9J$@;!81;ipv^h{BGWYfj&k-!8+&$=Oiu-%5MFnMJ2 z2{+D=a^0%|9fssS6IVSsgkp^{Gc_bj&){EHJ+r%xJAZlmW**!9Gxu^t8HJHCiYdJtJd~CSefHg~jU>xDvWN!AxGIP@o@xqdm&RZ1 zKCVmQOec^CHKAG;=jCptV#HS(p5S#Ry z*8Z01l_zBTa1auQdJWp;$pe_#ZtQNY+vJ`=RjJ&mxM)K%<#B>QLJQX^*GfPzJKeH} z4ZOxv;Zfq&diJW7uN(w@7b5^A+MHFBy?iYe~IPSeE;XLhicO z%+w68^UJKPEFb?x{jsr)#lVFJ3w>D!xD0E*jh02V#V6ri*3=MM;j8JalAGuB0w)2w zPB{kXR(j}9L})~}i(pmnZN`t&&(UL%r1nh=^VJ3dFoLX+Vm4xrxtIwvV-32xI5WnR zhOlftp;-hjCU0Ch`qK0?BqSv^8#lGbV0dG}3;CKpvQ~h~xxL0G8d1mu2MaM>mK1B} zg|m(nAF;B9+!5vqmM0{_CJf*( zq(30F{mlvr-U6H9Nbf^iW*!}E`q(MH4?K0DO*-ub=MiAxx2DuBof;x80{M)_HT@-PJh_689T-(jK9(L@v$szGmdwa7e4rvn5$$ zF;V@!{k-);@SG<`IP4-P;v>jK^KgWez;Xrh+CuLhQvxKn)Wpqc;yaRFF&x^AD5R4G z{YP9)2SG8q;mpHU(6diO<}bx&{Jvn6_-yw5giG)G41b*|pnW5$t6DH{j2k+RYORR_ z2yp7^L}*EG&wE(@v*dMJ)3fErl6ln^WhMV3!w28}`I|aga;tIVRCbQCdXq9zyw?ggj zU-`*lT8TS7d(;D71qCdmp6~_n&FKuhKK?3lVe17ldDNwibn#|mfI!Dzh`>qw2i!mb7I|R6rzeKUN5fi(8jMf zAu1>;(aui2P9QnXyZxBt8@6uUAkUIwAt<-!iQmr?JA$aa46s<-AB*|rFr}4s)3e?| zg|^~rXP7Ya*^i)-e6ZRhPyVXF@^^3lzK6SlXUD%ndMl3D&AOcgDJ=L37z(S5XyTaz zC)D|2Ee?@{r$4j=6wfYI*eJw`p;~$$&H66{KY4gU`!z6JnPF+_Z}Spg#ZA+gMdFm& zD-oM5%^o?>Mbn?=aj;d0)I*y(NCzYPC&|&|JoO$3aBAhL-(o@|7(+RHTeK$$8-*>G zGxJZR>?{d`f6s;JCq_(1^nIKi!-PY2gxDOc8_DV?BCmS|$M-jD?TU?a1gJTmxZ+#z zdQW=)PRN^fm1bDtzH@qhB4i!jiqj$qW)%{Zo4m0#^M>eGRcPPtJi8G|I+bIx9NkBo_u^5hv0e_NT7*>ZSS=01UwvL)pg;-VHUnDByicGx z1#@&AueASkZ5sY}+Y37bYYK7d%*?=0#OAHE{?kk5f?gA>Y5l@-&o2qs>g}?OCtMQm z*MHb~#wR9G(;l=(+ZTu_=1Y)-RBPdd}T+r zO1wj&Tq07nD8oo0M^_%HH9a#~pAtGsRkj zN%snpw^nND_@tOSH8JqQZ7-ep@Ig?LQk3u4N#8DZy#3SE?%!z+&Wl?Vo27qs%;bDp z{1$UsFZQL}7R%9h<{Ybjij%!l=gdoNw^~@-i6;%5NEQg1D*M)3SxdIiIkC^a8Ii45 z`fm$5XP|-W3I81^)h~}=ZpE5Ua~iZIcXsiv@6E&iR}0XyUenC7$rmq^5^jYmifD+M zNi%@QIc6`Wl!kMSa++V>nGtbQx&c0T@BYca)1QaTcuO$BFZc!Ib&grpL%-Z4fCuR+ zh^=K`aFd2Gnaep(bRplZ$K%uRB_wn1hISJ<%k`IZKDYI}P0WwPwH(nx-xWwl_~lA1 zNZ=nJuPl8;b{0;7_harw+I)54GQeV2$lbLP1E#afgpMH7B3Vg4LJ=L`yyd)dV9ka=WP zeDM^f@1K10#S1&C&%}M2&$(mfg3GtEwd3(O-U@h4pVsOT*bzcH`sP(fQeMf6$S;@6 z4G#|j#H3>nv#wR>`vPLe$agfQY!Nhmd3u|h1yMBft8cyB*pK$qgks6kvmw7Fo`&NZ z=+TWmCLuP0OPstw?_s(ro9hHNy>c&p&)!nkkh1R9(4cG1{C5pOjH+C zkhN1Uk%hx0`BCCcDrgxKRa_pfP-9DF!!>tkItq^sJZYxNL_Sn^ziBL$YVrBc5o*{i z*xY-nRvqqp7WdxU?_OW+6vh(sb1(HDMBy1Iao%ad~ls)Vtgom=Q(Op?~c?1rbE98r3yG@&4HF3~3ho5vbQ<%0*paSLN5)02J* zvfvR#oB7x92)Ne|A0RFU(NBfxHI?rag{pBx!(Tf>6%yXJ`)Xs*CsEr#r!4FMI#=aA z^dw6r*N42Dch{f_c=OfyHp}S#?XDeBXD@+@jMaMt4dg}+T@-%b0eStr|>1NlK4CyCqY=tTx1tz2khGJ0M~ zUV%U*1%qcwn*`$ZEPE_TYner(tj!2v2GRbq7uKqa zE$8?z_yfk+8jXQDf4ivAa|(B@JH_wbq#f#SgCIRzIVDC*l2dkP25`|e%`W7d*lRv)3IGe-uXfSonsWlaEL|9l}*lNW)s|` zfwq-HH;l}?Wl<1iRNeGX8LLOlQt8eHtiyxc^WP_Y{;~e+d?-vjc07_wSPj&AOh5AU&`&WB*n10rv=9}V~lK#Jg zL*n~eFS#4e`U;|(cGP{jrSsaH!HF<>E<=oN9;G@XugilAJ#D*6sko}S6{|TrrV^Ll zrfIa-x){j!7va@NV^7$e4pF_58S5Dnuf-3xPzz^Ct8zp)c*Vi--xUS9#?WVG|6uO*@rFeh~VKM`2)2q1IS~rd{&E43bi&Dpf zuQn2_k1ks*(~zp#cw~QL{}(K$GhCOnkKA-+A!LCpOqV1^Cn+I}%Dmt!n?zg7tI0E4 zc(PbUITT!#lO_Xo^`e&fE13g+T+1o)>LvKc>Ld#rAI;%Q<4ggR3=hj*j78z^hHdmq zMTM(8zhSg76t|=jCO5Fr^ev{)AHx)t{qWBeES{BlB&g@QMJ-geDS3TA$B7u>I=mQP zGf5=BBF!v-sw5ts#w~|rpUZ6aHi!N{(}%7hf4;^wX-VNLA)6K$hc&~GsV{;!B+S9V z@!-#0b90+koffWw6lMV8k_xayd- zwEr)npJ`hV5p|f)?hhjqhjY=K#(i3BZiq7_C^tF{n|2>R@u*BJEvs>g>W|^qzU_SA z_4aL6(*HAkxkn_n=ZhrC1y$zl5;hxFa~8HtyUF2Y4l7SVZGDqjnMRxUKdN_qs2Ft9 z@7mdUtIz{&fp}&syD2-0%)x=^$oCj7Grc^jx4N(4}aRuC-w5 zO_#J#KQdf=od?)&g>A7<)-`uGC_8k!Q;`3H2EQ(S`$}jBjC0iW#Jm;D_^_)$PR~A`BLc#7*F}BRe}OA9aG>V3>bnK&oE;Babm%yJvB#c|DlX;l5cTRlYK{7o8vqa^%}yz(8sABKsIz47 zO*Ii=RPX!zmc6dZla}L|pnlDk+hUuK7UJ6k@NYSDg; z;iA6YR{f{`GOt#N@pSfKW8c4(rgl0po}RgR$6@$XffH4IWAXavuCNQO6;d7*|2O>i zoM}|5rg#3ybCNB#?Iz`1+nLSS%Z^}7yJ6*!4UHD*dtUJ4>Fv%M#^FG}-ZA+^^R~e7 zXZ^Of?j-S|jGl9R+xl?!{#Z{Fn8#A*q5uNN7bV|YN?#7vFg&hjyfgT8czdUfa%P%v zkKj!xM+)z;q^%302P3f`$YFIOj($cP-{LNp;#fDW8~5Q6UpWpry`h_~>+ggMr{C%F zUj2~YdO(l3;&C=%*#Y(X5zM3Q&H-LhRS0^EuT4U+KtPvmSd3LWApV%^B(2RRYN4QV z9TVhwg@A@l(7mgy4yOvF7-by&`;wir1l{EAhm=wl5rL=}cYcBA=O(!k`?ODJ&ubaR zU@EY;kzZKI!ioN3+R+>1A?T`3g)$nZzFc3E#r{FMy4^QEPx#w+*m}~wO;Xn~c_!_L zUmmTIzvkb*R$@+-@S4y=;y=_iKELd^Crb#hE9==WMeAXFBTE*gR<5cPo*%Bn#r zMu!|fxq)QJolrWir)MN3!<}lffzmRP8lp>WnNGNy^FbTqQR@T7GLnW&e*aYIa1eUb zcHaQJ3(-X0Z{@mddfaNX5%_+&^w!Ahn*Jz0Cn0$#E}_nY%vBdJ$FQ#+y>XnWJi$GA(6Y!6 z(qg!?c#FQJPAmDU8r`1KTl5~4dw9VQrg(%&9PujtN+=JRDa^BTY@gH4p45RBa&B&S zl7mSRW2+ko(M_zkp{kd1KDmi+zz5_u*AA6933e&R33laOV9P$vq(px2drhC8?e&R3 zyh%DlegCr6Q87(@@%|wA{ntciA5@SM#n^5MuVciFYp=nhL1qN?z6n08NFzld_5Q9^!+!01oq04- zNs;491^~zMI=G+fOj}3F`k(3PxQ+7G{_eP;U~uQt#>>F2fN>*S|EJsb4g5EO9QI2+ zR6PbGx2C^LQi4~rY*9v1SZzou3ya`+#jdi)y2O`rJD~Dn&D0**!4#u|SEW;KZ(XX8 ztl*4iZ@%tyLWbNRbb~%mJe@{rdvE8QpDNhx!%jl9clXuc0kwYj67pv6+o6J7AgTMc zH(TAnW{b^%IWw$BtN#n!u&`lIFo^rf;5_yX$>R%cG^UxK%Y8075+Ia#}YYV1yF~p6L;YMA6@Sq)%5a&jfaFLy+{!d z6hf1Z^r{j;P(bNYBT|&EAiX3uh&1Wa6+uOiA|f54A|O=|q>~8JOF%lI{5HV-{@(YG z_Z;rI*K=_{yR*;C&d$y}qsTg#Oa zd8ii{YqcF*3eZl2}PH~FpNlaA8qXyFV3ID{I$vdTFWYVbVy6jd@fg?YLWHNS&+`@*FFxS3q zRCm7F+)kukivhjr1~uZT$JJ>H?ZN}(3sh^W4i>N**k~**;yx83JBI6dyi{$9Pk>ula*p6jPtAbIS6WZ6l}VxC zx*de1K*N_QLCgaWTjbQ)EY6kfxnk83v3StQyK%0Hh`B(#jo6;3h={U4oozg~JsH8$ zjmvqhCWmO4r^OJyg{GFyxl7pp`QC{wfdVCVn6$eP+!5dVb4ewr>O0k^^6d(P_}sr) z}g^dWP$ahZ0DUU=h$!@Tje>8!a!3-w_Ie6fhS8&OfK@%o*-fNnu!I zlK+7U{ykU6<@jE8MY0MqO;{*>d+K04+r2;g&iD!}$AeTG!loee9m5>W8yaBYG1!;w z8>SI2cXhF)*!IL}i_Q@&V+5AadU+!NMQvsmtzHG-RqgwBqW0bMAk6pZc$ew3NbhmW z`!N}dcCdX4H@!!8TD(5jtA?xNa|5Lz)>bgl&@w4km1S$i=FG^Mi5HD!*eH(}JNHP4 zYT}@?^Nu#e-&pS;s@9|cJOKte2zjY6*Q&;dEDmRj=rT2d`rM#VlVr#1ba?2^N6mgh2YA$LeLq=X&V! zKzL;cAOpAd`U900%dL-lg?j%^y$U|j&_643I<+ylv7_TY+A+c{rMX51RHe?P9*_mW zrkVFp%}$@C=ysz;w1Od}Lsot33d_cL>v3G@HIii&ory+aOqG_P*w~26khYWl4bd~M z!DL>rQtIpRX-#UhQ4TF7uWUY7bh%6}Dvo{l7B%NYf#k^HZ{f5*Ys6B_f<|#;3fn)+ z61y({^JV2`cGO82I=y9_8j}2W4fl&eq@iV2A6ed5Hdp0}Sw+E}ST3gc@{1gZF3a2t zT~R5$LS!x+I-7sr4~i2EB}_|l*`(Zgvg*z?@7Lj<^Uw6VnN8pZo7rh~2jar~FmIYq z6EXX9XnfN^fSc*rd_y3VEZ4tY>=q974+;AI>+U7Vq3Yt05wgM;NZf18#{{EtcbGK* zAanVP2)I0|8NH5PoVA_`)3;CBbj04m5&__F|KQzzn^Fh`$wuW~r5tB3E=A5ARz;@` zcwhj%cu7|c)(+E*L6Odw^5zYfMM$HB}FXYq;SLw?I z`)AIma;6WI@mmr-JS6yJVlq zySfr)(uLvMy1cu4Y?NW9s*fTfXN?$j=dKQ6xF3o^nqkK0hpj+!6uRsQ5o<1s*);7$ z1vM4tVa{=)v94?4cMM@M9ox}p{|-Dsi1W8?sy6bY>~1Za8`<^kRxoO_T*L;=1}rP$ zj2fYDV8&dY-Z|>%9vR4%iWkkw=AIk!RdweTBTrxM<&Yfc#UlL~E=~}je&++lO61eM z{>u*8*ZPPj+`yjb1MZEk9+Cz;Q|M3gVztoVzK1?NCtFrOU4dXyaMh}Au<&47*{jZM zX;*nVGyP&-K8I>WgkL21e0R1*{7zwVsKk2KCHW9~f3AV5F;-pt=6cqJ1TMsB%BFV3CDq5gzF~J0Jc;L6-zRM(EuYHlsQynmZ4y_2>%2 zJuR=(Dk<%jT$O1vd!O!~p6ct*xUh!nP$A#ae%KXFX#fvAu%UO;oA#cYMQE=GAqY4e ztNJTJ-3zR^3l_00J2sVI9(PXXB))rpTWMJ_$M54!*7!i+gqT-Lo*8}mPVR;-1&Dg_ zJ#XcE_NFe_RqZvZ1|`_G(9f_7-SX~p2BtjFMXf{<)pI@2|_a<=;yxQbOCjUdko3W}M*K z6W6KtDp{!Rv8#9_t7CSGNSU4Lz4NQ>E{339-L#OtlDsme<$`*~%&Z(_Ad*^z*KpUS zOX?u6RUfti;RRXfd#DCR>*Rni`SGWVl8qh1!3pRF@ZcL>png-=g{H%gh&0Ig_bubZ zW;WECY?s!bdMjd!8y7pnS4&qqa7PPydz@fgUWgD!)Z{*Ep7}5)(8>DIN%-Mf*(S1( zkChD>qkqdbsTxT7d9OPM>=C+bX4l+D2hV`tRcQxDA#H9ita7^u&($uA7@WTU1*brA z_3(rX$g}pBttFx%>>s|}`z8ClMZt z10HAht<9lfAHG?Gm=0o`4linVZEAv%hA=kfRQ~duWy}mk<@MA(OAPmO4!_$A9ipSR zmu*yJk$Qu1Jzr*>QC2lF#rG*8!_0$9uFMzP7i=EuB1(48<(N0Eos8+fwB|hovB=(1 zNCtJE_e^*pk{14dHroKPFtU4ogg(fm3@SJVY$) z=G5L>)G#YAxa|B8YsB?Mqm41`JSa!BRRpe^2$Sk{^zE4UM=IK1?7d_EAf(<9^RE*e zYQH^P;(Jx~v*fClxudU>E2_7#&_0P*qTJF~Y!0JUVo?Iby>p2!w%~1P>ndB5hcn&@Mrh;q_f~8>3sK_oEKw|BwtCqufM29ep@cIoPc5nP&B48~dR)$0 z9hM^^=wj9H9G|B|t43uvKk=*L@1&;AYRsk=wWefGk;^Otk?bI}HNRe|eHi@7=T7gEu1<(4=>s-Fb$a#;g2l5 z(rbG;_|k|}Wdb#IiWp-ZPl;F_$I?NBQnT39O%-+0=ST*MpBEl+u$#x~A#ab-e zwW=Uc^?diFrY!iGGCfoIhx6_mz--}5sq+2tAj^0zt!TrD^%QL6@-{eXa-0qQ9yQc; zGho#d8JnCWQU(un)RlhQBZOAIk1E*~eMhfGq&H30hxdIHT~@pI<%Z;zRm)Ce7k)Ok zPs%#J_w;#8gJ2spOo%=BqWJY8KL2PDm|4ddD&)uIfMUb5AJK|%JzuWfB={uPHS*Qg zfGq0kdAIcRCjO@LDV6Bm-lV8tA=`m}d9i1#GSwep#x`uMMpXI`>dtX}$JHvlrCQ%b zS&u4g7=w6d#C~>ZbL8SI-t+7?3=!@L8TMH%(`~1;)*rbl%J#QB8<0dQkS>KRAmH-C zjA9?e8O0ri;$8)s_Boq`akE}rTG|kr)3H_xZs5+o8C&^$Cr~lgw57W7eIJ`KXMrk% zfaIlV{ab~5n1r_b1*w9%eJdxxF{w2@N4?4z~|IkWY&`z{4 z@J2Ul`$)jRyTb>Lh|nb~Sn;%*UGzK4d#pFmm(lrV>S@DgfnERWsc!zIVD5a@* zkrzQXCBf2J@zzYBzVki@3xUb*n``4{z~vktlYH9?HINC6Y%Xx?t1vA)1L2k3FFG`a zo{5<6a^xu1gzQ!mP9V$LS54=YnRVyf6_l2iZu;21H0cd-jw$-*=EDEu(yO2d3hn5> z)Oa*MVezU7N5EU(Uw6-S!MY#?iGzfBY^u6i)BVu_#SX|sd1D=_3Phc`EF#}sGj3HB zsZ%;&aoiAduJBlJ1|RQ3bn11_U!O?#+x=x?APa?Vv8}6(MAgVfxpy%Wy@yakjNXM+kIj2 zBXgb_uY`Zcl0BM1eF1l4iZf|Tz1#E63*VgDZe7HQ73&7L*;`MG@zrLg$taDk!3{qA zaz?+Hap+~jAIGt<_wB^^-u{j=JI`#i)})6M-t9_1-U_J~TTz@f;&Q!c{yIo>dOPPq z`V)A*qJ_#A4MwvN_R-BVr7sp#DaN?3Wti{gzB%tA>32f|Vja!KlnqUY`1o^hL;o%% z=UZqBjF>bqO_j(9NfA2;7X5zp%>kE1N&B>?z|PHv7ZoAKAbiYJ^_&cQ3elGIiSR^U z@SHHi8}F%)q&vEa=>}xU<_+2K^>nX2N6U!HTDYk(nYNd>SxJN6y+WOBBO@J_o42;V*hSFBf0zwkRmEMYwp~_7kSCVK zaCUT&o3D-P;n2psM5|IU@YvWK(IBG=WxfBo_;O-E?c<)X>635%s!5sN z0JWDD^k)b65)BK~<9Gv;Tc*SvAd|%>Oh=keY@9L+P5Ed`Rj)1nGVA%~*g?ei*}Km7 zHT7rdaN8nvA|bST6(fP1{zWZ8K{u+O{Ru~yRu~P|6hp#90KZyfl3qFU>L^-?vCBPO zF{yzPrFMF9b%%Mc#a3{L zjdMU;a&x5lV=&;Ljo+7W|4Y|!8A2ne)a@ZLo!QAh^y}}CFce^CX2Pz;!aPp_ov*4{g90V4?gcZ zS$;+f-~IW3o8Q~{I;=g9UFc1v-TvjR3&^4+{kQzOlGNO{+Kr|@tgOIw)%_GpRF==< zUuP_WgP7^dpTN$NPt?8T{-vPLijiWJn50cq%+*@IWn6=jPn5Y%(?Y<>$gM=!=dM$rmfm!^GO2- zqSc>gTjf7C2K8P4u`+hemGsh^bj}j0f@5iu_yUb=H(#y`U*gOjdQ6V(Zq)CFPnLa# z770tXo?8@1@Z5MzoTM@-xF!7~kazQKu$fP&RGMYVbbiykgu~TdSheT8k7{3 zwQmGOKvpzTHwk(xFYJULwhtRsukkhFmQByc^09o7^niArkxebQt5o9DIZrGIR?~HH zd2Md{6oh$fh$^mA;=FZlO$y)k`zQYUQt!#q-)CH)naN9xs@(LtAGc|EW}l&;b{(C^ zvS6;8Jmbt3tf0@q6iGunOBI}LVSQmLAU}vY?_=1jUH*el^o97c=zn{FJX4PsBAJ($ z_he&3`#t-}Ndtb5c-|Bw&J)(pT!;Qe2NkA=NTrR-M5d+`p@%PlD4&97XhY^~YH4`V zR=Z9vheL>F-o5)FCZQZcOs9IvRkA>mW!;4gO*e|&xE4A}i&I;SH*WpC&aVnw7sx)u z?WS8eV6l`A-tvNI9PD~)?7VBsPPvHSt0FL8oP87_9_KQU9OFIV7ZC=+7DJY4Z*rDL zv9}yIQqx63tR%6fp3W?h&Lo|D`i;97G z<^St4Mv1lZ6ex~{-AN?G0-L5nhBWP1LHuaK!3X@j`C0wBzh4IKgLO6_U1U zY{akctphk$sAM<*PK`6{Y3sh(Oc?5#lL29DNVBZTX|4P$*F`g??F034_!^=J^EeYQ)3U33|Er?# zJ<0q0sdxg43ew;zjO7b-hwix2sS;(Y?J|>3gZ5u5#MU{uerYJULO~#IADQ?POX7^Ktue z&-&`OPb{wZUHZCbSTJtwviMMzZ~z%7|0;9l{l{y-C6`4MB*^hj#sIU}E0+7Kxhy~u z&(%l3^yTWh(Tq^j>6Um_jlsRi+LHXkIOF~p)8eSA6`s$zaZX+})~Cu6DG9Mo?fRgT zSpOaoW>4WJ|H*jYbxS5`{UciB)@T3JkoZD3yLVr~b6bxh^VW%Os?GY0MOv~j7;lab z<+P8u(n^t*aL%m966CJ->k7k^qA!|s z>LR4-;R=)uZ$a%Y7ZFRZP-dm}=kb2MKtrup&ez3pudU)uf()4&=KX|r znw>*3siAh4`k$zMi!kabxZvP-I&60Z@OrC@Hw5R+zeQpMlktE_W&%`i-^QYfRbGf| zjreC&eEs8hrZe+n{HL+!@O2Y!i8Eo3{+`ODV)?+KC-Jefg2hYW#?VI1Xu4(WF3i7> zvUxU|lBrMUHwA+bJ5uxNKSCQ|E`b-k=(GA{tn$c@Img{XM)zY_nTP1=zBbl(lv4^f zg^&&;(lmj?$wfpo31x8mq@Wkgy;pIKPaD5a{$wG-t4fB^Eh4tDl>;}^Eb4aQ_a0hb zRfSYQ3#Q!d7%G!nyddHULNRSN5ZH~T$rz_nUjtCP?671W>H5oQmR&{-SqNdD?d z<|`1|&!;O+4c_k`n?DlS4&I(j@otxC{#k6N^4;kpgAyL%QM;l|$+&+``L^gd5Ayno zVUnEYa!N*jKP&J=a?jn(VflHpIiYrQW(x6h*%HZ!q)=g(jfS%?dR!E*4^b*380wBq z#V3`ulaRUF5?sd38GNHfyQR$< zr?vmmCxh)C<#CZN<-EJ!J}(u~@EX+2HlO65@zI#pNC_^eNkVT-Z!U9HsB z)xdpn`?J311G6isO5GJT{nYOf&g6??2_mD0LB4pG@q_PesFlWtyQz)*TMPV=@|acy z*90?HHBugUq7r0xES<3OrH1chaB+FLeEA8{xL+uT`M7;gmDZU%LK;^K};0yy<)Rp%QdL z$k5zhiRJOak`3xNkQWKGnhVQ7Do=8zm$~z))Z6MBJrSyvMf>6Wi^qPwOX7%F7#l|a zd60FLE9Q*s^K?m@nkGxM))iZ!+KOt_5*4b(cwc$m&7L`+>a)r1+29yJ^xP{fXgjMA zGUQXgJKpv%cEjkbylI;qD%)7)WokJuGr8oahwIn)CqBOI%u~o?*@O@FTef$@#VUNn zN>tUwZuN>VymIC+zN^Bz^{LbQIs5_cQsdW>3e+%7?ExfCX65$3)@8u?=d!!gukJ37 z3(-8-?izF}>-Z=|#Wcy8@FIG~F=KdQkq-ZF%}H1{O`&{C{jtzGbvNHed7vSjA{KH` zxCsxe>c_}-ckqJd(Ob*DiW^~>EwY57n+V9(EN?Jq7nk2v+N(?O6eV;-p8_$-^>hSHCyuc*7hz08_p?j8Eu??WxsE-Gu! z8ZcFQ+VU5S)AOhMGUpt4zEWUzN9fMDf8@5bv{V8uGEPaq^c}rS`onx-8K>M#!A1l3 zbqxWBlgmn{Pqi}v!nHkdUA=F#*nZS%L_^Bmf~FVWdGsAh-Un&GW#5?#4LPh)d}yq? zWyI05nmrkdfrl@xF@l4CF5%f&o@=IitN4tc{?~i;T5C6q7c;dol(O}=QTx>hrmbP@ zwDK@P*zC>zqnTS)ox<4tUJc_ZIZ!!Zr>oEv8)4LvvW~adgorVdE$u~c|52BRRX*C< ztb6tFUiG{^-WGqIzeO|mVX21D)IKA=^KtezM~MnE3dhcA66mLEl(}$OJh4^krgG+% z)s|jd*J5(WN6=aX5JkD39Q+WNY<*vsM77Wf8WAqM*~2va*|v-@V+w;zbp{b=$JKsc z1_cG^{qxQtx(AJ$OrPHdw9$r=@RnRn1h2klCpI)CGGM^!MjEd@VLxw(M;B6uEg88y;Q!I`LYg33HFQ< zRWWeTH;awY1sO{1-Gti^C5$Mb7pn%1nUU}-Rq5e z4G5v7@7TtS#R{Wp=P{V$)T+wlfz8L%h6$7mXcd)-F)iQD5|FIz6g0za^l~)PuV^wy z@+d&+_gJI}eG!H&P)@EgXhKnBpK^8}RQPN{E)U5M`u@n)We4Ntm$Xeo1e8l%4vo}2%+1l1!u z#D<--KL}?(_0*E#3*TZIN+X&2?X8&HPd*jeTDXQ6#S8Jje3ARtUF^nWP9nx}tjmw* z9(~H+Ak(7go@=dLgi-Ql-EU%z5AGT*HdcM-NIeL>WxEwn*rHVhCh@f?Wi z9nfv81@20W*Zy;L+~770j=o#TRY^Gzb!SpO+2IG&Vd{Au?jFazuGbl-5)V%KUIdI5 zE}Z2Bzl2d;5lPrSugg<6FrwHWsm)$FjCgAP2|wufwiDU5f9_?!3_6g^hBkDmQL+ke z(K2z^6O4A99vF6z7qzLK@d%7PBO9U3bP*?oDV*DUR~Y71o+cc6$pMp{8K6#;+3-{< zg5bH~dSw{SPrDOyE(-KLt-p*Z#K!~uH7 zvrAM#8ITV_Gn!lh|Eq1i_~YUoUvJADp+>yF7Z;Uzqu5JCj8!LZRp-JM++s)9h4tUqA{|do1|k_jdzb8bAuoV{h457u__apRAhs z;{)G6u_i5fwAJ!n&cHbl{OAPt57H1L2|<|_ke>-eZNe`l&2Qie>v#%cCX;YQ^SLGc zuZX_ScZ(_|oK=<^+Fw_sWdyAK)@Y8;&9aJmcU|V3^Kb_O)0}&gFWn*At!grPwxck- zo;XTyf?=61j*LK-^AX9O+;M%u()U<*g7JQcuG^@*2t9#$){FMK@jbt3)UpGv=;6p~ zy8wL#c_N_${3~}#MvD1wJ7w4~pp)ExJSw_f;j-~#XTCUl=x?A4Xaw02lA4{m0>AO~ zz9~N3=TrC{7=!Q*U=7wUykKII|H+(`@eO`V6iJz5@jFk+u%=kq)c!?8em(9dGeqB| zY9S%X%u+PzhpB!0ixIuw;x@>X3DL3PMJdD?C0Ap7i;oisnUO~6vz-%sDO^t^<1=&{ zc^|MiT2}}7_hb$MSmc5YoHXHf| zJf!?-vp3$!^G~p|>O8Jzq*bv$Uum6^yL0`C@zrL~mu+(Vj(@zQVc~hC4h*DhDieM$+GW}0RhlN3y*XW1*Ze(~_j!4On~sKo zTLxqstpRmpCHDlq57!KWg=ax7;Ks)JmyR(eP47!LHj-23)TzKU&kaR&rWOXljKw&M z>0eONWod+{{CcB*dl~;Fsa=Xqd9VSJ@NCh%DGU`xX60aez*718;a$K!NrWwaO2mCw z`Bbr@OAT5#izHUv7@3<+X^Muf`U|8g{~pjX1Yb` z2E7x6^v6kUit97zf^UBDYW~Fx8Kbsf_xkb@o_D7%#7-k;3>@-ceojt+k^}&#T>M6> zlGumKK~m1yq?TEt^n>0cy5yy{12d%&0qpD4X$kD67^vJMNlEv;C@I}ux5HI<>wTk> z2eQm)qRem!bb3mBx==-Gm$f;`DoPg+(7F1-nDf8CbL!~t%-1M)733-A>rY zzw>N@lyVAwOijgMTLSn5O9MoFTH^&ICeZLwJnH*>ka_uJ1xGGPzCk$v$bS4m8|~DP zjllx1cSRrz6>OdQU3d2mkLs+JA^o<-jA;R59`pFLZUnxJG(ceHM)H?WpJLx{l9Fat zML8iJXw8^WuxGC7r*!8QdDXo0^Q>kM^hhb**yW6AK9gC_W-?W%cf(rG)x(nwwBR#U zF>%)63z3^>js-Y_@+AK|`(|1#TVk?QFhBp(&-=XtA$HXzq)LIYDfIbd+qco7j36N& z2zc89O1dm7Pf|)agOLd^qO)BFV7lE_4=5N1P4cF46AW5M*Ti}j@6ly;B@7$!S{;ew zJ=YP0?mBg@UNlwfbVP{WfiS4A{d`Y}ma3lN-x%>W1b8Y--#?LI`9v$aB|syeX5y$t z4xk|?y@};319G}xM0Z`!%6WX}LZlX>dZPNqz@vOX819QoJ@0&f;8u8PGjGt}jlB@^ zE#{xLy9w%_{X17T3#99hAZfyOXeQH)J=PxlILGHoSt7qoSa=#KSsh_&cJoQiQgy-j z7rxM~Tc9tbgn`EP-;s`3XgZmlNigcGSxdnFrKFfcU;t~=3G!+|BB^2q_0xTt%2QiR zN|MuiNiNdiqF6tacNw>Bz$BZ(h?kbasE_gf9wF@4@85N1)qoX_kHm96UsNM$sJ?Ep zWYrJFOerp?a*qyW)N#6Nz6#`CCH-zU#OsiG(lWpc3o=86qb6yPA5w^m(7x< z6<}MUAd{F&nguyhc@JuGr6i8151t{Bfb}~S{8`4Kg$eRTKfTGyv+g?yt$-u& zf0T5Z`woDBm*>6lhO(=G3cH>G#I|6(^yurkCZ|BXg|3TRB-59%a&$Q;Lp=*B`Doh7 zDV=RSX)^4hPq2HPl8$M)v@at~jr{ufLp*oAqczGK5+~56SBZRZztAVuYpKo5upC2u{|t| zT>N>1F$kG8W7$C6f7`5K=}QW<#C=;&Sg>B==cV}gqy8^&`6Ga>cQaaKwaj%mZ52_k z87>csVp)C2gPLKU90P>=Dj?hkd!27{Mt)ub)ToGzyN;4u&|9FXsTtcA6eY?CuGrVu z5J10M$knxpM1IcbUuAK8%Lq~wy2w1v$`*nX7RV>}DXDrEp06+^41GDcFeZ;yW(_Vn z?T?9B7+b-fhto!BTqb{JE&%P+fgJRYV&3Mm`HQUHF}VuSYzt6l<$Mt^#yFt1`hwJ7 zj=I*a?ZmOgnB*kMTn~4Fe|Uo%Ymrm9`tQQkhh3ozYP2JaY8S|_X}(8F3X_F(sp06A zGO6hb3N&G5&RK>KK%XJ3XA%2j(DS4h^oBh#8#{>jd_Sne3AD-}?I+uj`rL10O065D z!z!FZK(dYEzG-1Isc8;dhqb~AT4%H+!0+1K{4P`phvSdU1Lh6t60hOA8jxS*X+k38E#+>`c z?=4<_SiosKCx4LXpLDFKa(@XpR$k^0@2}2v8MeslhP20TfV{u$Q zgy1<3NQrEBnPU2AjrfnN%9@v^7seRhpYQm%Bp_=X79S8I7snQ?p9x|kW>iH~PHnZJ z>D0^Yt7mVR5gm}{bJW(@5x(-+>&UM531)t#V75NSwPT}#Is~G1B;Z0RcIGW0(_#rO zN}_%jL&Ylu4U=LTtcV`x0IfZqUC_iEA!WVh0dLFniqY=}2hDa=&o48jNGteVzTi!9 zPvGj4C0UL+hPECz)_osY$-Z8Cse)U-eZRM+XFW-s0tmOa5 zr_`XSaX#x-B}2RNdGC%?<>Kjyd+IN?ZM|()(aLB3X=Ha*#9uZYkj58Z*lb^AL`y*k z>|Tpp$;+|(|K$RV_b+8PW?^5WJYjYBZO_BKcUiVz%#>Z{E{-1ATcZRzQX0QiA3f(a z^149=oCbxhYIak}CNq+&B$rM;xyJ}EV!!5Nm@~2N%m1nN)x!s-msF8-UT32-YueBU zZyax@PqWzcaXvFeRkU;Us3mS)E$wO0-X6uOuF6O6Klb7SeM z&)3g(xXY)mVy;UoE{D{qIVJ<@$Z7Nsgvemr3_0&5@*MYD*H*F+kQ%$rAu5DdkZ0Vc z?TL{zWlENd%l&Bm^V9Pvt@F*n_Trm;CWQW@USRQ-XGvc%i45#fY6`GJ@^Llq78$~p z)uMwoHR#$Qb8W^>m#+y|8y|4YWZoodEZ+pRK}$4!rXG*bd@uowCv{62MKES7qfdW73=+pJXZ2pVGh@a`| zycASLh-Wpp2NRCh2Q&q9SM=q3+N2A?T?$;*b`sAjEt>a zrN=&Kyfek9XE&vj%#>Nrim_W|MN7eKWT#b`hzwz5{&Ca~4mNDrjU(q*kn8w-Zhmp(U4-KD-lW+1?$@ak;aEkmmzmz31agqodP+un$FH#; z^qD&RuJqwHrnwv3{+YhGyk~*#@4U6RG$xE^Jf1dLHwT=D4#Xu*3ccPZXptPAdci=N@CXQ@nRjj;@6++tgHwb#TGpgP@Keyg30 zV_)p3vfvN#=_p3yVv{3@n>Lf|X*oMLSxuR*AEEPt4Ozb=f3*qUo6>K7w*p!Vb-N(p ztheSi(Fc?&lXY*Lr&`uMwy#=UHMd9nb4J~|4`OGWLaPe<>xUsVMaZK!zIy1`g+n7#- z4EDplh9Q?#ucwmB@~%x*D)qvA-^Ys@53CqDJSBDubeg`mfP zX2wznl%WYeNZnH|Ca?W49=|jlvpi2MTnKiuIp0i$NLl}v%bkl9ElnYOqNxk3>SY~m z4HMWP-)C5M=l|QOBH}F$LX#?wt*rb&M-P%z&oz={n)HnU*uLvi15`5>*auf5)D7QG z^TgDAsI8C=eZKE?p4c_qfkS^=#POb{VEE$z_S0GG2SBzqe9v^MW=j-1zlgF;YKP;JVeGinjos4O2gS5aVYO=ndOj(fnrmmKUpvXz>ytuc7uFOeLC1KM{J7Q(=vqioQ4(^P&tt?_Hb@>HZtj*OZtweR7G!vDAOo0%)~3> zm^o9GZO?4b)Ta&LZXO{_zMUiu#|OeHCxDpivB?y+H7M!;Zy3(e@!gG{>Nl|0=xivi zU2RNbIyel^f>*PTS}R5g_ehY6|G>{(k{t5g=Po?H^ym&`h`=?lM(jOId1HI5X01(NBDtSmF-8kyFp@Js z6eRK0eT`cKvvQR6h7i|Q<2Si17O-nSctSwdeFEwOuht#7(#)Ca`#R2-tdR`H>?Tj>HSaxQZ&cj08)H)!DedrO=|g##;uKe*hTF7 z|80yeNvI~HL#(gUsU~@}P|-A%z9*L9u6~=HqK2vQ=iCHK4+wDkU7{kI6$KnFZ{(B( z#G28XqFuk@+Tp&y+1f~}nd_k)2rq9|_O!*9dq*%dxZ#N+%rv?#R<@8%NGB|Y(tu*4 zRT;<43Co}y(TWP+SN-|iCFy7-E{D2cSsaZTMZW?vs5$As$d8u+s|rW?wO)UnJ6E1D zvsRb2wd#1X)XvK{Y}prE(!Rnk&0I<8uez>x1Q3%$<7FAEfza_(SUyeXa}Hk}h-8=f zs>}@_;-RklB%L9O(txU-E`x3%%{x>W>~JvN011^Aoa8zl0s=&;_dAYyB5iW6q2bqN z$sYIHm3q0b$z-f=o|tPL7s}i?P>QWswR<$3K6sPjv-t85h1}AhD%%TfpMRin5Iggq zr9SK{AaiQ2O*R5~R69>BYy$m%8afQH?V^Joh52>dE z7Y}-Afl=9`MI?5S_M>NBYa*+T$|**Y!n%UXA59$uFwcrj`@xzU(y!x|CtB=g#t!)-Moqr}F4A_L8OIJBP9<@=n{uLjqZ7n)(Ad2M&v zs>yekEZ*&5ya29#@luKf+k%iW6^JN2Y`|*US1KdW;(9}b=V4&%9l3e6b zNWf(t2;eSEW3#I3UiW1V)9NUpjx_l;h?4purn{pG9C%K@6&(NwqtOHywhgFNeCyI> zJRRN|eUmJ=_B=LPUqu7O{S-(Be$kwLD;6PFmc2 z$|aZ|Zw5(_Z63qk!51|8kVn*YhC9XtfL5dDM%s>B(7KV1TpDotz_ftC{Y00CE@N*4 zFoNF4iLbPET{?J$q4-~@Vu&zV&144Qst;ZEiIIyw6bIaQt{kgE?vNAkgLX zV*R9l=A-84&^H^iNPiNdTCu3eRII|xNNSIE~WE@$kEa45BEJ$sNFNyjK{FIYc z(#!56Tus`t&zi)`rB^luLksh5mJb@P^mq}eO!lpfE}?DPNB@D5FA z9}?l0%YuwR#P#cp4grvR&KWG`y>G~!k+Mx}dv_jc-V7#H0*+?yBNXxPM`gw0pycY2Fh1Or{B>Di`c1!T zW0$bkfnBnpsr6-E<^%{e!H>%Jis-)2_}w-}a7Gy3VE`&Y=D!pK7zYvYejkKeBJr(*(^--jARB;-1|+(F_- zrUC5Ws0}P+oUa*Z5J>n2=!dH5-o_Il%lAo3y!*%r<_wu8(}BEYIp}S#v9Y)Xb^Lk6CeSGdZIQ4Ts=POFTD2L%Xz!Vt{j}#4>LUFBVaIe0tEG_Cy z$KP}zjD99f8OOkxtZWM{qAdEpH*;u^jgroT0fs7=OH@mp!O1k5hjqnKz3JxMGN2ff zAPtf9%ZHT2x9o@~VukN_5~!`A8H^*kJ`{$!RiIxP*!J9bY0? z-TTw0L09Gun&3TgJhT1dRe?s0`hr$ta1Hs;>4TpK$Jh!MgZ->LO$9s}XV4b72%4pL zIH&wK^t|dJY@y%s;7J)|CGfr&l{AJhO^6eOk>zwf;{rq0gqlTVSslER28TfS}XlZzkft0q_Sk$hO!^903fO1zt5n`(YC( z<=JHLBmq|RDRl9i4QL7JV8%+^_oea`Y_c?fWPM*I^;z^kp#&S?Qc9zK;_Xfey53R> zu?A5RJIW6~ns0je(bA_g8xP=lfJ3+x`huK}%9pGNqR~#;Ldp@ETD^r+wWh7|M>mBJ z-vLs=ClwZy@h4w0N^|+2BLwpy>AKZ}#hB^(^KOzzX5hLkB;5l3uD7_*XNv(=&WwNz z)sWW$GAx0z%WBLto^kiVc7tEcrw5n-|B;)VYTyIgBr~9x`kRSM<)l@QouSb<(>HfG zno+s~DYgP!?*Kkdsx^TP+AQ+k=?ua(fDDQ^WUo=t#zjZ2X*n|q^I(s;Tv4LfZtQ35 zDeQbe@hS2sswwBt5%~`25fm4XKiiYe7)9X=hyD@0pw+ppk1@bIqo^WEko?b|J>>iPX%6* z1fKuE3U-Yw2I4Dp=}*P#p%-6!4ZtcSjp7hixxc?L0Ecq}UJ~FfRei@K`}D?Gz{Kp} zG%c>+plOAp?!Jz$n5(6h9zg(H=BEE2PLSt?|E_AF1w1el4h87V^q}r9K|b5a2c2F| zifmLQ?*0e?3syin!0*s9nasZcMx@u&%Eye=)95&?1HT+E%>D=zb z-os8{<&Y;!?j0*)F3W!(uAqDY<)L?@%3u_44heu0zq0ye6P`w>hvmnfNNi3q@rYborCL8wxgnhc~DHT z1W|e>{D-B~qqXQtL7X8W=n1s&zs^m{CWtqPrwBx#4($WHT=ruhI(7_;L_$mC zrh|ikvuZ$+OtARzWuqtXXom6e*Qm5vZN9ulrv^+DnQyt;3sHXgkx_=1D~Ms@ettuvfpmhDr-#YDp{y`<52em-oFwTQJip_= zUv%mMn>9>QO@KhMCWsWwkNJz2&9PPKO;b8|xDpe3b{MN{^BLqxFP@)*-u3EU{!=d~ ztZA>2kLlL+{QjUSqh0eoMrk#vNPuy0;q`P^eeJH-OTV}W?k#x;=StEy@4}7&)90kp zO|M3<nG)*P`5TUWR=K#25T|q~%9htUh$0Xd|n>f7-RJg{;cWoKC zL=bhp9QA~kYJ{Osudat?ey~hfE1cm27-O#cwNBpE!`Jlpe~;%XVX41xpvuE};qh0( z-@2&_cr&CV0R87O=SowTMMVf0SeS0Ofc2$+-_A5I9=$~81R?>*} z7@U9!-|D3AXj`sH}nXc)nP?WqM2?=p6Hz-$C?xz zA)a(UBW?1BMS6gEm~_SrjculPC@bg-1ecart-N|{bQ4K8Qt*qp4#2wr;3PQ%ss#B* z5gzyetP+xV3}n&cGhUZ_gI|U{P>i1`E8Z{E8~?X`UWk=Q>38K=bB}?MJ7sY(>M<|F z0*Gn_zaU-R=qi&2w6<9IX@$-i9Uhl#p2s19Dz_+fKXUpt#BeE|G(b8JRIsT`hON<>}y42DO)LPWT#J&@IfJK zmK2rAQubw-DMX1djfvKwJNup+el}+K(2@<>%xnf?a%@ore6G^t< z?;hAiY&KA03eaWH9=rvb0`CIK;L{G13;)@nFO9_11NU4%l#S!P1ym&;=dNAv-8Ic* zGFg-*0;!#9z_g=0_-`SS0}ByUQg(%xa&SjYoonCWa?pPZ(X)lR@G>2N>fK>!eMiHx zcG!GQWv4~OafIiQAa`mQHL-pPk5wbK5-(9X@|1UkkGx~D=6`kJJI_DE%fl`Yu?TFh zg2zKcVU-DIs!ex>w-Ce9q-G_p|96`!-@a{!yq>Fq-#P2Wnfmcu`Dr^TrXuozvl2CpD` zQEvQhHTejj=yMnnL;VzF!5oj5|8--a9QN2aDPEH2U$*pLPyLZRCC5QA2_WRVc4nDf z@Tn@dv~W}xM?_8z4U-3YwOj@+^bK^+Ii&rF^KxjT!aY_Q2cMC znA8>S*yzkZQtf1RI>q>=Y{97=g~EJja}T&DtbYT)^TobD>qIaRM5+0IH8nKOxVyVg zsz2%O=r}Ve#Q3iQ#T|;f*)5-HKS&f-u&?D|&});Z!vJb9ZW%v5u@bELj+1D`&>g<< z>iXLCUZ39<=O-EFZj60>J&wms;hrCMA)NDSATZt`^X!P)Kynr4Bhc`_+~=yjTsK0v zD@oID>O3cCZq=_QzA~Ga8)4~A*2n(eJ9~jOhOA~Z8(AXqUk7%W%zQ}Z*ddPj@PB?# z*RB@A=(VqvgNI&T=-Bm0sw>ris^k&ftY~YRXVih5;5!1Pu;q~S{bnNs++DJuLl|8T z!3D#G0}KB&E21(>uoBodEIX(PdBo!%xx377;`~^19vn)%phG?F5#uq=Tffaqz2Ko3 za9Fh}bDnn6gCO7T%KNY1*cM0`Th;lkyT{Rqh#QC2{VH)q3_4u0Y`$U|bxZ+*tehk9 z=1GPntkw*36rV1sedWs^#np%G6Yk^fyJS%24*$k33E4g#g~pxIgAXH5L0mK!pXd;M ziT+rJ&O@vPUWp|{X|3tR6wFy;y4MFFzb%iu;Oenl`|B3G9NyxrOI?*ShUeO*^y_5H~jII80Y^gTH zI5aQ@lvT1N(gi+*pgIuMY^##rcM}ZtAV%^xM=h}23HRNYtf4VQ)^5)WtO!O;LuIqA zmrv9x2xjzeS2jLw0HF=?fV=N}#L_Q*9Pw|Ez}6+^y%nnM++5K5B58a-N=;3%HZ=l5 zhgx}q=cQHv4jdyW11i&jwpkXxosDZ^`3h~QRRTVUwP?cKK0YVkF; z4`^>fkirsVSK)RtTt#?ff?GBv(vSZN`h00y@;;H;}gnF(Iu`4Xi&A~7<&k;f&DzPJ~tpm z*Q4^l^xz)tiqDaDl{v&*CoML2v!p>vyr^1~fGXzGS<3tvz9G4f5JtPPrw&?#eGTHX zk)knN6DOm5$$~~L6l0M!61F$1nq;P7aIAi?lx@Yzk#Ku;Eoxt121i09BzJ%KwdH3` z2U*6GI3pMfruwd9ux+j=^^rkE&)m-MeIAD(kiDU1#{by)Vq_9KfJ}a@?*-k5YC%DL zl)AH-IGYVlL81318G`!*N}GWze-wk@99Krh!vdGugXL@SUHhKZr%tFFC3Be4q*wlA zpH4dKcKl~=k2#gsgKII9CbQCSsh+&5Yt{8ZHuS8!2^*enrmdCPXR#h18~9pqLzl_A z0p{Kd2&4oYQay43tz6+hFG`*DrUdE)XqF$YAYDZZE-ST1Hpne}JTT`_5&}=QbfilP z9w{XS5@Rao#>;QH>WIoAuiWsq!{8!5O5 z;}1#sT@ngWRy6Zs$X$M+7mG8FNT`BBG2ukeJwvx&TMiH-nZ_C|m#~ypYC>F zN2*l0U)9_YF_)`8TI|cl@9j5_pDO}SH0Ebiu0=)sv!;fg8he2k#Y+VHhsEp-yv1K! zjP^~q2Qgy~|NA?l!Nf8r8eJ(}=x2`m;gQHKq^NPw^JBfjO?}k!SppZaKko3!hE8q2 z&7`eqPKUgfg#7h2JG=X>*s4Y2de5Pr`|rjVhA|mg*HiWyV{(g%(m(K25f^U!_=k|$#e8eo6?s2yKG-pmOJruBRF#e!d^@em-jr>dd?}=HApKAS z{VXTSp^9o~)|;=B$^_f7V$C3c^C^)N#f`mwRm29AbucAV{;QeXkT{dyAp>4`Er8HeL7Ng;_5TtARL;E^ZG?z^i+gv-qiv13$wTb^O-?t(t&xWB#JRH%Rg`w z6?{-tXf=$owwu^{(2p%J@OLj{A%a2ZkyOe_1_N1p;UE~5sDw@CoNkVFyy^_w$HZa@;IcfLb1qF_z`o*qF2bzP_DRf7wmHrB+_|X8DRjOYov?*LS@y=_ew$DNof@4rW0O&sEX+t#1Ll@=nM^Odup_3PCc@KM`OYSbJ&?TW7bP&ldGE&kDpY%*Hk-DZ7++!HnR_!{fsj@@+YU+@%YT- z)5vdZlCWt0ecXQ$rP4R);FKo1ban@!!2N|MMQ^gw@t;Lk3X^w=cBlJyK;tSqf^D+gs!7`_EqB*r=akQx@zOeV1v}v}# z$Pnohgk-Sn6|LNqLy zis0N@;6D=$A}Yt=M)2*^Ns>xUiNxQ=AX741H+b~Ti{#PA7n4V*xA?ugEVC$c!Jshg zAjobXj9O}A0P2spH|R%B*@A$tsOLBEILGHv1@gvla*QC(@BE$ z%r^N?88B8fGhe1VZZoURT`fJn7Dbh&{+oI-Sylq$g7x!FR#HUW+a&kz&>ZaTC4y=oi{pAk&UqVP^oZbmLcLq5B>oJv~DV9;??OF; zF9pU-->c9?p@*^*3KdMo4}II1F`L=VLo`#!ZX!k7Q@mS_lJp;TSsNLIPDZ8HEq1SG zf2zH3s7XtH-?`_sEBsGS#(FF5efZ=2Ji|KIrH;n-+CLN*>S>46?g>Lnpi-WYYVS3! zh~DPNM#}A5i{#3%jr0USdb&dN-9`n4Yi+egx1nusMo7oelmV!*L&&paD9|M?JMkCX zRi@q}gREZR%$6do3bagfNpdzpI$wqecEehL2)Vt5^^T7l(RDx)7s+rIaz1)oB7hpW}BFqH;+$-vTOC^tE|2&FM`At2XLqs zkbKi3pLH$Ob%1x(S<%1LCX4N9MmZ&CLKL04dQD4tAzC8A>T`_1$h={5M&C6Hr%3-< z=LNnm67>pDg69@JMO$puw{cyw>EMFKU+EJhou=IIjqlpq(I;yKUVO=w)4jaZCbNak zA$bn9PnKZmm!hwk8&RP>ylunJ*Xz2OiqPb3-3cHE5nBnLRDC4_&T`M4MLx|$-&mh~ zUAeXLBO~u!w>Z&q?Bkf}x+n)=6Lqs1B znjITR<0veX`-CFxj|OhMyZj1@MEAbK?rG#v$0&y(TX2uL&roPJ1Ynl>1c+nj&5arU z8E6MSGStdoxm)N_Pn1G-kPu<>w@NrDN)A~U9vL>+edtuPE`ONds==3yuOTqLwtY%! z!eP(jdfzuGdd`nz*+h|U6e;M^=_i9y$HY!Yg~Wy3=13h@alNY42?v-7XHT641a(-h z=uNo1z>7^nZdC(kfce~ymT-7*!cI|3Kq4bFeIaLF2ntG-1DB~W?c7mtigg3=tE;fp%^xb(?M(~|6b z(kuQ<|By3)u`O$(Igr4i^w!UJ1pWTN%EL=tmReq)@9%H!Imb+b$}x!iGnVBK`zwFy zA0zkg&+WKUpzd-jhb2xWNen;i%fKm=?-g7jWa;dIbtQVB6c>EBDN84MhHP;<&zw_Z z1SHC4v$P4%i5r_7efs_-snB0s&zJskfhF;BBI&itVrB_skHE%vie=DAg43@^Lnx26 zV@wqWB_g~r>ISBGwbr60PEV~p0+}bSw*OfU$9oaMjlD*k`c^YWTh3>VZUyWPaG(qz zO(;ulqrLR5Cda8QELabEd7GkJR0M}b7ac}vplmqM_b6smztHejf(JYGB=!3q%D8Yp z5+go=P-`M?` zn&|DGW|ERBD_051q5*K#%+san$8(iRZ}}ruW}KRQ&0jl|xz$)(vp3j1vjLTVgNp&y z0KAj`2gI!M2Lg7XLfOm%W#Cxhi7vBfd+XJz=*rA&1?CEwwc`AkI#9YLy+Os(D2M+!)ZAKh zabJK*ocV)%XEHJ@uhWFt>Q@Gw-shtMQ7=vTgpE%;o4G7 zkHfS{Vd7J+v;$_i!iJp~^H&C(*XLtDk$$e0!>pi2Nc+%IwSyonTzP}K*dnD;m}e6{ zny7Fi*U-Iy1qJ{ZKXNJa?Sg){Z{dSw2^;#zdgQ`XQJMN>E3YiyaZ)ZRkOYh*6**ld z8UPHi<}HTfZnOMlHc8@q6WSh5^U58Jm@Y9HEDVcTYjwYJ<;rKnE&lSiH-GPN=^O(r z1_nX@-`FY$`d}&d>e$RJR2e>*`pTE;>p!b! z-~YICuq1%K+G14wE7jsgM!W0@CNc+nP>BJLOtr%+BgE8G_}Ln4n(%&X@=y&8jewbo zmK*dfnurr+-Qdq>nJBp@hNsLU$gddu-ATii`bzvlGb>w#cOA)Mphv*fiJ_g|eC?f`+KNX^@*0?th86ezI z5l3@oi`o;$>oM8+GD%eJW?;L{ZQ!Up=SDcX z?l7>k0E+Ggw`vdHzUl!}qDN9tLFW1NWFXG-LJn*nXm_TOy%1tOehuX=1H zfbxLhznn3CiQ;c{R*Pw|g1maB!%n_Ao!Fr@z4~owMX7$c2&630KP!KGtagi$Gy3f0 z?HeC?waUSO-t>EStzX@*Va=P*aMgoPcS2f{aRgJ>L+lnvvSG0*)jaRyOKwIgrrA@r zYy3PUefqHu5pDJ`0)iHa)YcCi=Ehn=Us+{;?JlsL9IQEt+!p`=+Uf#GXZ8mELw?|R zd-3_2RQfPwE7sXiD_~ztt{P9cr+MWANlgu}ll}KMo%)h?8Qh!6pOHPhe9&gk+p`Dz z4{CT_Jl%gM{OiTe$3on{pS+cBoc-|aiuasmZ-sWKKxxat^)$W&?pV z^;9=^X={6Id^{^F3pLz;p6#!EEn}Lz89jHB{DRy?wzjmwGA(0~vF z_7(UQxvjU=SQKd$WfZl(eo8Bv+Lk|@B=v^kZ^K8I_&N1)>Vpp|*#E0L<27xsVhFqA z2MRv&M}7EG@x|gR7cN}r5yW(LeV;0omz(`bt|7mvt*m|Vg=<9QiGB>t*ywaaY0h~` zmqPKv!-c+N1lg!is_+(hd+O&Dp|uwp@T9*c@N!+q?33W6-4cdTVLgK2Gd*$$RU%h< zdb(}^l~{Zc^6zxP)V-;isoLLx;)O9vDYq=Y_JhfxX!|k@}B0Md-psL*5mfL@i;_iu?UiPoM)J1MdA*|1}SQ8A&HDWry zY!6h}>Kj2IM2~|Q==Q|eRh3PWt&58LRPk0zO3OXT+Qo(%2M2{cM7vMqqSl{dQ*-hp z&W451`3>)TflyU(cZc4bZ)|nB_19V_ig^i9{z+m6(Oc+1Qf`ns=z)D4OcX->w%GHh z&(5{$Qt-k#a?jM#-Z2<>2C+|MKCC>0$Lllx#z6a3rx`bltC$ZUGMN|gI3SI6%YFjN z!@5%y;PPPIVX$W)Oi_YBcw#|=%*#W17m;-bfjhh8ofvUc@*aIsfek{nTkV2X4;YrtqMopMKpqhw zkf&Eay;w~4X( - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - K8s APIserver - Patch - - Watch - - Controller - - - - - - - - - Kubelet - - - - - - - - CNI Driver - - - - CNI 0.3.1 - - - - - - - - - - - - - - - - Neutron - - - v2 - - - - - diff --git a/doc/images/kuryr_k8s_ingress_ctrl_flow_diagram.svg b/doc/images/kuryr_k8s_ingress_ctrl_flow_diagram.svg deleted file mode 100644 index e85bd3010..000000000 --- a/doc/images/kuryr_k8s_ingress_ctrl_flow_diagram.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/doc/images/kuryr_k8s_ingress_sw_components.svg b/doc/images/kuryr_k8s_ingress_sw_components.svg deleted file mode 100644 index 839508c9a..000000000 --- a/doc/images/kuryr_k8s_ingress_sw_components.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/doc/images/kuryr_k8s_ocp_route_ctrl_sw.svg b/doc/images/kuryr_k8s_ocp_route_ctrl_sw.svg deleted file mode 100644 index 41d84bb9d..000000000 --- a/doc/images/kuryr_k8s_ocp_route_ctrl_sw.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/doc/images/l7_routing_and_user_lb_neutron_entities.svg b/doc/images/l7_routing_and_user_lb_neutron_entities.svg deleted file mode 100644 index 391792081..000000000 --- a/doc/images/l7_routing_and_user_lb_neutron_entities.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/doc/images/lbaas_translation.svg b/doc/images/lbaas_translation.svg deleted file mode 100644 index 4a45332d1..000000000 --- a/doc/images/lbaas_translation.svg +++ /dev/null @@ -1,2569 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - ref. impl - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Worker B - - - - - Pod - - - 10.8.1.5/16 - - - - - iptableskubeproxy - - - - - - - - - - Worker A - - - - - Pod - - - 10.8.0.3/16 - - - - - - Pod - - - 10.8.0.4/16 - - - - - - iptableskubeproxy - - - - - - - - - - - - - - - - - - - - - - - Serviceendpoints - - - - - Pod - - - 10.8.0.3/16 - - - - - - Pod - - - 10.8.0.4/16 - - - - - - Pod - - - 10.8.1.5/16 - - - - - 192.168.2.8/24 - - - - - - Cluster IP - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - resources - - - - - - 80 - - 443 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Neutron - - - - - - Worker A - - - - - Pod - - - 10.8.0.3/16 - - - - - - Pod - - - 10.8.0.4/16 - - - - - - - - - Worker B - - - - - Pod - - - 10.8.0.5/16 - - - - - - - - lbaasv2 - - - - - - - - - - - - - - - - - - - - - lb - - - - - - listener - - - - - - pool - - - - - - - - listener - - - - - pool - - - - 192.168.2.8/24 - - - - - - - - - - - - - - - - - - 80 - 443 - - - - - - diff --git a/doc/images/net-policy.svg b/doc/images/net-policy.svg deleted file mode 100644 index adb6a0293..000000000 --- a/doc/images/net-policy.svg +++ /dev/null @@ -1,687 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Api server - - - - - - - - Pod watch - - - - - - - - Policy selectedPods watch - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Pod created - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Pod created - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Pod created with default SG - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Pod annotated with net-policy SG - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Pod update - - - - - - Attach policy - - - - - - vxcxvattsdsadsadsa - - - - - - - - - Attach policy sg to port - - - - - - - - \ No newline at end of file diff --git a/doc/images/pod_creation_flow_daemon.png b/doc/images/pod_creation_flow_daemon.png deleted file mode 100644 index cbe9a060b5c014c0af29c9d8995bd55b9311b064..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 220449 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

    gT^hfQTKT%2UR&ikBmr<$wCEcXMt68wZPDde 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 h#>Qf(~cyFZbniT8vKclp6|I;=XUnF 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 z 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 CQ%Gq5 diff --git a/doc/images/pod_creation_flow_daemon.svg b/doc/images/pod_creation_flow_daemon.svg deleted file mode 100644 index e887c5e90..000000000 --- a/doc/images/pod_creation_flow_daemon.svg +++ /dev/null @@ -1,1496 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - K8s APIserver - - - - KuryrController - - - - Neutron - - - - Kubelet - - - - Daemon(Watcher) - - - - - - - - - - - - - watch pod/kp ev() - - - - - watch kp ev() - - - - - - create pod() - - - ADD ev(kp) - - - - - ADD ev(pod) - - - - - - create port() - - - - - update kp status - - - - update kp (vif) - - - - - - ADD to network(kp) - - - - POST addNetwork - - - - MODIFIED ev(kp)with vif info - - - - - show port(port.id) - - - - - show port(port.id) - - while port.status ≠ active - - - - - - - - - - - - - - run podcontainers() - - - - watch for pod running() - - - MODIFIED ev(pod)running - - - - - - pod running() - - - MODIFIED ev(kp)with vif info - - - ovs-agent port active - - - KuryrCNI - - - - watch pod ev() - - - - MODIFIED ev(kp)with vif active info - - - 201 Accepted - - - - - - Daemon(Server) - - - - - - - - - - create vif() - - - - - - - - - - vif plug() - - - - - - - - - - configure vif() - - - - - - - - - - wait vif() - - - - Get VIF (Manager) - - - - - - - - - - - - - wait active - - - Get active VIF - - - - - - - - create kp CRD - - - - - ADD ev(kp) - - - diff --git a/doc/images/service_creation_diagram.png b/doc/images/service_creation_diagram.png deleted file mode 100644 index e0fa2211876f7fb7225964753a201eeb9aeb8af6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 92990 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νu$kFq-X>e@V)b+!e;y8) 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 \ No newline at end of file diff --git a/doc/images/update_network_policy_on_pod_creation.svg b/doc/images/update_network_policy_on_pod_creation.svg deleted file mode 100644 index 93050adf1..000000000 --- a/doc/images/update_network_policy_on_pod_creation.svg +++ /dev/null @@ -1,2 +0,0 @@ - -
    Network Policy Pod SG driver
    Network Policy Pod SG driver
    Network Policy SVC SG driver
    Network Policy SVC SG driver
    VIF handler
    VIF handler
    create_sg_rules
    create_sg_rules
    ...
    [Not supported by viewer]
    matched_selectors
    matched_selectors
    get_svc_sgs
    get_svc_sgs
    LBaaS driver
    LBaaS driver
    update_lbaas_sgs
    update_lbaas_sgs
    \ No newline at end of file diff --git a/doc/images/vif_handler_drivers_design.png b/doc/images/vif_handler_drivers_design.png deleted file mode 100644 index 64e318091b1b6eb8b0d40231b98d9d08756386b6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 36189 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 diff --git a/doc/requirements.txt b/doc/requirements.txt deleted file mode 100644 index 56f8df5ad..000000000 --- a/doc/requirements.txt +++ /dev/null @@ -1,6 +0,0 @@ -# The order of packages is significant, because pip processes them in the order -# of appearance. Changing the order has an impact on the overall integration -# process, which may cause wedges in the gate later. -sphinx>=2.0.0,!=2.1.0 # BSD -openstackdocstheme>=2.2.1 # Apache-2.0 -reno>=3.1.0 # Apache-2.0 diff --git a/doc/source/conf.py b/doc/source/conf.py deleted file mode 100755 index 6c80ce1dc..000000000 --- a/doc/source/conf.py +++ /dev/null @@ -1,82 +0,0 @@ -# -*- coding: utf-8 -*- -# 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 os -import sys - -sys.path.insert(0, os.path.abspath('../..')) -# -- General configuration ---------------------------------------------------- - -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones. -extensions = [ - 'sphinx.ext.autodoc', - 'sphinx.ext.todo', - 'openstackdocstheme', - 'reno.sphinxext' -] - -# autodoc generation is a bit aggressive and a nuisance when doing heavy -# text edit cycles. -# execute "export SPHINX_DEBUG=1" in your terminal to disable - -# The suffix of source filenames. -source_suffix = '.rst' - -# The master toctree document. -master_doc = 'index' - -# General information about the project. -project = 'kuryr-kubernetes' -copyright = '2013, OpenStack Foundation' - -# openstackdocstheme options -openstackdocs_repo_name = 'openstack/kuryr-kubernetes' -openstackdocs_auto_name = False -openstackdocs_bug_project = 'kuryr-kubernetes' -openstackdocs_bug_tag = '' - -# If true, '()' will be appended to :func: etc. cross-reference text. -add_function_parentheses = True - -# If true, the current module name will be prepended to all description -# unit titles (such as .. function::). -add_module_names = True - -# The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'native' - -# -- Options for HTML output -------------------------------------------------- - -# The theme to use for HTML and HTML Help pages. Major themes that come with -# Sphinx are currently 'default' and 'sphinxdoc'. -# html_theme_path = ["."] -html_theme = 'openstackdocs' -# html_static_path = ['static'] - -# Output file base name for HTML help builder. -htmlhelp_basename = '%sdoc' % project - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, author, documentclass -# [howto/manual]). -latex_documents = [ - ('index', - '%s.tex' % project, - '%s Documentation' % project, - 'OpenStack Foundation', 'manual'), -] - -# Example configuration for intersphinx: refer to the Python standard library. -#intersphinx_mapping = {'http://docs.python.org/': None} diff --git a/doc/source/contributor/contributing.rst b/doc/source/contributor/contributing.rst deleted file mode 100644 index ebb97911f..000000000 --- a/doc/source/contributor/contributing.rst +++ /dev/null @@ -1,82 +0,0 @@ -============================ -So You Want to Contribute... -============================ - -For general information on contributing to OpenStack, please check out the -`contributor guide `_ to get started. -It covers all the basics that are common to all OpenStack projects: the -accounts you need, the basics of interacting with our Gerrit review system, how -we communicate as a community, etc. - -Below will cover the more project specific information you need to get started -with kuryr-kubernetes. - - -Communication -------------- - -The primary communication channel of kuryr-kubernetes team is `#openstack-kuryr -channel on IRC `_. For more -formal inquiries you can use [kuryr] tag on `openstack-discuss mailing list -`_. -kuryr-kubernetes team is not holding weekly meetings, but we have office hours -every Monday at 15:00 UTC on our IRC channel. - - -Contacting the Core Team ------------------------- - -Outside of office hours, kuryr-kubernetes team is available mostly in the CET -working hours (7:00-17:00 UTC), as most of the team is located in Europe. Feel -free to try pinging dulek, ltomasbo, maysams or gryf on IRC, we have bouncers -set up so we'll answer once online. - - -New Feature Planning --------------------- - -We don't really follow a very detailed way of feature planning. If you want to -implement a feature, come talk to us on IRC, create a `blueprint on Launchpad -`_ and start coding! -kuryr-kubernetes follows OpenStack release schedule pretty loosely as we're -more bound to Kubernetes release schedule. This means that we do not observe as -hard deadlines as other projects. - - -Task Tracking -------------- - -We track our `tasks in Launchpad -`_. - -If you're looking for some smaller, easier work item to pick up and get started -on, search for the 'low-hanging-fruit' tag in either blueprints or bugs. - - -Reporting a Bug ---------------- - -You found an issue and want to make sure we are aware of it? You can do so on -`Launchpad `_. It won't hurt to -ping us about it on IRC too. - - -Getting Your Patch Merged -------------------------- - -We follow the normal procedures, requiring two +2's before approving the patch. -Due to limited number of contributors we do not require that those +2's are -from reviewers working for separate businesses. - -If your patch is stuck in review, please ping us on IRC as listed in sections -above. - - -Project Team Lead Duties ------------------------- - -All common PTL duties are enumerated in the `PTL guide -`_. - -And additional PTL duty is to maintain `kuryr images on Docker Hub -`_. diff --git a/doc/source/contributor/index.rst b/doc/source/contributor/index.rst deleted file mode 100644 index c5a86cac1..000000000 --- a/doc/source/contributor/index.rst +++ /dev/null @@ -1,8 +0,0 @@ -=========================== - Contributor Documentation -=========================== - -.. toctree:: - :maxdepth: 2 - - contributing diff --git a/doc/source/devref/annotation_project_driver.rst b/doc/source/devref/annotation_project_driver.rst deleted file mode 100644 index 9991ebcaa..000000000 --- a/doc/source/devref/annotation_project_driver.rst +++ /dev/null @@ -1,98 +0,0 @@ -.. - This work is licensed under a Creative Commons Attribution 3.0 Unported - License. - - http://creativecommons.org/licenses/by/3.0/legalcode - - Convention for heading levels in Neutron devref: - ======= Heading 0 (reserved for the title in a document) - ------- Heading 1 - ~~~~~~~ Heading 2 - +++++++ Heading 3 - ''''''' Heading 4 - (Avoid deeper levels because they do not render well.) - - -====================================== -Kuryr Support Multiple Projects Design -====================================== - - -Purpose -------- - -Now, ``kuryr-kubernetes`` just implement a default project driver, the project -id of openstack resource which used to support k8s resource was specified by -configuration option ``neutron_defaults.project``. This means all of these -openstack resources have the same project id. This will result in some puzzling -issues in multiple tenant environment. Such as, the metering and billing system -can not classify these resources and the resources will exceed the tenant's -quota. In order to resolve these issues, we need to ensure these resources have -different project id (For the sake of simplicity, we can treat a project as a -tenant). - - -Overview --------- - -Implement an annotation project driver for ``namespace``, ``pod`. ``service`` -and ``network policy``. The driver can read project id from the annotations of -this resources' namespace. - - -Proposed Solution ------------------ - -Now, the openstack resources that are created by ``kuryr-kubernetes`` only -involves ``neutron`` and ``octavia``. ``Neutron`` and ``octavia`` use openstack -project id to isolate their resources, so we can treat a openstack project as a -metering or billing tenant. Generally, ``kuryr-kubernetes`` use ``kuryr`` user -to create/delete/update/read ``neutron`` or ``octavia`` resources. The -``kuryr`` user has admin role, so ``kuryr-kubernetes`` can manage any project's -resources. - -So, I propose that we introduce an annotation ``openstack.org/kuryr-project``, -the annotation should be set when a k8s namespace was created. The annotation's -value is a openstack project's id. One k8s namespace can only specify one -openstack project, but one openstack project can be associated with one or -multiple k8s namespace. - -.. note:: - - ``kuryr-kubernetes`` can not verify the project id that speficied by - ``openstack.org/kuryr-project``. So, the validity of project id should be - ensured by third-party process. In addition to, we suggest that the - privilege of k8s namespace creation and updation only grant the user who has - admin role (avoid the common user to create k8s namespace arbitrarily). - -When user create a ``pod``, ``service`` or ``network policy``, the new project -driver will retrieve these resources's namespace and get the namespace's -information, then the driver will try to get project id from annotaion -``openstack.org/kuryr-project``. If the driver succeed get project id, the -project id will return to these resource's handlers, then these handlers will -create related openstack resource with the project id. - -.. note:: - - This is only solving the resource ownership issues. No isolation in terms - of networking will be achieved this way. - -For namespace, then namespace handler can get namespace information from the -``on_present`` function's parameter. So, the namespace annotaion project driver -can try get project id from the information directly. - -If user don't add ``openstack.org/kuryr-project`` annotation to namespace, the -default project need to be selected, the default project specified by -configuration option ``neutron_defaults.project``. If the default project not -specified still, the driver will raise ``cfg.RequiredOptError`` error. - - -Testing -------- - -Need to add a new CI gate with these drivers - -Tempest Tests -~~~~~~~~~~~~~ - -Need to add tempest tests diff --git a/doc/source/devref/health_manager.rst b/doc/source/devref/health_manager.rst deleted file mode 100644 index eb82185ae..000000000 --- a/doc/source/devref/health_manager.rst +++ /dev/null @@ -1,84 +0,0 @@ -.. - This work is licensed under a Creative Commons Attribution 3.0 Unported - License. - - http://creativecommons.org/licenses/by/3.0/legalcode - - Convention for heading levels in Neutron devref: - ======= Heading 0 (reserved for the title in a document) - ------- Heading 1 - ~~~~~~~ Heading 2 - +++++++ Heading 3 - ''''''' Heading 4 - (Avoid deeper levels because they do not render well.) - -====================================== -Kuryr Kubernetes Health Manager Design -====================================== - -Purpose -------- - -The purpose of this document is to present the design decision behind -Kuryr Kubernetes Health Managers. - -The main purpose of the Health Managers is to perform Health verifications that -assures readiness and liveness to Kuryr Controller and CNI pod, and so improve -the management that Kubernetes does on Kuryr-Kubernetes pods. - - -Overview --------- - -Kuryr Controller might get to a broken state due to problems like: -unable to connect with services it depends on and they being not healthy. - -It is important to check health of these services so that Kubernetes and -its users know when Kuryr Controller is ready to perform its networking -tasks. Also, it is necessary to check the health state of Kuryr components in -order to assure Kuryr Controller service is alive. To provide these -functionalities, Controller's Health Manager will verify and serve the health -state of these services and components to the probes. - -Besides these problems on the Controller, Kuryr CNI daemon also might get to a -broken state as a result of its components being not healthy and necessary -configurations not present. It is essential that CNI components health and -configurations are properly verified to assure CNI daemon is in a good shape. -On this way, the CNI Health Manager will check and serve the health state to -Kubernetes readiness and liveness probes. - - -Proposed Solution ------------------ - -One of the endpoints provided by the Controller Health Manager will check -whether it is able to watch the Kubernetes API, authenticate with Keystone -and talk to Neutron, since these are services needed by Kuryr Controller. -These checks will assure the Controller readiness. The other endpoint, will -verify the health state of Kuryr components and guarantee Controller liveness. - -The CNI Health Manager also provides two endpoints to Kubernetes probes. -The endpoint that provides readiness state to the probe checks connection -to Kubernetes API and presence of NET_ADMIN capabilities. The other endpoint, -which provides liveness, validates whether IPDB is in working order, maximum -CNI ADD failure is reached, health of CNI components and existence of memory -leak. - -.. note:: - - The CNI Health Manager will be started with the check for memory leak - disabled. In order to enable, set the following option in kuryr.conf to a - limit value of memory in MiBs. - - .. code-block:: ini - - [cni_health_server] - max_memory_usage = -1 - -The CNI Health Manager is added as a process to CNI daemon and communicates -to the other two processes i.e. Watcher and Server with a shared boolean -object, which indicates the current health state of each component. - -The idea behind these two Managers is to combine all the necessary checks in -servers running inside Kuryr Controller and CNI pods to provide the result of -these checks to the probes. diff --git a/doc/source/devref/high_availability.rst b/doc/source/devref/high_availability.rst deleted file mode 100644 index 2af75d718..000000000 --- a/doc/source/devref/high_availability.rst +++ /dev/null @@ -1,143 +0,0 @@ -.. - This work is licensed under a Creative Commons Attribution 3.0 Unported - License. - - http://creativecommons.org/licenses/by/3.0/legalcode - - Convention for heading levels in Neutron devref: - ======= Heading 0 (reserved for the title in a document) - ------- Heading 1 - ~~~~~~~ Heading 2 - +++++++ Heading 3 - ''''''' Heading 4 - (Avoid deeper levels because they do not render well.) - -================================ -Active/Passive High Availability -================================ - -Overview --------- - -Initially it was assumed that there will only be a single kuryr-controller -instance in the Kuryr-Kubernetes deployment. While it simplified a lot of -controller code, it is obviously not a perfect situation. Having redundant -controllers can help with achieving higher availability and scalability of the -deployment. - -Now with introduction of possibility to run Kuryr in Pods on Kubernetes cluster -HA is much easier to be implemented. The purpose of this document is to explain -how will it work in practice. - - -Proposed Solution ------------------ - -There are two types of HA - Active/Passive and Active/Active. In this document -we'll focus on the former. A/P basically works as one of the instances being -the leader (doing all the exclusive tasks) and other instances waiting in -*standby* mode in case the leader *dies* to take over the leader role. As you -can see a *leader election* mechanism is required to make this work. - - -Leader election -~~~~~~~~~~~~~~~ - -The idea here is to use leader election mechanism based on Kubernetes -endpoints. The idea is neatly `explained on Kubernetes blog`_. Election is -based on Endpoint resources, that hold annotation about current leader and its -leadership lease time. If leader dies, other instances of the service are free -to take over the record. Kubernetes API mechanisms will provide update -exclusion mechanisms to prevent race conditions. - -This can be implemented by adding another *leader-elector* container to each -of kuryr-controller pods: - -.. code-block:: yaml - - - image: gcr.io/google_containers/leader-elector:0.5 - name: leader-elector - args: - - "--election=kuryr-controller" - - "--http=0.0.0.0:${KURYR_CONTROLLER_HA_PORT:-16401}" - - "--election-namespace=kube-system" - - "--ttl=5s" - ports: - - containerPort: ${KURYR_CONTROLLER_HA_PORT:-16401} - protocol: TCP - -This adds a new container to the pod. This container will do the -leader-election and expose the simple JSON API on port 16401 by default. This -API will be available to kuryr-controller container. - - -Kuryr Controller Implementation -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The main issue with having multiple controllers is task division. All of the -controllers are watching the same endpoints and getting the same notifications, -but those notifications cannot be processed by multiple controllers at once, -because we end up with a huge race condition, where each controller creates -Neutron resources but only one succeeds to put the annotation on the Kubernetes -resource it is processing. - -This is obviously unacceptable so as a first step we're implementing A/P HA, -where only the leader is working on the resources and the other instances wait -as standby. This will be implemented by periodically calling the leader-elector -API to check the current leader. On leader change: - -* Pod losing the leadership will stop its Watcher. Please note that it will be - stopped gracefully, so all the ongoing operations will be completed. -* Pod gaining the leadership will start its Watcher. Please note that it will - get notified about all the previously created Kubernetes resources, but will - ignore them as they already have the annotations. -* Pods not affected by leadership change will continue to be in standby mode - with their Watchers stopped. - -Please note that this means that in HA mode Watcher will not get started on -controller startup, but only when periodic task will notice that it is the -leader. - - -Issues -~~~~~~ - -There are certain issues related to orphaned OpenStack resources that we may -hit. Those can happen in two cases: - -* Controller instance dies instantly during request processing. Some of - OpenStack resources were already created, but information about them was not - yet annotated onto Kubernetes resource. Therefore information is lost and we - end up with orphaned OpenStack resources. New leader will process the - Kubernetes resource by creating resources again. -* During leader transition (short period after a leader died, but before its - lease expired and periodic task on other controllers noticed that; this - shouldn't exceed 10s) some K8s resources are deleted. New leader will not - get the notification about the deletion and those will go unnoticed. - -Both of this issues can be tackled by garbage-collector mechanism that will -periodically look over Kubernetes resources and delete OpenStack resources that -have no representation in annotations. - -The latter of the issues can also be tackled by saving last seen -``resourceVersion`` of watched resources list when stopping the Watcher and -restarting watching from that point. - - -Future enhancements -~~~~~~~~~~~~~~~~~~~ - -It would be useful to implement the garbage collector and -``resourceVersion``-based protection mechanism described in section above. - -Besides that to further improve the scalability, we should work on -Active/Active HA model, where work is divided evenly between all of the -kuryr-controller instances. This can be achieved e.g. by using -consistent hash ring to decide which instance will process which resource. - -Potentially this can be extended with support for non-containerized deployments -by using Tooz and some other tool providing leader-election - like Consul or -Zookeeper. - - -.. _explained on Kubernetes blog: https://kubernetes.io/blog/2016/01/simple-leader-election-with-kubernetes/ diff --git a/doc/source/devref/index.rst b/doc/source/devref/index.rst deleted file mode 100644 index 86897b8f8..000000000 --- a/doc/source/devref/index.rst +++ /dev/null @@ -1,56 +0,0 @@ -.. - 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. - - Convention for heading levels in Neutron devref: - ======= Heading 0 (reserved for the title in a document) - ------- Heading 1 - ~~~~~~~ Heading 2 - +++++++ Heading 3 - ''''''' Heading 4 - (Avoid deeper levels because they do not render well.) - - -=========================== -Design and Developer Guides -=========================== - -In the Design and Developer Guides, you will find information on kuryr -kubernetes integration plans and design decisions. There are sections that -contain specific integration components description and detailed designs. -Finally, the developer guide includes information about kuryr kubernetes -testing infrastructure. - - -Design documents ----------------- -.. toctree:: - :maxdepth: 3 - - kuryr_kubernetes_design - service_support - port_manager - vif_handler_drivers_design - health_manager - high_availability - kuryr_kubernetes_versions - network_policy - updating_pod_resources_api - annotation_project_driver - - -Indices and tables ------------------- - -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` diff --git a/doc/source/devref/kuryr_kubernetes_design.rst b/doc/source/devref/kuryr_kubernetes_design.rst deleted file mode 100644 index 4e6327665..000000000 --- a/doc/source/devref/kuryr_kubernetes_design.rst +++ /dev/null @@ -1,328 +0,0 @@ -.. - This work is licensed under a Creative Commons Attribution 3.0 Unported - License. - - http://creativecommons.org/licenses/by/3.0/legalcode - - Convention for heading levels in Neutron devref: - ======= Heading 0 (reserved for the title in a document) - ------- Heading 1 - ~~~~~~~ Heading 2 - +++++++ Heading 3 - ''''''' Heading 4 - (Avoid deeper levels because they do not render well.) - -=================================== -Kuryr Kubernetes Integration Design -=================================== - -Purpose -------- - -The purpose of this document is to present the main Kuryr-K8s integration -components and capture the design decisions of each component currently taken -by the Kuryr team. - - -Goal Statement --------------- - -Enable OpenStack Neutron realization of the Kubernetes networking. Start by -supporting network connectivity and expand to support advanced features, such -as Network Policies. In the future, it may be extended to some other -OpenStack services. - - -Overview --------- - -In order to integrate Neutron into Kubernetes networking, 2 components are -introduced: Controller and CNI Driver. Controller is a supervisor component -responsible to maintain translation of networking relevant Kubernetes model -into the OpenStack (i.e. Neutron) model. This can be considered as a -centralized service (supporting HA mode in the future). CNI driver is -responsible for binding Kubernetes pods on worker nodes into Neutron ports -ensuring requested level of isolation. Please see below the component view of -the integrated system: - -.. image:: ../../images/kuryr_k8s_components.png - :alt: integration components - :align: center - :width: 100% - - -Design Principles ------------------ - -#. Loose coupling between integration components. -#. Flexible deployment options to support different project, subnet and - security groups assignment profiles. -#. The communication of the pod binding data between Controller and CNI driver - should rely on existing communication channels, currently through the - KuryrPort CRDs. -#. CNI Driver should not depend on Neutron. It gets all required details - from Kubernetes API server (currently through Kubernetes CRDs), - therefore depending on Controller to perform its translation tasks. -#. Allow different neutron backends to bind Kubernetes pods without code - modification. This means that both Controller and CNI binding mechanism - should allow loading of the vif management and binding components, - manifested via configuration. If some vendor requires some extra code, it - should be handled in one of the stevedore drivers. - - -Kuryr Controller Design ------------------------ - -Controller is responsible for watching Kubernetes API endpoints to make sure -that the corresponding model is maintained in Neutron. Controller updates K8s -resources endpoints' annotations and/or CRDs to keep neutron details required -by the CNI driver as well as for the model mapping persistency. - -Controller is composed from the following components: - - -Watcher -~~~~~~~ - -Watcher is a common software component used by both the Controller and the CNI -driver. Watcher connects to Kubernetes API. Watcher's responsibility is to -observe the registered (either on startup or dynamically during its runtime) -endpoints and invoke registered callback handler (pipeline) to pass all events -from registered endpoints. As an example, if a Service is created at the -Kubernetes end, the ServiceHandler which is watching the Service Objects uses -the watcher to detect the changes on them and calls the right driver for the -reconciliation of Kubernetes and the needed OpenStack resources. - - - -Event Handler -~~~~~~~~~~~~~ - -EventHandler is an interface class for the Kubernetes event handling. There are -several 'wrapper' event handlers that can be composed to implement Controller -handling pipeline. - -**Retry** Event Handler is used for handling specified failures during event -processing. It can be used to 'wrap' another EventHandler and in case of -specified error will retry the wrapped event handler invocation within -specified timeout. In case of persistent failure, Retry will raise the wrapped -EventHandler exception. - -**Async** Event Handler is used to execute event handling asynchronously. -Events are grouped based on the specified 'thread_groups'. Events of the same -group are processed in order of arrival. Thread group maps to an unique K8s -resource (each Pod, Service, etc.). Async can be used to 'wrap' another -EventHandler. Queues per thread group are added dynamically once relevant -events arrive and removed once queue is empty. - -**LogExceptions** Event Handler suppresses exceptions and sends them to log -facility. - -**Dispatcher** is an Event Handler that distributes events to registered -handlers based on event content and handler predicate provided during event -handler registration. - - -ControllerPipeline -~~~~~~~~~~~~~~~~~~ - -ControllerPipeline serves as an event dispatcher of the Watcher for Kuryr-K8s -controller Service. Currently watched endpoints are 'pods', 'services' and -'endpoints'. Kubernetes resource event handlers (Event Consumers) are -registered into the Controller Pipeline. There is a special EventConsumer, -ResourceEventHandler, that provides API for Kubernetes event handling. When a -watched event arrives, it is processed by all Resource Event Handlers -registered for specific Kubernetes object kind. Pipeline retries on resource -event handler invocation in case of the ResourceNotReady exception till it -succeeds or the number of retries (time-based) is reached. Any unrecovered -failure is logged without affecting other Handlers (of the current and other -events). Events of the same group (same Kubernetes object) are handled -sequentially in the order arrival. Events of different Kubernetes objects are -handled concurrently. - -.. image:: ../..//images/controller_pipeline.png - :alt: controller pipeline - :align: center - :width: 100% - - -ResourceEventHandler -~~~~~~~~~~~~~~~~~~~~ - -ResourceEventHandler is a convenience base class for the Kubernetes event -processing. The specific Handler associates itself with specific Kubernetes -object kind (through setting OBJECT_KIND) and is expected to implement at -least one of the methods of the base class to handle at least one of the -ADDED/MODIFIED/DELETED events of the Kubernetes object. For details, see -`k8s-api`_. Since both ADDED and MODIFIED event types trigger very similar -sequence of actions, Handler has 'on_present' method that is invoked for both -event types. The specific Handler implementation should strive to put all the -common ADDED and MODIFIED event handling logic in this method to avoid code -duplication. - - -Pluggable Handlers -~~~~~~~~~~~~~~~~~~ - -Starting with the Rocky release, Kuryr-Kubernetes includes a pluggable -interface for the Kuryr controller handlers. - -The pluggable handlers framework allows : - -- Using externally provided handlers. -- Controlling which handlers should be active. - -To control which Kuryr Controller handlers should be active, the selected -handlers need to be included in kuryr.conf at the 'kubernetes' section. -If not specified, Kuryr Controller will run the default handlers, which -currently includes the following: - -====================== ========================= - Handler Kubernetes resource -====================== ========================= -vif Pod -kuryrport KuryrPort CRD -endpoints Endpoints -service Service -kuryrloadbalancer KuryrLoadBalancer CRD -kuryrnetwork KuryrNetwork CRD -namespace Namespaces -kuryrnetworkpolicy KuryrNetworkPolicy CRD -podlabel Pod -policy NetworkPolicy -machine Machine -kuryrnetworkpopulation KuryrNetwork CRD - -====================== ========================= - -For example, to enable only the 'vif' controller handler we should set the -following at kuryr.conf: - -.. code-block:: ini - - [kubernetes] - enabled_handlers=vif,kuryrport - -Note, that we have to specify vif and kuryrport together, since currently those -two plugins works together. - -Providers -~~~~~~~~~ - -Provider (Drivers) are used by ResourceEventHandlers to manage specific aspects -of the Kubernetes resource in the OpenStack domain. For example, creating a -Kubernetes Pod will require a neutron port to be created on a specific network -with the proper security groups applied to it. There will be dedicated Drivers -for Project, Subnet, Port and Security Groups settings in neutron. For -instance, the Handler that processes pod events, will use PodVIFDriver, -PodProjectDriver, PodSubnetsDriver and PodSecurityGroupsDriver. The Drivers -model is introduced in order to allow flexibility in the Kubernetes model -mapping to the OpenStack. There can be different drivers that do Neutron -resources management, i.e. create on demand or grab one from the precreated -pool. There can be different drivers for the Project management, i.e. single -Tenant or multiple. Same goes for the other drivers. There are drivers that -handle the Pod based on the project, subnet and security groups specified via -configuration settings during cluster deployment phase. - - -NeutronPodVifDriver -~~~~~~~~~~~~~~~~~~~ - -PodVifDriver subclass should implement request_vif, release_vif and -activate_vif methods. In case request_vif returns Vif object in down state, -Controller will invoke activate_vif. Vif 'active' state is required by the -CNI driver to complete pod handling. -The NeutronPodVifDriver is the default driver that creates neutron port upon -Pod addition and deletes port upon Pod removal. - - -CNI Driver ----------- - -CNI driver is just a thin client that passes CNI ADD and DEL requests to -kuryr-daemon instance via its HTTP API. It's a simple executable that is -supposed to be called by kubelet's CNI. Since Train release the CNI driver -has an alternative golang implementation (see the kuryr_cni directory) to make -injecting it onto the Kubernetes node from the kuryr-cni pod easier. This -enables Kuryr to work on K8s deployments that does not have Python or curl on -Kubernetes nodes. Compatibility between Python and golang CNI drivers is -supposed to be maintained. - -.. _cni-daemon: - - -CNI Daemon ----------- - -CNI Daemon is a service that should run on every Kubernetes node. Starting from -Rocky release it should be seen as a default supported deployment option. And -running without it is impossible starting from Stein release. It is responsible -for watching pod events on the node it's running on, answering calls from CNI -Driver and attaching VIFs when they are ready. In the future it will also keep -information about pooled ports in memory. This helps to limit the number of -processes spawned when creating multiple Pods, as a single Watcher is enough -for each node and CNI Driver will only wait on local network socket for -response from the Daemon. - -Currently CNI Daemon consists of two processes i.e. Watcher and Server. -Processes communicate between each other using Python's -``multiprocessing.Manager`` and a shared dictionary object. Watcher is -responsible for extracting VIF information from KuryrPort CRD events and -putting them into the shared dictionary. Server is a regular WSGI server that -will answer CNI Driver calls. When a CNI request comes, Server is waiting for -VIF object to appear in the shared dictionary. As CRD data is read from -kubernetes API and added to the registry by Watcher thread, Server will -eventually get VIF it needs to connect for a given pod. Then it waits for the -VIF to become active before returning to the CNI Driver. - - -Communication -~~~~~~~~~~~~~ - -CNI Daemon Server is starting an HTTP server on a local network socket -(``127.0.0.1:5036`` by default). Currently server is listening for 2 API -calls. Both calls load the ``CNIParameters`` from the body of the call (it is -expected to be JSON). - -For reference see updated pod creation flow diagram: - -.. image:: ../../images/pod_creation_flow_daemon.png - :alt: Controller-CNI-daemon interaction - :align: center - :width: 100% - - -/addNetwork -+++++++++++ - -**Function**: Is equivalent of running ``K8sCNIPlugin.add``. - -**Return code:** 202 Accepted - -**Return body:** Returns VIF data in JSON form. This is serialized -oslo.versionedobject from ``os_vif`` library. On the other side it can be -deserialized using o.vo's ``obj_from_primitive()`` method. - - -/delNetwork -+++++++++++ - -**Function**: Is equivalent of running ``K8sCNIPlugin.delete``. - -**Return code:** 204 No content - -**Return body:** None. - -When running in daemonized mode, CNI Driver will call CNI Daemon over those -APIs to perform its tasks and wait on socket for result. - - -Kubernetes Documentation ------------------------- - -The `Kubernetes reference documentation`_ is a great source for finding more -details about Kubernetes API, CLIs, and tools. - - -.. _k8s-api: https://github.com/kubernetes/kubernetes/blob/release-1.4/docs/devel/api-conventions.md#types-kinds> -.. _Kubernetes reference documentation: https://kubernetes.io/docs/reference/ diff --git a/doc/source/devref/kuryr_kubernetes_versions.rst b/doc/source/devref/kuryr_kubernetes_versions.rst deleted file mode 100644 index 2f9726fb7..000000000 --- a/doc/source/devref/kuryr_kubernetes_versions.rst +++ /dev/null @@ -1,43 +0,0 @@ -=============================================== -Kubernetes and OpenShift version support matrix -=============================================== - -This document maintains updated documentation about what Kubernetes and -OpenShift versions are supported at each Kuryr-Kubernetes release. - - -.. note:: - - In general Kuryr should work fine with older versions of Kubernetes and - OpenShift as well as it only depends from the APIs that are quite stable - in Kubernetes itself. However we try to limit the number of supported - versions, as Kubernetes policy is to only support last 3 minor releases. - -.. note:: - - Kuryr-Kubernetes follows *cycle-with-intermediary* release model and that's - why there are multiple minor releases per single OpenStack release. Going - forward it is possible that Kuryr-Kubernetes will switch to *independent* - release model, that would completely untie it from OpenStack releases. This - is because it seems to be easier to follow Kubernetes releases than - OpenStack releases. - -.. warning:: - - In most cases only the latest supported version is tested in the CI/CD - system. - -======================== ====================================== ======================== -Kuryr-Kubernetes version Kubernetes version OpenShift Origin version -======================== ====================================== ======================== -master (Victoria) v1.16.x, v1.17.x, v1.18.x 4.3, 4.4, 4.5 -2.0.0 (Ussuri) v1.14.x, v1.15.x, v1.16.x 3.11, 4.2 -1.1.0 (Train) v1.13.x, v1.14.x, v1.15.x 3.9, 3.10, 3.11, 4.2 -0.6.x, 1.0.0 (Stein) v1.11.x, v1.12.x, v1.13.x 3.9, 3.10, 3.11, 4.2 -0.5.2-3 (Rocky) v1.9.x, v1.10.x, v1.11.x, v1.12.x 3.9, 3.10 -0.5.0-1 (Rocky) v1.9.x, v1.10.x 3.9, 3.10 -0.4.x (Queens) v1.8.x 3.7 -0.3.0 (Queens) v1.6.x, v1.8.x No support -0.2.x (Pike) v1.4.x, v1.6.x No support -0.1.0 (Pike) v1.3.x, v1.4.x No support -======================== ====================================== ======================== diff --git a/doc/source/devref/network_policy.rst b/doc/source/devref/network_policy.rst deleted file mode 100644 index ce01c3c5c..000000000 --- a/doc/source/devref/network_policy.rst +++ /dev/null @@ -1,521 +0,0 @@ -.. - This work is licensed under a Creative Commons Attribution 3.0 Unported - License. - - http://creativecommons.org/licenses/by/3.0/legalcode - - Convention for heading levels in Neutron devref: - ======= Heading 0 (reserved for the title in a document) - ------- Heading 1 - ~~~~~~~ Heading 2 - +++++++ Heading 3 - ''''''' Heading 4 - (Avoid deeper levels because they do not render well.) - -============== -Network Policy -============== - -Purpose --------- - -The purpose of this document is to present how Network Policy is supported by -Kuryr-Kubernetes. - - -Overview --------- - -Kubernetes supports a Network Policy object to express ingress and egress rules -for pods. Network Policy reacts on labels to qualify multiple pods, and defines -rules based on different labeling and/or CIDRs. When combined with a networking -plugin, those policy objects are enforced and respected. - - -Proposed Solution ------------------ - -Kuryr-Kubernetes relies on Neutron security groups and security group rules to -enforce a Network Policy object, more specifically one security group per policy -with possibly multiple rules. Each object has a namespace scoped Network Policy -CRD that stores all OpenStack related resources on the Kubernetes side, avoiding -many calls to Neutron and helping to differentiate between the current Kubernetes -status of the Network Policy and the last one Kuryr-Kubernetes enforced. - -The network policy CRD has the following format: - -.. code-block:: yaml - - apiVersion: openstack.org/v1 - kind: KuryrNetworkPolicy - metadata: - ... - spec: - egressSgRules: - - sgRule: - ... - ingressSgRules: - - sgRule: - ... - podSelector: - ... - status: - securityGroupId: ... - podSelector: ... - securityGroupRules: ... - -A new handler has been added to react to Network Policy events, and the existing -ones, for instance service/pod handlers, have been modified to account for the -side effects/actions of when a Network Policy is being enforced. - -.. note:: - - Kuryr supports a network policy that contains: - - * Ingress and Egress rules - * namespace selector and pod selector, defined with match labels or match - expressions, mix of namespace and pod selector, ip block - * named port - - -New handlers and drivers -~~~~~~~~~~~~~~~~~~~~~~~~ - -The Network Policy handler -++++++++++++++++++++++++++ - -This handler is responsible for triggering the Network Policy Spec processing, -and the creation or removal of security group with appropriate security group -rules. It also, applies the security group to the pods and services affected -by the policy. - - -The Pod Label handler -+++++++++++++++++++++ - -This new handler is responsible for triggering the update of a security group -rule upon pod labels changes, and its enforcement on the pod port and service. - - -The Network Policy driver -++++++++++++++++++++++++++ - -Is the main driver. It ensures a Network Policy by processing the Spec -and creating or updating the Security group with appropriate -security group rules. - - -The Network Policy Security Group driver -++++++++++++++++++++++++++++++++++++++++ - -It is responsible for creating, deleting, or updating security group rules -for pods, namespaces or services based on different Network Policies. - - -Modified handlers and drivers -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The KuryrPort handler -+++++++++++++++++++++ - -As network policy rules can be defined based on pod labels, this handler -has been enhanced to trigger a security group rule creation or deletion, -depending on the type of pod event, if the pod is affected by the network -policy and if a new security group rule is needed. Also, it triggers the -translation of the pod rules to the affected service. Note, that KuryrPort -takes over most of the VIF handler functionality, although it has to be enabled -together with VIF handler. - - -The Namespace handler -+++++++++++++++++++++ - -Just as the pods labels, namespaces labels can also define a rule in a -Network Policy. To account for this, the namespace handler has been -incremented to trigger the creation, deletion or update of a -security group rule, in case the namespace affects a Network Policy rule, -and the translation of the rule to the affected service. - - -The Namespace Subnet driver -+++++++++++++++++++++++++++ - -In case of a namespace event and a Network Policy enforcement based -on the namespace, this driver creates a subnet to this namespace, -and restrict the number of security group rules for the Network Policy -to just one with the subnet CIDR, instead of one for each pod in the namespace. - - -The LBaaS driver -++++++++++++++++ - -To restrict the incoming traffic to the backend pods, the LBaaS driver -has been enhanced to translate pods rules to the listener port, and react -to Service ports updates. E.g., when the target port is not allowed by the -policy enforced in the pod, the rule should not be added. - - -The VIF Pool driver -+++++++++++++++++++ - -The VIF Pool driver is responsible for updating the Security group applied -to the pods ports. It has been modified to embrace the fact that with Network -Policies pods' ports changes their security group while being used, meaning the -original pool does not fit them anymore, resulting in useless pools and ports -reapplying the original security group. To avoid it, the security group id -is removed from the pool merging all pools with same network, project -and host id. Thus if there is no ports on the pool with the needed -security group id(s), one of the existing ports in the pool is updated -to match the requested sg Id. - - -Use cases examples -~~~~~~~~~~~~~~~~~~ - -This section describes some scenarios with a Network Policy being enforced, -what Kuryr components gets triggered and what resources are created. - - -Deny all incoming traffic -+++++++++++++++++++++++++ - -By default, Kubernetes clusters do not restrict traffic. Only once a network -policy is enforced to a namespace, all traffic not explicitly allowed in the -policy becomes denied. As specified in the following policy: - -.. code-block:: yaml - - apiVersion: networking.k8s.io/v1 - kind: NetworkPolicy - metadata: - name: default-deny - spec: - podSelector: {} - policyTypes: - - Ingress - -The following CRD is the translation of policy rules to security group rules. -No ingress rule was created, which means traffic is blocked, and since -there is no restriction for egress traffic, it is allowed to everywhere. Note -that the same happens when no ``policyType`` is defined, since all policies -are assumed to affect Ingress. - -.. code-block:: yaml - - apiVersion: openstack.org/v1 - kind: KuryrNetworkPolicy - metadata: - name: default-deny - namespace: default - ... - spec: - egressSgRules: - - sgRule: - description: Kuryr-Kubernetes NetPolicy SG rule - direction: egress - ethertype: IPv4 - security_group_id: 20d9b623-f1e0-449d-95c1-01624cb3e315 - ingressSgRules: [] - podSelector: - ... - status: - securityGroupId: 20d9b623-f1e0-449d-95c1-01624cb3e315 - securityGroupRules: ... - podSelector: ... - - -Allow traffic from pod -++++++++++++++++++++++ - -The following Network Policy specification has a single rule allowing traffic -on a single port from the group of pods that have the label ``role=monitoring``. - -.. code-block:: yaml - - apiVersion: networking.k8s.io/v1 - kind: NetworkPolicy - metadata: - name: allow-monitoring-via-pod-selector - spec: - podSelector: - matchLabels: - app: server - policyTypes: - - Ingress - ingress: - - from: - - podSelector: - matchLabels: - role: monitoring - ports: - - protocol: TCP - port: 8080 - -Create the following pod with label ``role=monitoring``: - -.. code-block:: console - - $ kubectl create deployment monitor --image=busybox --restart=Never --labels=role=monitoring - -The generated CRD contains an ingress rule allowing traffic on port 8080 from -the created pod, and an egress rule allowing traffic to everywhere, since no -restriction was enforced. - -.. code-block:: yaml - - apiVersion: openstack.org/v1 - kind: KuryrNetworkPolicy - metadata: - name: allow-monitoring-via-pod-selector - namespace: default - ... - spec: - egressSgRules: - - sgRule: - description: Kuryr-Kubernetes NetPolicy SG rule - direction: egress - ethertype: IPv4 - ingressSgRules: - - namespace: default - sgRule: - description: Kuryr-Kubernetes NetPolicy SG rule - direction: ingress - ethertype: IPv4 - port_range_max: 8080 - port_range_min: 8080 - protocol: tcp - remote_ip_prefix: 10.0.1.143 - podSelector: - ... - status: - securityGroupId: 7f0ef8c2-4846-4d8c-952f-94a9098fff17 - securityGroupRules: ... - podSelector: ... - - -Allow traffic from namespace -++++++++++++++++++++++++++++ - -The following network policy only allows ingress traffic -from namespace with the label ``purpose=test``: - -.. code-block:: yaml - - apiVersion: networking.k8s.io/v1 - kind: NetworkPolicy - metadata: - name: allow-test-via-ns-selector - spec: - podSelector: - matchLabels: - app: server - policyTypes: - - Ingress - ingress: - - from: - - namespaceSelector: - matchLabels: - purpose: test - ports: - - protocol: TCP - port: 8080 - -Create a namespace and label it with ``purpose=test``: - -.. code-block:: console - - $ kubectl create namespace dev - $ kubectl label namespace dev purpose=test - -The resulting CRD has an ingress rule allowing traffic -from the namespace CIDR on the specified port, and an -egress rule allowing traffic to everywhere. - -.. code-block:: yaml - - apiVersion: openstack.org/v1 - kind: KuryrNetworkPolicy - name: allow-test-via-ns-selector - namespace: default - ... - spec: - egressSgRules: - - sgRule: - description: Kuryr-Kubernetes NetPolicy SG rule - direction: egress - ethertype: IPv4 - ingressSgRules: - - namespace: dev - sgRule: - description: Kuryr-Kubernetes NetPolicy SG rule - direction: ingress - ethertype: IPv4 - port_range_max: 8080 - port_range_min: 8080 - protocol: tcp - remote_ip_prefix: 10.0.1.192/26 - podSelector: - ... - status: - securityGroupId: c480327c-2db4-4eb6-af1e-eeb0ce9b46c9 - securityGroupRules: ... - podSelector: ... - -.. note:: - - Only when using Amphora Octavia provider and Services with selector, - the Load Balancer security groups need to be rechecked when a - network policy that affects ingress traffic is created, and - also everytime a pod or namespace is created. Network Policy - is not enforced on Services without Selectors. - -Allow traffic to a Pod in a Namespace -+++++++++++++++++++++++++++++++++++++ - -The following network policy only allows egress traffic from Pods -with the label ``app=demo`` at Namespace with label ``app=demo``: - -.. code-block:: yaml - - apiVersion: networking.k8s.io/v1 - kind: NetworkPolicy - metadata: - name: block-egress - namespace: client - spec: - podSelector: - matchLabels: - app: client - policyTypes: - - Egress - egress: - - to: - - namespaceSelector: - matchLabels: - app: demo - podSelector: - matchLabels: - app: demo - -The resulting CRD has an ingress rules allowing traffic -from everywhere and egress rules to the selected Pod -and the Service that points to it. - -.. code-block:: yaml - - apiVersion: openstack.org/v1 - kind: KuryrNetworkPolicy - metadata: ... - spec: - egressSgRules: - - namespace: demo - sgRule: - description: Kuryr-Kubernetes NetPolicy SG rule - direction: egress - ethertype: IPv4 - port_range_max: 65535 - port_range_min: 1 - protocol: tcp - remote_ip_prefix: 10.0.2.120 - - sgRule: - description: Kuryr-Kubernetes NetPolicy SG rule - direction: egress - ethertype: IPv4 - port_range_max: 65535 - port_range_min: 1 - protocol: tcp - remote_ip_prefix: 10.0.0.144 - ingressSgRules: - - sgRule: - description: Kuryr-Kubernetes NetPolicy SG rule - direction: ingress - ethertype: IPv4 - - sgRule: - description: Kuryr-Kubernetes NetPolicy SG rule - direction: ingress - ethertype: IPv6 - podSelector: - matchLabels: - app: client - policyTypes: - - Egress - status: - podSelector: - matchLabels: - app: client - securityGroupId: 322a347b-0684-4aea-945a-5f204361a64e - securityGroupRules: ... - -.. note:: - - A Network Policy egress rule creates a Security Group rule - corresponding to a Service(with or without selectors) that - points to the selected Pod. - - -Create network policy flow -++++++++++++++++++++++++++ - -.. image:: ../../images/create_network_policy_flow.svg - :alt: Network Policy creation flow - :align: center - :width: 100% - - -Create pod flow -+++++++++++++++ - -The following diagram only covers the implementation part that affects -network policy. - -.. image:: ../../images/update_network_policy_on_pod_creation.svg - :alt: Pod creation flow - :align: center - :width: 100% - - -Network policy rule definition -++++++++++++++++++++++++++++++ - -======================== ======================= ============================================== -NamespaceSelector podSelector Expected result -======================== ======================= ============================================== -namespaceSelector: ns1 podSelector: pod1 Allow traffic from pod1 at ns1 -namespaceSelector: ns1 podSelector: {} Allow traffic from all pods at ns1 -namespaceSelector: ns1 none Allow traffic from all pods at ns1 -namespaceSelector: {} podSelector: pod1 Allow traffic from pod1 from all namespaces -namespaceSelector: {} podSelector: {} Allow traffic from all namespaces -namespaceSelector: {} none Allow traffic from all namespaces -none podSelector: pod1 Allow traffic from pod1 from NP namespace -none podSelector: {} Allow traffic from all pods from NP namespace -======================== ======================= ============================================== - -======================== ================================================ -Rules definition Expected result -======================== ================================================ -No FROM (or from: []) Allow traffic from all pods from all namespaces -Ingress: {} Allow traffic from all namespaces -ingress: [] Deny all traffic -No ingress Blocks all traffic -======================== ================================================ - - -Policy types definition -+++++++++++++++++++++++ - -=============== ===================== ======================= ====================== -PolicyType Spec Ingress/Egress Ingress generated rules Egress generated rules -=============== ===================== ======================= ====================== -none none BLOCK ALLOW -none ingress Specific rules ALLOW -none egress Block Specific rules -none ingress, egress Specific rules Specific rules -ingress none Block ALLOW -ingress ingress Specific rules ALLOW -egress none ALLOW BLOCK -egress egress ALLOW Specific rules -Ingress, egress none BLOCK BLOCK -Ingress, egress ingress Specific rules BLOCK -Ingress, egress egress BLOCK Specific rules -Ingress, egress ingress,egress Specific rules Specific rules -=============== ===================== ======================= ====================== diff --git a/doc/source/devref/port_manager.rst b/doc/source/devref/port_manager.rst deleted file mode 100644 index a504c7db9..000000000 --- a/doc/source/devref/port_manager.rst +++ /dev/null @@ -1,195 +0,0 @@ -.. - This work is licensed under a Creative Commons Attribution 3.0 Unported - License. - - http://creativecommons.org/licenses/by/3.0/legalcode - - Convention for heading levels in Neutron devref: - ======= Heading 0 (reserved for the title in a document) - ------- Heading 1 - ~~~~~~~ Heading 2 - +++++++ Heading 3 - ''''''' Heading 4 - (Avoid deeper levels because they do not render well.) - -==================================== -Kuryr Kubernetes Port Manager Design -==================================== - -Purpose -------- - -The purpose of this document is to present Kuryr Kubernetes Port Manager, -capturing the design decision currently taken by the kuryr team. - -The main purpose of the Port Manager is to perform Neutron resources handling, -i.e., ports creation and deletion. The main idea behind is to try to minimize -the amount of calls to Neutron by ensuring port reuse as well as performing -bulk actions, e.g., creating/deleting several ports within the same Neutron -call. - - -Overview --------- - -Interactions between Kuryr and Neutron may take more time than desired from -the container management perspective. - -Some of these interactions between Kuryr and Neutron can be optimized. For -instance, by maintaining pre-created pools of Neutron port resources instead -of asking for their creation during pod lifecycle pipeline. - -As an example, every time a container is created or deleted, there is a call -from Kuryr to Neutron to create/remove the port used by the container. To -optimize this interaction and speed up both container creation and deletion, -the Kuryr-Kubernetes Port Manager will take care of both: Neutron ports -creation beforehand, and Neutron ports deletion afterwards. This will -consequently remove the waiting time for: - -- Creating ports and waiting for them to become active when booting containers -- Deleting ports when removing containers - - -Proposed Solution ------------------ - -The Port Manager will be in charge of handling Neutron ports. The main -difference with the current implementation resides on when and how these -ports are managed. The idea behind is to minimize the amount of calls to the -Neutron server by reusing already created ports as well as by creating/deleting -them in bulk requests. - -This design focuses on Neutron ports management, but similar optimization can -be done for other Neutron resources, and consequently new resource managers -can be added. - -Ports Manager -~~~~~~~~~~~~~ - -The Port Manager will handle different pool of Neutron ports: - -- Available pools: There will be a pool of ports for each tenant, host (or - trunk port for the nested case) and security group, ready to be used by the - pods. Note at the beginning there are no pools. Once a pod is created at - a given host/VM by a tenant, with a specific security group, a corresponding - pool gets created and populated with the desired minimum amount of ports. - Note the Neutron port quota needs to be considered when configuring the - parameters of the pool, i.e., the minimum and/or maximum size of the pools as - well as the size of the bulk creation requests. -- Recyclable pool: Instead of deleting the port during pods removal it will - just be included into this pool. The ports in this pool will be later - recycled by the Port Manager and put back into the corresponding - available pool. - -The Port Manager will handle the available pools ensuring that at least X ports -are ready to be used at each existing pool, i.e., for each security group -and tenant which already has a pod on it. The port creation at each -available_pool will be handled in batches, i.e., instead of creating one port -at a time, a configurable amount of them will be created altogether. -The Port Manager will check for each pod creation that the remaining number of -ports in the specific pool is above X. Otherwise it creates Y extra ports for -that pool (with the specific tenant and security group). Note both X and Y are -configurable. - -Thanks to having the available ports pool, during the container creation -process, instead of calling Neutron port_create and then waiting for the port -to become active, a port will be taken from the right available_pool (hence, -no need to call Neutron) and then the port info will be updated with the -proper container name (i.e., call to Neutron port_update). Thus, thanks to the -Port Manager, at least two calls to Neutron are skipped (port create and -pooling waiting for port to become ACTIVE), while doing an extra one (update) -which is faster than the other ones. Similarly, for the port deletion we save -the call to remove the port as it is just included in the recyclable pool. - -The port cleanup actions return ports to the corresponding available_pool after -re-applying security groups and changing the device name to 'available-port'. -A maximum limit for the pool can be specified to ensure that once the -corresponding available_pool reach a certain size, the ports gets deleted -instead of recycled. Note this upper limit can be disabled by setting it to 0. -In addition, a Time-To-Live (TTL) could be set to the ports at the pool, so -that if they are not used during a certain period of time, they are removed -- -if and only if the available_pool size is still larger than the target minimum. - - -Recovery of pool ports upon Kuryr-Controller restart -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -If the Kuryr-Controller is restarted, the pre-created ports will still exist -on the Neutron side but the Kuryr-controller will be unaware of them, thus -pre-creating more upon pod allocation requests. To avoid having these existing -but unused ports a mechanisms is needed to either delete them after -controller's reboot, or obtain their information and re-include them into -their corresponding pool. - -For the baremetal (NeutronVIF) case, as the ports are not attached to any -hosts (at least not until CNI support is included) there is not enough -information to decide which pool should be selected for adding the port. -For simplicity, and as a temporal solution until CNI support is developed, -the implemented mechanism will find the previously created ports by looking -at the existing neutron ports and filtering them by device_owner and name, -which should be compute:kuryr and available-port, respectively. -Once these ports are obtained, they are deleted to release unused Neutron -resources and avoid problems related to ports quota limits. - -By contrast, it is possible to obtain all the needed information for the -subports previously created for the nested (VLAN+Trunk) case as they are still -attached to their respective trunk ports. Therefore, these ports instead of -being deleted will be re-added to their corresponding pools. -To do this, the Neutron ports are filtered by device_owner (trunk:subport in -this case) and name (available-port), and then we iterate over the subports -attached to each existing trunk port to find where the filtered ports are -attached and then obtain all the needed information to re-add them into the -corresponding pools. - - -Kuryr Controller Impact -~~~~~~~~~~~~~~~~~~~~~~~ - -A new VIF Pool driver is created to manage the ports pools upon pods creation -and deletion events. It will ensure that a pool with at least X ports is -available for each tenant, host or trunk port, and security group, when the -first request to create a pod with these attributes happens. Similarly, it will -ensure that ports are recycled from the recyclable pool after pods deletion and -are put back in the corresponding available_pool to be reused. Thanks to this -Neutron calls are skipped and the ports of the pools are used instead. If the -corresponding pool is empty, a ResourceNotReady exception will be triggered and -the pool will be repopulated. - -In addition to the handler modification and the new pool drivers there are -changes related to the VIF drivers. The VIF drivers (neutron-vif and nested) -will be extended to support bulk ports creation of Neutron ports and similarly -for the VIF objects requests. - - -Future enhancement -++++++++++++++++++ - -The VIFHandler needs to be aware of the new Pool driver, which will load the -respective VIF driver to be used. In a sense, the Pool Driver will be a proxy -to the VIF Driver, but also managing the pools. When a mechanism to load and -set the VIFHandler drivers is in place, this will be reverted so that the -VIFHandlers becomes unaware of the pool drivers. - - -Kuryr CNI Impact -~~~~~~~~~~~~~~~~ - -For the nested vlan case, the subports at the different pools are already -attached to the VMs trunk ports, therefore they are already in ACTIVE status. -However, for the generic case the ports are not really bond to anything (yet), -therefore their status will be DOWN. In order to keep these ports returned to -the pool in ACTIVE status, we will implement another pool at the CNI side for -the generic case. This solution could be different for different SDN -controllers. The main idea is that they should keep the port in ACTIVE -state without allowing network traffic through them. For instance, for the -Neutron reference implementation, this pool will maintain a pool of veth -devices at each host, by connecting them to a recyclable namespace so that the -OVS agent sees them as 'still connected' and maintains their ACTIVE status. -This modification must ensure the OVS (br-int) ports where these veth devices -are connected are not deleted after container deletion by the CNI. - - -Future enhancement -++++++++++++++++++ - -The CNI modifications will be implemented in a second phase. diff --git a/doc/source/devref/service_support.rst b/doc/source/devref/service_support.rst deleted file mode 100644 index 278f37dae..000000000 --- a/doc/source/devref/service_support.rst +++ /dev/null @@ -1,157 +0,0 @@ -.. - This work is licensed under a Creative Commons Attribution 3.0 Unported - License. - - http://creativecommons.org/licenses/by/3.0/legalcode - - Convention for heading levels in Neutron devref: - ======= Heading 0 (reserved for the title in a document) - ------- Heading 1 - ~~~~~~~ Heading 2 - +++++++ Heading 3 - ''''''' Heading 4 - (Avoid deeper levels because they do not render well.) - -============================================ -Kuryr Kubernetes Services Integration Design -============================================ - -Purpose -------- - -The purpose of this document is to present how Kubernetes Service is supported -by the kuryr integration and to capture the design decisions currently taken -by the kuryr team. - - -Overview --------- - -A Kubernetes Service is an abstraction which defines a logical set of Pods and -a policy by which to access them. Service is a Kubernetes managed API object. -For Kubernetes-native applications, Kubernetes offers an Endpoints API that is -updated whenever the set of Pods in a Service changes. For detailed information -please refer to `Kubernetes service`_. Kubernetes supports services with -kube-proxy component that runs on each node, `Kube-Proxy`_. - - -Proposed Solution ------------------ - -Kubernetes service in its essence is a Load Balancer across Pods that fit the -service selection. Kuryr's choice is to support Kubernetes services by using -Neutron LBaaS service. The initial implementation is based on the OpenStack -LBaaSv2 API, so compatible with any LBaaSv2 API provider. - -In order to be compatible with Kubernetes networking, Kuryr-Kubernetes makes -sure that services Load Balancers have access to Pods Neutron ports. This may -be affected once Kubernetes Network Policies will be supported. Oslo versioned -objects are used to keep translation details in Kubernetes entities annotation. -This will allow future changes to be backward compatible. - - -Data Model Translation -~~~~~~~~~~~~~~~~~~~~~~ - -Kubernetes service is mapped to the LBaaSv2 Load Balancer with associated -Listeners and Pools. Service endpoints are mapped to Load Balancer Pool -members. - - -Kuryr Controller Impact -~~~~~~~~~~~~~~~~~~~~~~~ - -Three Kubernetes Event Handlers are added to the Controller pipeline. - -- ServiceHandler manages Kubernetes Service events. - Based on the service spec and metadata details, it creates KuryrLoadBalancer - CRD or it updates the CRD, more specifically the spec part of the CRD with - details to be used for translation to LBaaSv2 model, such as tenant-id, - subnet-id, ip address and security groups. -- EndpointsHandler is responsible for adding endpoints subsets to the - KuryrLoadBalancer CRD. If endpoint is created before Service, this handler - creates the CRD with the endpoints subsets, otherwise the existent CRD is - updated. -- KuryrLoadBalancerHandler manages KuryrLoadBalancer CRD events when the CRD is - successfully created and filled with spec data. This handler is responsible - for creating the needed Octavia resources according to the CRD spec and - update the status field with information about the generated resources, such - as LoadBalancer, LoadBalancerListener, LoadBalancerPool and - LoadBalancerMembers. - -These Handlers use Project, Subnet and SecurityGroup service drivers to get -details for service mapping. - -In order to prevent Kubernetes objects from being deleted before the OpenStack -resources are cleaned up, finalizers are used. Finalizers block deletion of the -Service, Endpoints and KuryrLoadBalancer objects until Kuryr deletes the -associated OpenStack loadbalancers. After that the finalizers are removed -allowing the Kubernetes API to delete the objects. - -LBaaS Driver is added to manage service translation to the LBaaSv2-like API. It -abstracts all the details of service translation to Load Balancer. -LBaaSv2Driver supports this interface by mapping to neutron LBaaSv2 constructs. - - -Service Creation Process -~~~~~~~~~~~~~~~~~~~~~~~~ - -What happens when a service gets created by Kubernetes? -+++++++++++++++++++++++++++++++++++++++++++++++++++++++ - -When a Kubernetes Service and Endpoints are created,the ServiceHandler and -EndpointHandler (at controller/handlers/lbaas.py) are called. When the -ServiceHandler first starts handling the on_present event, it creates the -KuryrLoadBalancer CRD with the Service spec and empty status. When the -Endpoints information is not yet added on the spec by the EndpointHandler, the -event reaching KuryrLoadBalancerHandler is skipped. If the EndpointsHandler -starts handling the on_present event first, the KuryrLoadBalancer CRD is -created with the endpoints subsets. Otherwise, it will update the existing CRD -created by the ServiceHandler with the endpoints subsets. - -The KuryrLoadBalancerHandler (at controller/handlers/loadbalancer.py) upon -noticing the KuryrLoadBalancer CRD with the full specification, calls the -appropriate Drivers to handle the openstack resources such as Loadbalancer, -Load Balancer Listeners, Load Balancer Pools, and Load Balancer Members. It -uses the _sync_lbaas_members function to check if Openstack Loadbalancers are -in sync with the kubernetes counterparts. - - -.. figure:: ../../images/service_creation_diagram.svg - :alt: Service creation Diagram - :align: center - :width: 100% - - Service creation flow diagram - -Service Deletion Process -~~~~~~~~~~~~~~~~~~~~~~~~ - -What happens when a service gets deleted by Kubernetes? -+++++++++++++++++++++++++++++++++++++++++++++++++++++++ - -When a Kubernetes Service and Endpoints are deleted, the finalizers which are -added to the service object (and the KLB CRD Object too) and defined during the -KuryrLoadBalancer CRD creation, blocks the removal of the kubernetes object -until the associated OpenStack resources are removed, which also avoids -leftovers. When they are removed, Kubernetes is able to remove the CRD, the -service and the endpoints, hence completing the service removal action. - -What happens if the KuryrLoadBalancer CRD status changes? -+++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - -If the members field on the status of the CRD is manually removed or the status -is completely set to an empty object, the KuryrLoadBalancerHandler that is -watching these CRD objects detects this change and confirms that there is no -information about the OpenStack resources on the status and so needs to -rediscover or recreate them. It checks if there are provisioned OpenStack -resources (in this case loadbalancers, listeners, pools, and members) for the -service/endpoints defined on the KuryrLoadBalancer CRD spec. If that is the -case, it retrieves their information and puts it back on the CRD status field. -If that is not the case (due to the resources being deleted on the OpenStack -side), it will recreate the resources and write the new information about them -on the CRD status field. - - -.. _Kubernetes service: http://kubernetes.io/docs/user-guide/services/ -.. _Kube-Proxy: http://kubernetes.io/docs/admin/kube-proxy/ diff --git a/doc/source/devref/updating_pod_resources_api.rst b/doc/source/devref/updating_pod_resources_api.rst deleted file mode 100644 index 323eca9f6..000000000 --- a/doc/source/devref/updating_pod_resources_api.rst +++ /dev/null @@ -1,137 +0,0 @@ -.. - This work is licensed under a Creative Commons Attribution 3.0 Unported - License. - - http://creativecommons.org/licenses/by/3.0/legalcode - - Convention for heading levels in Neutron devref: - ======= Heading 0 (reserved for the title in a document) - ------- Heading 1 - ~~~~~~~ Heading 2 - +++++++ Heading 3 - ''''''' Heading 4 - (Avoid deeper levels because they do not render well.) - -================================== -HowTo Update PodResources gRPC API -================================== - -Purpose -------- - -The purpose of this document is to describe how to update gRPC API files in -kuryr-kubernetes repository in case of upgrading to a new version of Kubernetes -PodResources API. These files are ``api_pb2_grpc.py``, ``api_pb2.py`` and -``api.proto`` from ``kuryr_kubernetes/pod_resources/`` directory. - -``api.proto`` is a gRPC API definition file generated from the -``kubernetes/pkg/kubelet/apis/podresources//api.proto`` of the -Kubernetes source tree. - -``api_pb2_grpc.py`` and ``api_pb2.py`` are python bindings for gRPC API. - -.. note:: - - There are only 2 reasons for update: - - #. Kubernetes released new version of PodResources API and the old one is no - longer supported. In this case, without update, we'll not be able to use - PodResources service. - #. ``protobuf`` version in ``lower-constraints.txt`` changed to lower - version (this is highly unlikely). In this case ``protobuf`` could fail - to use our python bindings. - - -Automated update ----------------- - -``contrib/regenerate_pod_resources_api.sh`` script could be used to re-generate -PodResources gRPC API files. By default, this script will download ``v1alpha1`` -version of ``api.proto`` file from the Kubernetes GitHub repo and create -required kuryr-kubernetes files from it: - -.. code-block:: console - - [kuryr-kubernetes]$ ./contrib/regenerate_pod_resources_api.sh - -Alternatively, path to ``api.proto`` file could be specified in -``KUBERNETES_API_PROTO`` environment variable: - -.. code-block:: console - - $ export KUBERNETES_API_PROTO=/path/to/api.proto - -Define ``API_VERSION`` environment variable to use specific version of -``api.proto`` from the Kubernetes GitHub: - -.. code-block:: console - - $ export API_VERSION=v1alpha1 - - -Manual update steps -------------------- - -Preparing the new api.proto -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Copy the ``api.proto`` from K8s sources to ``kuryr_kubernetes/pod_resources/`` -and remove all the lines that contains ``gogoproto`` since this is unwanted -dependency that is not needed for python bindings: - -.. code-block:: console - - $ sed '/gogoproto/d' \ - ../kubernetes/pkg/kubelet/apis/podresources//api.proto \ - > kuryr_kubernetes/pod_resources/api.proto - -Don't forget to update the file header that should point to the original -``api.proto`` and to this reference document:: - - // Generated from kubernetes/pkg/kubelet/apis/podresources//api.proto - // To regenerate api.proto, api_pb2.py and api_pb2_grpc.py follow instructions - // from doc/source/devref/updating_pod_resources_api.rst. - - -Generating the python bindings -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -* (Optional) Create the python virtual environment: - -.. code-block:: console - - [kuryr-kubernetes]$ python3 -m venv venv - [kuryr-kubernetes]$ . ./venv/bin/activate - -* To generate python bindings we need a ``protoc`` compiler and the - ``gRPC plugin`` for it. The most simple way to get them is to install - ``grpcio-tools``: - - .. code-block:: console - - (venv) [kuryr-kubernetes]$ pip install grpcio-tools==1.19 - - .. note:: - - We're installing specific version of ``grpcio-tools`` to get specific - version of ``protoc`` compiler. The version of ``protoc`` compiler should - be equal to the ``protobuf`` package version in ``lower-constraints.txt``. - This is because older ``protobuf`` might be not able to use files - generated by newer compiler. In case you need to use more recent compiler, - you need update ``requirements.txt`` and ``lower-constraints.txt`` - accordingly. - - To check version of compiler installed with ``grpcio-tools`` use: - - .. code-block:: console - - (venv) [kuryr-kubernetes]$ python -m grpc_tools.protoc --version - libprotoc 3.6.1 - -* Following command will generate ``api_pb2_grpc.py`` and ``api_pb2.py``: - - .. code-block:: console - - (venv) [kuryr-kubernetes]$ python -m grpc_tools.protoc -I./ \ - --python_out=. --grpc_python_out=. \ - kuryr_kubernetes/pod_resources/api.proto diff --git a/doc/source/devref/vif_handler_drivers_design.rst b/doc/source/devref/vif_handler_drivers_design.rst deleted file mode 100644 index 15ae21cc9..000000000 --- a/doc/source/devref/vif_handler_drivers_design.rst +++ /dev/null @@ -1,157 +0,0 @@ -.. - This work is licensed under a Creative Commons Attribution 3.0 Unported - License. - - http://creativecommons.org/licenses/by/3.0/legalcode - - Convention for heading levels in Neutron devref: - ======= Heading 0 (reserved for the title in a document) - ------- Heading 1 - ~~~~~~~ Heading 2 - +++++++ Heading 3 - ''''''' Heading 4 - (Avoid deeper levels because they do not render well.) - -================================== -VIF-Handler And Vif Drivers Design -================================== - -Purpose -------- - -The purpose of this document is to present an approach for implementing -design of interaction between VIF-handler and the drivers it uses in -Kuryr-Kubernetes Controller. - - -VIF-Handler ------------ - -VIF-handler was intended to handle VIFs. Currently it is responsible for -reacting on Pod object events, and for creating/deleting corresponding -KuryrPort CRD object. - - -KuryrPort-handler ------------------ - -KuryrPort is responsible for taking care about associated Pod VIFs. The main -aim of this handler is to get the KuryrPort CRD object, created by the -VIF-handler, send it to the: - -- VIF-driver for the default network, -- enabled Multi-VIF drivers for the additional networks, -- and get VIF objects from both. - -After that KuryrPort-handler is able to activate, release or update VIFs. -KuryrPort-handler should stay clean whereas parsing of specific pod information -should be done by Multi-VIF drivers. - - -Multi-VIF driver -~~~~~~~~~~~~~~~~ - -The new type of drivers which is used to call other VIF-drivers to attach -additional interfaces to Pods. The main aim of this kind of drivers is to get -additional interfaces from the Pods definition, then invoke real VIF-drivers -like neutron-vif, nested-macvlan to retrieve the VIF objects accordingly. - -All Multi-VIF drivers should be derived from class *MultiVIFDriver*. And all -should implement the *request_additional_vifs* method which returns a list of -VIF objects. Those VIF objects are created by each of the vif-drivers invoked -by the Multi-VIF driver. Each of the multi-vif driver should support a syntax -of additional interfaces definition in Pod. If the pod object doesn't define -additional interfaces, the Multi-VIF driver can just return. - -Diagram describing VifHandler - Drivers flow is giver below: - -.. image:: ../../images/vif_handler_drivers_design.png - :alt: vif handler drivers design - :align: center - :width: 100% - - -Config Options -~~~~~~~~~~~~~~ - -Add new config option "multi_vif_drivers" (list) to config file that shows -what Multi-VIF drivers should be used in to specify the addition VIF objects. -It is allowed to have one or more multi_vif_drivers enabled, which means that -multi_vif_drivers can either work separately or together. By default, a noop -driver which basically does nothing will be used if this field is not -explicitly specified. - -Option in config file might look like this: - -.. code-block:: ini - - [kubernetes] - multi_vif_drivers = additional_subnets - -Or like this: - -.. code-block:: ini - - [kubernetes] - multi_vif_drivers = npwg_multiple_interfaces - - -Additional Subnets Driver -~~~~~~~~~~~~~~~~~~~~~~~~~ - -Since it is possible to request additional subnets for the pod through the pod -annotations it is necessary to have new driver. According to parsed information -(requested subnets) by Multi-vif driver it has to return dictionary containing -the mapping 'subnet_id' -> 'network' for all requested subnets in unified -format specified in PodSubnetsDriver class. Here's how a Pod Spec with -additional subnets requests might look like: - -.. code-block:: yaml - - spec: - replicas: 1 - template: - metadata: - name: some-name - labels: - app: some-name - annotations: - openstack.org/kuryr-additional-subnets: '[ - "id_of_neutron_subnet_created_previously" - ]' - - -Specific ports support ----------------------- - -Specific ports support is enabled by default and will be a part of the drivers -to implement it. It is possible to have manually precreated specific ports in -neutron and specify them in pod annotations as preferably used. This means that -drivers will use specific ports if it is specified in pod annotations, -otherwise it will create new ports by default. It is important that specific -ports can have vnic_type both direct and normal, so it is necessary to provide -processing support for specific ports in both SRIOV and generic driver. Pod -annotation with requested specific ports might look like this: - -.. code-block:: yaml - - spec: - replicas: 1 - template: - metadata: - name: some-name - labels: - app: some-name - annotations: - spec-ports: '[ - "id_of_direct_precreated_port". - "id_of_normal_precreated_port" - ]' - -Pod spec above should be interpreted the following way: Multi-vif driver parses -pod annotations and gets ids of specific ports. If vnic_type is "normal" and -such ports exist, it calls generic driver to create vif objects for these -ports. Else if vnic_type is "direct" and such ports exist, it calls sriov -driver to create vif objects for these ports. If certain ports are not -requested in annotations then driver doesn't return additional vifs to -Multi-vif driver. diff --git a/doc/source/index.rst b/doc/source/index.rst deleted file mode 100644 index 20e95887b..000000000 --- a/doc/source/index.rst +++ /dev/null @@ -1,48 +0,0 @@ -.. kuryr-kubernetes documentation master file, created by - sphinx-quickstart on Tue Jul 9 22:26:36 2013. - You can adapt this file completely to your liking, but it should at least - contain the root `toctree` directive. - -Welcome to kuryr-kubernetes's documentation! -============================================ - -Contents --------- - -.. toctree:: - :maxdepth: 3 - - readme - nested_vlan_mode - installation/index - usage - contributor/index - - -Developer Docs --------------- - -.. toctree:: - :maxdepth: 3 - - devref/index - - -Design Specs ------------- - -.. toctree:: - :maxdepth: 1 - - specs/pike/contrail_support - specs/pike/fuxi_kubernetes - specs/queens/network_policy - specs/rocky/npwg_spec_support - specs/stein/vhostuser - - -Indices and tables ------------------- - -* :ref:`genindex` -* :ref:`search` diff --git a/doc/source/installation/containerized.rst b/doc/source/installation/containerized.rst deleted file mode 100644 index 5391595a8..000000000 --- a/doc/source/installation/containerized.rst +++ /dev/null @@ -1,187 +0,0 @@ -.. _containerized: - -================================================ -Kuryr installation as a Kubernetes network addon -================================================ - -Building images -~~~~~~~~~~~~~~~ - -First you should build kuryr-controller and kuryr-cni docker images and place -them on cluster-wide accessible registry. - -For creating controller image on local machine: - -.. code-block:: console - - $ docker build -t kuryr/controller -f controller.Dockerfile . - -For creating cni daemonset image on local machine: - -.. code-block:: console - - $ docker build -t kuryr/cni -f cni.Dockerfile . - -Kuryr-kubernetes also includes a tool to automatically build the controller -image and deletes the existing container to apply the newly built -image. The tool is avaliable at: - -.. code-block:: console - - $ contrib/regenerate_controller_pod.sh - -If you want to run kuryr CNI without the daemon, build the image with: - -.. code-block:: console - - $ docker build -t kuryr/cni -f cni.Dockerfile --build-arg CNI_DAEMON=False . - -Alternatively, you can remove ``imagePullPolicy: Never`` from kuryr-controller -Deployment and kuryr-cni DaemonSet definitions to use pre-built `controller`_ -and `cni`_ images from the Docker Hub. Those definitions will be generated in -next step. - -.. _containerized-generate: - -Generating Kuryr resource definitions for Kubernetes -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -kuryr-kubernetes includes a tool that lets you generate resource definitions -that can be used to Deploy Kuryr on Kubernetes. The script is placed in -``tools/generate_k8s_resource_definitions.sh`` and takes up to 3 arguments: - -.. code-block:: console - - $ ./tools/generate_k8s_resource_definitions.sh [] [] [] - -* ``output_dir`` - directory where to put yaml files with definitions. -* ``controller_conf_path`` - path to custom kuryr-controller configuration - file. -* ``cni_conf_path`` - path to custom kuryr-cni configuration file (defaults to - ``controller_conf_path``). -* ``ca_certificate_path`` - path to custom CA certificate for OpenStack API. It - will be added into Kubernetes as a ``Secret`` and mounted into - kuryr-controller container. Defaults to no certificate. - -.. note:: - - Providing no or incorrect ``ca_certificate_path`` will still create the file - with ``Secret`` definition with empty CA certificate file. This file will - still be mounted in kuryr-controller ``Deployment`` definition. - -If no path to config files is provided, script automatically generates minimal -configuration. However some of the options should be filled by the user. You -can do that either by editing the file after the ConfigMap definition is -generated or provide your options as environment variables before running the -script. Below is the list of available variables: - -* ``$KURYR_K8S_API_ROOT`` - ``[kubernetes]api_root`` (default: - https://127.0.0.1:6443) -* ``$KURYR_K8S_AUTH_URL`` - ``[neutron]auth_url`` (default: - http://127.0.0.1/identity) -* ``$KURYR_K8S_USERNAME`` - ``[neutron]username`` (default: admin) -* ``$KURYR_K8S_PASSWORD`` - ``[neutron]password`` (default: password) -* ``$KURYR_K8S_USER_DOMAIN_NAME`` - ``[neutron]user_domain_name`` (default: - Default) -* ``$KURYR_K8S_KURYR_PROJECT_ID`` - ``[neutron]kuryr_project_id`` -* ``$KURYR_K8S_PROJECT_DOMAIN_NAME`` - ``[neutron]project_domain_name`` - (default: Default) -* ``$KURYR_K8S_PROJECT_ID`` - ``[neutron]k8s_project_id`` -* ``$KURYR_K8S_POD_SUBNET_ID`` - ``[neutron_defaults]pod_subnet_id`` -* ``$KURYR_K8S_POD_SG`` - ``[neutron_defaults]pod_sg`` -* ``$KURYR_K8S_SERVICE_SUBNET_ID`` - ``[neutron_defaults]service_subnet_id`` -* ``$KURYR_K8S_WORKER_NODES_SUBNETS`` - ``[pod_vif_nested]worker_nodes_subnets`` -* ``$KURYR_K8S_BINDING_DRIVER`` - ``[binding]driver`` (default: - ``kuryr.lib.binding.drivers.vlan``) -* ``$KURYR_K8S_BINDING_IFACE`` - ``[binding]link_iface`` (default: eth0) - -.. note:: - - kuryr-daemon will be started in the CNI container. It is using ``os-vif`` - and ``oslo.privsep`` to do pod wiring tasks. By default it'll call ``sudo`` - to raise privileges, even though container is priviledged by itself or - ``sudo`` is missing from container OS (e.g. default CentOS 8). To prevent - that make sure to set following options in kuryr.conf used for kuryr-daemon: - - .. code-block:: ini - - [vif_plug_ovs_privileged] - helper_command=privsep-helper - [vif_plug_linux_bridge_privileged] - helper_command=privsep-helper - - Those options will prevent oslo.privsep from doing that. If rely on - aformentioned script to generate config files, those options will be added - automatically. - -In case of using ports pool functionality, we may want to make the -kuryr-controller not ready until the pools are populated with the existing -ports. To achieve this a readiness probe must be added to the kuryr-controller -deployment. To add the readiness probe, in addition to the above environment -variables or the kuryr-controller configuration file, and extra environmental -variable must be set: - -* ``$KURYR_USE_PORTS_POOLS`` - ``True`` (default: False) - -Example run: - -.. code-block:: console - - $ KURYR_K8S_API_ROOT="192.168.0.1:6443" ./tools/generate_k8s_resource_definitions.sh /tmp - -This should generate 6 files in your ````: - -* config_map.yml -* certificates_secret.yml -* controller_service_account.yml -* cni_service_account.yml -* controller_deployment.yml -* cni_ds.yml - -.. note:: - - kuryr-cni daemonset mounts /var/run, due to necessity of accessing to - several sub directories like openvswitch and auxiliary directory for - vhostuser configuration and socket files. Also when - neutron-openvswitch-agent works with datapath_type = netdev configuration - option, kuryr-kubernetes has to move vhostuser socket to auxiliary - directory, that auxiliary directory should be on the same mount point, - otherwise connection of this socket will be refused. In case when Open - vSwitch keeps vhostuser socket files not in /var/run/openvswitch, - openvswitch mount point in cni_ds.yaml and [vhostuser] section in - config_map.yml should be changed properly. - - -Deploying Kuryr resources on Kubernetes -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -To deploy the files on your Kubernetes cluster run: - -.. code-block:: console - - $ kubectl apply -f config_map.yml -n kube-system - $ kubectl apply -f certificates_secret.yml -n kube-system - $ kubectl apply -f controller_service_account.yml -n kube-system - $ kubectl apply -f cni_service_account.yml -n kube-system - $ kubectl apply -f controller_deployment.yml -n kube-system - $ kubectl apply -f cni_ds.yml -n kube-system - -After successful completion: - -* kuryr-controller Deployment object, with single replica count, will get - created in kube-system namespace. -* kuryr-cni gets installed as a daemonset object on all the nodes in - kube-system namespace - -To see kuryr-controller logs: - -.. code-block:: console - - $ kubectl logs - -NOTE: kuryr-cni has no logs and to debug failures you need to check out kubelet -logs. - - -.. _controller: https://hub.docker.com/r/kuryr/controller/ -.. _cni: https://hub.docker.com/r/kuryr/cni/ diff --git a/doc/source/installation/default_configuration.rst b/doc/source/installation/default_configuration.rst deleted file mode 100644 index 8a32c270e..000000000 --- a/doc/source/installation/default_configuration.rst +++ /dev/null @@ -1,91 +0,0 @@ -============================= -Inspect default Configuration -============================= - -By default, DevStack creates networks called ``private`` and ``public``: - -.. code-block:: console - - $ openstack network list --project demo - +--------------------------------------+---------+----------------------------------------------------------------------------+ - | ID | Name | Subnets | - +--------------------------------------+---------+----------------------------------------------------------------------------+ - | 12bc346b-35ed-4cfa-855b-389305c05740 | private | 1ee73076-e01e-4cec-a3a4-cbb275f94d0f, 8376a091-dcea-4ed5-b738-c16446e861da | - +--------------------------------------+---------+----------------------------------------------------------------------------+ - - $ openstack network list --project admin - +--------------------------------------+--------+----------------------------------------------------------------------------+ - | ID | Name | Subnets | - +--------------------------------------+--------+----------------------------------------------------------------------------+ - | 646baf54-6178-4a26-a52b-68ad0ba1e057 | public | 00e0b1e4-4bee-4204-bd02-610291c56334, b1be34f2-7c3d-41ca-b2f5-6dcbd3c1715b | - +--------------------------------------+--------+----------------------------------------------------------------------------+ - -And kuryr-kubernetes creates two extra ones for the kubernetes services and -pods under the project k8s: - -.. code-block:: console - - $ openstack network list --project k8s - +--------------------------------------+-----------------+--------------------------------------+ - | ID | Name | Subnets | - +--------------------------------------+-----------------+--------------------------------------+ - | 1bff74a6-e4e2-42fb-a81b-33c9c144987c | k8s-pod-net | 3c3e18f9-d1d0-4674-b3be-9fc8561980d3 | - | d4be7efc-b84d-480e-a1db-34205877e6c4 | k8s-service-net | 55405e9d-4e25-4a55-bac2-e25ee88584e1 | - +--------------------------------------+-----------------+--------------------------------------+ - -And similarly for the subnets: - -.. code-block:: console - - $ openstack subnet list --project k8s - +--------------------------------------+--------------------+--------------------------------------+---------------+ - | ID | Name | Network | Subnet | - +--------------------------------------+--------------------+--------------------------------------+---------------+ - | 3c3e18f9-d1d0-4674-b3be-9fc8561980d3 | k8s-pod-subnet | 1bff74a6-e4e2-42fb-a81b-33c9c144987c | 10.0.0.64/26 | - | 55405e9d-4e25-4a55-bac2-e25ee88584e1 | k8s-service-subnet | d4be7efc-b84d-480e-a1db-34205877e6c4 | 10.0.0.128/26 | - +--------------------------------------+--------------------+--------------------------------------+---------------+ - -In addition to that, security groups for both pods and services are created too: - -.. code-block:: console - - $ openstack security group list --project k8s - +--------------------------------------+--------------------+------------------------+----------------------------------+ - | ID | Name | Description | Project | - +--------------------------------------+--------------------+------------------------+----------------------------------+ - | 00fd78f9-484d-4ea7-b677-82f73c54064a | service_pod_access | service_pod_access | 49e2683370f245e38ac2d6a8c16697b3 | - | fe7cee41-6021-4d7b-ab03-1ce1e391a1ca | default | Default security group | 49e2683370f245e38ac2d6a8c16697b3 | - +--------------------------------------+--------------------+------------------------+----------------------------------+ - -And finally, the loadbalancer for the kubernetes API service is also created, -with the subsequence listener, pool and added members: - -.. code-block:: console - - $ openstack loadbalancer list - +--------------------------------------+--------------------+----------------------------------+-------------+---------------------+----------+ - | id | name | tenant_id | vip_address | provisioning_status | provider | - +--------------------------------------+--------------------+----------------------------------+-------------+---------------------+----------+ - | 7d0cf5b5-b164-4b32-87d3-ae6c82513927 | default/kubernetes | 47c28e562795468ea52e92226e3bc7b1 | 10.0.0.129 | ACTIVE | haproxy | - +--------------------------------------+--------------------+----------------------------------+-------------+---------------------+----------+ - - $ openstack loadbalancer listener list - +--------------------------------------+--------------------------------------+------------------------+----------------------------------+----------+---------------+----------------+ - | id | default_pool_id | name | tenant_id | protocol | protocol_port | admin_state_up | - +--------------------------------------+--------------------------------------+------------------------+----------------------------------+----------+---------------+----------------+ - | abfbafd8-7609-4b7d-9def-4edddf2b887b | 70bed821-9a9f-4e1d-8c7e-7df89a923982 | default/kubernetes:443 | 47c28e562795468ea52e92226e3bc7b1 | HTTPS | 443 | True | - +--------------------------------------+--------------------------------------+------------------------+----------------------------------+----------+---------------+----------------+ - - $ openstack loadbalancer pool list - +--------------------------------------+------------------------+----------------------------------+--------------+----------+----------------+ - | id | name | tenant_id | lb_algorithm | protocol | admin_state_up | - +--------------------------------------+------------------------+----------------------------------+--------------+----------+----------------+ - | 70bed821-9a9f-4e1d-8c7e-7df89a923982 | default/kubernetes:443 | 47c28e562795468ea52e92226e3bc7b1 | ROUND_ROBIN | HTTPS | True | - +--------------------------------------+------------------------+----------------------------------+--------------+----------+----------------+ - - $ openstack loadbalancer member list default/kubernetes:443 - +--------------------------------------+------+----------------------------------+--------------+---------------+--------+--------------------------------------+----------------+ - | id | name | tenant_id | address | protocol_port | weight | subnet_id | admin_state_up | - +--------------------------------------+------+----------------------------------+--------------+---------------+--------+--------------------------------------+----------------+ - | 5ddceaff-180b-47fa-b787-8921f4591cb0 | | 47c28e562795468ea52e92226e3bc7b1 | 192.168.5.10 | 6443 | 1 | b1be34f2-7c3d-41ca-b2f5-6dcbd3c1715b | True | - +--------------------------------------+------+----------------------------------+--------------+---------------+--------+--------------------------------------+----------------+ diff --git a/doc/source/installation/devstack/basic.rst b/doc/source/installation/devstack/basic.rst deleted file mode 100644 index 0538e13ad..000000000 --- a/doc/source/installation/devstack/basic.rst +++ /dev/null @@ -1,186 +0,0 @@ -=========================== -Basic DevStack installation -=========================== - -Most basic DevStack installation of kuryr-kubernetes is pretty simple. This -document aims to be a tutorial through installation steps. - -Document assumes using Ubuntu LTS 20.04 (using server or cloud installation is -recommended, but desktop will also work), but same steps should apply for other -operating systems. It is also assumed that ``git`` and ``curl`` are already -installed on the system. DevStack will make sure to install and configure -OpenStack, Kubernetes and dependencies of both systems. - -Please note, that DevStack installation should be done inside isolated -environment such as virtual machine, since it will make substantial changes to -the host. - - -Cloning required repositories ------------------------------ - -First of all, you'll need a user account, which can execute passwordless -``sudo`` command. Consult `DevStack Documentation`_ for details, how to create -one, or simply add line: - -.. code-block:: ini - - "USERNAME ALL=(ALL) NOPASSWD:ALL" - -to ``/etc/sudoers`` using ``visudo`` command. Remember to change ``USERNAME`` -to the real name of the user account. - -Clone DevStack: - -.. code-block:: console - - $ git clone https://opendev.org/openstack-dev/devstack - -Copy sample ``local.conf`` (DevStack configuration file) to devstack -directory: - -.. code-block:: console - - $ curl https://opendev.org/openstack/kuryr-kubernetes/raw/branch/master/devstack/local.conf.sample \ - -o devstack/local.conf - -.. note:: - - ``local.conf.sample`` file is configuring Neutron and Kuryr with OVN - ML2 networking. In the ``kuryr-kubernetes/devstack`` directory there are - other sample configuration files that enable Open vSwitch instead OVN. - networking. See other pages in this documentation section to learn more. - -Now edit ``devstack/local.conf`` to set up some initial options: - -* If you have multiple network interfaces, you need to set ``HOST_IP`` variable - to the IP on the interface you want to use as DevStack's primary. DevStack - sometimes complain about lacking of ``HOST_IP`` even if there is single - network interface. -* If you already have Docker installed on the machine, you can comment out line - starting with ``enable_plugin devstack-plugin-container``. -* If you can't pull images from k8s.gcr.io, you can add the variable - ``KURYR_KUBEADMIN_IMAGE_REPOSITORY`` to ``devstack/local.conf`` and set it's - value to the repository that you can access. - -Once ``local.conf`` is configured, you can start the installation: - -.. code-block:: console - - $ devstack/stack.sh - -Installation takes from 20 to 40 minutes. Once that's done you should see -similar output: - -.. code-block:: console - - ========================= - DevStack Component Timing - (times are in seconds) - ========================= - wait_for_service 8 - pip_install 137 - apt-get 295 - run_process 14 - dbsync 22 - git_timed 168 - apt-get-update 4 - test_with_retry 3 - async_wait 71 - osc 200 - ------------------------- - Unaccounted time 505 - ========================= - Total runtime 1427 - - ================= - Async summary - ================= - Time spent in the background minus waits: 140 sec - Elapsed time: 1427 sec - Time if we did everything serially: 1567 sec - Speedup: 1.09811 - - - - This is your host IP address: 10.0.2.15 - This is your host IPv6 address: ::1 - Keystone is serving at http://10.0.2.15/identity/ - The default users are: admin and demo - The password: pass - - Services are running under systemd unit files. - For more information see: - https://docs.openstack.org/devstack/latest/systemd.html - - DevStack Version: xena - Change: - OS Version: Ubuntu 20.04 focal - - -You can test DevStack by sourcing credentials and trying some commands: - -.. code-block:: console - - $ source devstack/openrc admin admin - $ openstack service list - +----------------------------------+------------------+------------------+ - | ID | Name | Type | - +----------------------------------+------------------+------------------+ - | 07e985b425fc4f8a9da20970a26f754a | octavia | load-balancer | - | 1dc08cb4401243848a562c0042d3f40a | neutron | network | - | 35627730938d4a4295f3add6fc826261 | nova | compute | - | 636b43b739e548e0bb369bc41fe1df08 | glance | image | - | 90ef7129985e4e10874d5e4ddb36ea01 | keystone | identity | - | ce177a3f05dc454fb3d43f705ae24dde | kuryr-kubernetes | kuryr-kubernetes | - | d3d6a461a78e4601a14a5e484ec6cdd1 | nova_legacy | compute_legacy | - | d97e5c31b1054a308c5409ee813c0310 | placement | placement | - +----------------------------------+------------------+------------------+ - -To verify if Kubernetes is running properly, list its nodes and check status of -the only node you should have. The correct value is "Ready": - -.. code-block:: console - - $ kubectl get nodes - NAME STATUS AGE VERSION - localhost Ready 2m v1.6.2 - -To test kuryr-kubernetes itself try creating a Kubernetes pod: - -.. code-block:: console - - $ kubectl create deployment --image busybox test -- sleep 3600 - $ kubectl get pods -o wide - NAME READY STATUS RESTARTS AGE IP NODE - test-3202410914-1dp7g 0/1 ContainerCreating 0 7s localhost - -After a moment (even up to few minutes as Docker image needs to be downloaded) -you should see that pod got the IP from OpenStack network: - -.. code-block:: console - - $ kubectl get pods -o wide - NAME READY STATUS RESTARTS AGE IP NODE - test-3202410914-1dp7g 1/1 Running 0 35s 10.0.0.73 localhost - -You can verify that this IP is really assigned to Neutron port: - -.. code-block:: console - - [stack@localhost kuryr-kubernetes]$ openstack port list | grep 10.0.0.73 - | 3ce7fd13-ad0a-4e92-9b6f-0d38d50b1699 | | fa:16:3e:8e:f4:30 | ip_address='10.0.0.73', subnet_id='ddfbc8e9-68da-48f9-8a05-238ea0607e0d' | ACTIVE | - -If those steps were successful, then it looks like your DevStack with -kuryr-kubernetes is working correctly. In case of errors, copy last ~50 lines -of the logs, paste them into `paste.openstack.org`_ and ask other developers -for help on `Kuryr's IRC channel`_. More info on how to use DevStack can be -found in `DevStack Documentation`_, especially in section `Using Systemd in -DevStack`_, which explains how to use ``systemctl`` to control services and -``journalctl`` to read its logs. - - -.. _paste.openstack.org: http://paste.openstack.org -.. _Kuryr's IRC channel: ircs://irc.oftc.net:6697/openstack-kuryr -.. _DevStack Documentation: https://docs.openstack.org/devstack/latest/ -.. _Using Systemd in DevStack: https://docs.openstack.org/devstack/latest/systemd.html diff --git a/doc/source/installation/devstack/containerized.rst b/doc/source/installation/devstack/containerized.rst deleted file mode 100644 index a4866d181..000000000 --- a/doc/source/installation/devstack/containerized.rst +++ /dev/null @@ -1,83 +0,0 @@ -========================== -Containerized installation -========================== - -It is possible to configure DevStack to install kuryr-controller and kuryr-cni -on Kubernetes as pods. Details can be found on :doc:`../containerized` page, -this page will explain DevStack aspects of running containerized. - - -Installation ------------- - -To configure DevStack to install Kuryr services as containerized Kubernetes -resources, you need to switch ``KURYR_K8S_CONTAINERIZED_DEPLOYMENT``. Add this -line to your ``local.conf``: - -.. code-block:: ini - - KURYR_K8S_CONTAINERIZED_DEPLOYMENT=True - -This will trigger building the kuryr-controller and kuryr-cni containers during -installation, as well as will deploy those on Kubernetes cluster it installed. - - -Rebuilding container images ---------------------------- - -Instructions on how to manually rebuild both kuryr-controller and kuryr-cni -container images are presented on :doc:`../containerized` page. In case you -want to test any code changes, you need to rebuild the images first. - - -Changing configuration ----------------------- - -To change kuryr.conf files that are put into containers you need to edit the -associated ConfigMap. On DevStack deployment this can be done using: - -.. code-block:: console - - $ kubectl -n kube-system edit cm kuryr-config - -Then the editor will appear that will let you edit the ConfigMap. Make sure to -keep correct indentation when doing changes. - - -Restarting services -------------------- - -Once any changes are made to docker images or the configuration, it is crucial -to restart pod you've modified. - - -kuryr-controller -~~~~~~~~~~~~~~~~ - -To restart kuryr-controller and let it load new image and configuration, simply -kill existing pod: - -.. code-block:: console - - $ kubectl -n kube-system get pods - - $ kubectl -n kube-system delete pod - -Deployment controller will make sure to restart the pod with new configuration. - - -kuryr-cni -~~~~~~~~~ - -It's important to understand that kuryr-cni is only a storage pod i.e. it is -actually idling with ``sleep infinity`` once all the files are copied into -correct locations on Kubernetes host. - -You can force it to redeploy new files by killing it. DaemonSet controller -should make sure to restart it with new image and configuration files. - -.. code-block:: console - - $ kubectl -n kube-system get pods - - $ kubectl -n kube-system delete pod <...> diff --git a/doc/source/installation/devstack/index.rst b/doc/source/installation/devstack/index.rst deleted file mode 100644 index 1db445ff9..000000000 --- a/doc/source/installation/devstack/index.rst +++ /dev/null @@ -1,42 +0,0 @@ -.. - 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. - - Convention for heading levels in Neutron devref: - ======= Heading 0 (reserved for the title in a document) - ------- Heading 1 - ~~~~~~~ Heading 2 - +++++++ Heading 3 - ''''''' Heading 4 - (Avoid deeper levels because they do not render well.) - - -=========================== -DevStack based Installation -=========================== - -This section describes how you can install and configure kuryr-kubernetes with -DevStack for testing different functionality, such as nested or different -ML2 drivers. - - -.. toctree:: - :maxdepth: 1 - - basic - nested-vlan - nested-macvlan - nested-dpdk - ovn_support - ovn-octavia - containerized - ports-pool diff --git a/doc/source/installation/devstack/nested-dpdk.rst b/doc/source/installation/devstack/nested-dpdk.rst deleted file mode 100644 index 53ae2bf34..000000000 --- a/doc/source/installation/devstack/nested-dpdk.rst +++ /dev/null @@ -1,222 +0,0 @@ -========================================= -How to try out nested-pods locally (DPDK) -========================================= - -Following are the instructions for an all-in-one setup, using the nested DPDK -driver. We assume that we already have the 'undercloud' configured with at -least one VM as nova instance which is also a kubernetes minion. We assume -that VM has an access to the Internet to install necessary packages. - -Configure the VM: - -#. Install kernel version supporting uio_pci_generic module: - - .. code-block:: bash - - sudo apt install linux-image-`uname -r` linux-headers-`uname -r` - sudo update-grub - sudo reboot - -#. Install DPDK. On Ubuntu: - - .. code-block:: bash - - sudo apt update - sudo apt install dpdk - -#. Enable hugepages: - - .. code-block:: bash - - sudo sysctl -w vm.nr_hugepages=768 - -#. Load DPDK userspace driver: - - .. code-block:: bash - - sudo modprobe uio_pci_generic - -#. Clone devstack repository: - - .. code-block:: bash - - cd ~ - git clone https://git.openstack.org/openstack-dev/devstack - -#. Edit local.conf: - - .. code-block:: ini - - [[local|localrc]] - - RECLONE="no" - - enable_plugin kuryr-kubernetes \ - https://git.openstack.org/openstack/kuryr-kubernetes - - OFFLINE="no" - LOGFILE=devstack.log - LOG_COLOR=False - ADMIN_PASSWORD= - DATABASE_PASSWORD= - RABBIT_PASSWORD= - SERVICE_PASSWORD= - SERVICE_TOKEN= - IDENTITY_API_VERSION=3 - ENABLED_SERVICES="" - - HOST_IP= - - SERVICE_HOST= - MULTI_HOST=1 - KEYSTONE_SERVICE_HOST=$SERVICE_HOST - MYSQL_HOST=$SERVICE_HOST - RABBIT_HOST=$SERVICE_HOST - - KURYR_CONFIGURE_NEUTRON_DEFAULTS=False - KURYR_CONFIGURE_BAREMETAL_KUBELET_IFACE=False - - enable_service docker - enable_service etcd3 - enable_service kubernetes-api - enable_service kubernetes-controller-manager - enable_service kubernetes-scheduler - enable_service kubelet - enable_service kuryr-kubernetes - enable_service kuryr-daemon - - [[post-config|$KURYR_CONF]] - [nested_dpdk] - dpdk_driver = uio_pci_generic - -#. Stack: - - .. code-block:: bash - - cd ~/devstack - ./stack.sh - -#. Install CNI plugins: - - .. code-block:: bash - - wget https://github.com/containernetworking/plugins/releases/download/v0.6.0/cni-plugins-amd64-v0.6.0.tgz - tar xf cni-plugins-amd64-v0.6.0.tgz -C ~/cni/bin/ - -#. Install Multus CNI using this guide: https://github.com/intel/multus-cni#build - - - *Note: Kuryr natively supports multiple VIFs now. In step 13 solution* - *without Multus is described* - -#. Create Multus CNI configuration file ~/cni/conf/multus-cni.conf: - - .. code-block:: json - - { - "name":"multus-demo-network", - "type":"multus", - "delegates":[ - { - "type":"kuryr-cni", - "kuryr_conf":"/etc/kuryr/kuryr.conf", - "debug":true - }, - { - "type":"macvlan", - "master":"ens3", - "masterplugin":true, - "ipam":{ - "type":"host-local", - "subnet":"10.0.0.0/24" - } - } - ] - } - -#. Create a directory to store pci devices used by container: - - .. code-block:: bash - - mkdir /var/pci_address - -#. If you do not use Multus CNI as a tool to have multiple interfaces in - container but use some multi vif driver, then change Kuryr configuration file - /etc/kuryr/kuryr.conf: - - .. code-block:: ini - - [kubernetes] - pod_vif_driver = nested-vlan - multi_vif_drivers = npwg_multiple_interfaces - [vif_pool] - vif_pool_mapping = nested-vlan:nested,nested-dpdk:noop - -#. Also prepare and apply network attachment definition, for example: - - .. code-block:: yaml - - apiVersion: "k8s.cni.cncf.io/v1" - kind: NetworkAttachmentDefinition - metadata: - name: "net-nested-dpdk" - annotations: - openstack.org/kuryr-config: '{ - "subnetId": "", - "driverType": "nested-dpdk" - }' - -#. Reload systemd services: - - .. code-block:: bash - - sudo systemctl daemon-reload - -#. Restart systemd services: - - .. code-block:: bash - - sudo systemctl restart devstack@kubelet.service devstack@kuryr-kubernetes.service devstack@kuryr-daemon.service - -#. Create pod specifying additional interface in annotations: - - .. code-block:: yaml - - apiVersion: extensions/v1beta1 - kind: Deployment - metadata: - name: nginx-nested-dpdk - spec: - replicas: 1 - template: - metadata: - name: nginx-nested-dpdk - labels: - app: nginx-nested-dpdk - annotations: - k8s.v1.cni.cncf.io/networks: net-nested-dpdk - spec: - containers: - - name: nginx-nested-dpdk - image: nginx - resources: - requests: - cpu: "1" - memory: "512Mi" - limits: - cpu: "1" - memory: "512Mi" - volumeMounts: - - name: dev - mountPath: /dev - - name: pci_address - mountPath: /var/pci_address - volumes: - - name: dev - hostPath: - path: /dev - type: Directory - - name: pci_address - hostPath: - path: /var/pci_address - type: Directory - diff --git a/doc/source/installation/devstack/nested-macvlan.rst b/doc/source/installation/devstack/nested-macvlan.rst deleted file mode 100644 index dcbe043ae..000000000 --- a/doc/source/installation/devstack/nested-macvlan.rst +++ /dev/null @@ -1,53 +0,0 @@ -============================================ -How to try out nested-pods locally (MACVLAN) -============================================ - -Following are the instructions for an all-in-one setup, using the -nested MACVLAN driver rather than VLAN and trunk ports. - -#. To install OpenStack services run devstack with - ``devstack/local.conf.pod-in-vm.undercloud.sample``. -#. Launch a Nova VM with MACVLAN support - - .. todo:: - - Add a list of neutron commands, required to launch a such a VM - -#. Log into the VM and set up Kubernetes along with Kuryr using devstack: - - Since undercloud Neutron will be used by pods, Neutron services should be - disabled in localrc. - - Run devstack with ``devstack/local.conf.pod-in-vm.overcloud.sample``. - Fill in the needed information, such as the subnet pool id to use or the - router. - -#. Once devstack is done and all services are up inside VM. Next steps are to - configure the missing information at ``/etc/kuryr/kuryr.conf``: - - - Configure worker VMs subnet: - - .. code-block:: ini - - [pod_vif_nested] - worker_nodes_subnets = - - - Configure "pod_vif_driver" as "nested-macvlan": - - .. code-block:: ini - - [kubernetes] - pod_vif_driver = nested-macvlan - - - Configure binding section: - - .. code-block:: ini - - [binding] - link_iface = - - - Restart kuryr-k8s-controller: - - .. code-block:: console - - $ sudo systemctl restart devstack@kuryr-kubernetes.service - -Now launch pods using kubectl, Undercloud Neutron will serve the networking. diff --git a/doc/source/installation/devstack/nested-vlan.rst b/doc/source/installation/devstack/nested-vlan.rst deleted file mode 100644 index 7c749e5b5..000000000 --- a/doc/source/installation/devstack/nested-vlan.rst +++ /dev/null @@ -1,94 +0,0 @@ -================================================= -How to try out nested-pods locally (VLAN + trunk) -================================================= - -Following are the instructions for an all-in-one setup where Kubernetes will -also be running inside the same Nova VM in which Kuryr-controller and Kuryr-cni -will be running. 4GB memory and 2 vCPUs, is the minimum resource requirement -for the VM: - -#. To install OpenStack services run devstack with - ``devstack/local.conf.pod-in-vm.undercloud.sample``. Ensure that "trunk" - service plugin is enabled in ``/etc/neutron/neutron.conf``: - - .. code-block:: ini - - [DEFAULT] - service_plugins = neutron.services.l3_router.l3_router_plugin.L3RouterPlugin,neutron.services.trunk.plugin.TrunkPlugin - -#. Launch a VM with `Neutron trunk port`_. The next steps can be followed: - `Boot VM with a Trunk Port`_. - -#. Inside VM, install and setup Kubernetes along with Kuryr using devstack: - - - Since undercloud Neutron will be used by pods, Neutron services should be - disabled in localrc. - - Run devstack with ``devstack/local.conf.pod-in-vm.overcloud.sample``. - But first fill in the needed information: - - - Point to the undercloud deployment by setting: - - .. code-block:: bash - - SERVICE_HOST=UNDERCLOUD_CONTROLLER_IP - - - Fill in the subnetpool id of the undercloud deployment, as well as the - router where the new pod and service networks need to be connected: - - .. code-block:: bash - - KURYR_NEUTRON_DEFAULT_SUBNETPOOL_ID=UNDERCLOUD_SUBNETPOOL_V4_ID - KURYR_NEUTRON_DEFAULT_ROUTER=router1 - - - Ensure the nested-vlan driver is going to be set by setting: - - .. code-block:: bash - - KURYR_POD_VIF_DRIVER=nested-vlan - - - Optionally, the ports pool funcionality can be enabled by following: - `How to enable ports pool with devstack`_. - - - [OPTIONAL] If you want to enable the subport pools driver and the VIF - Pool Manager you need to include: - - .. code-block:: bash - - KURYR_VIF_POOL_MANAGER=True - -#. Once devstack is done and all services are up inside VM. Next steps are to - configure the missing information at ``/etc/kuryr/kuryr.conf``: - - - Configure worker VMs subnet: - - .. code-block:: ini - - [pod_vif_nested] - worker_nodes_subnets = - - - Configure binding section: - - .. code-block:: ini - - [binding] - driver = kuryr.lib.binding.drivers.vlan - link_iface = - - - Restart kuryr-k8s-controller: - - .. code-block:: console - - $ sudo systemctl restart devstack@kuryr-kubernetes.service - - - Restart kuryr-daemon: - - .. code-block:: console - - $ sudo systemctl restart devstack@kuryr-daemon.service - -Now launch pods using kubectl, Undercloud Neutron will serve the networking. - - -.. _Neutron trunk port: https://wiki.openstack.org/wiki/Neutron/TrunkPort -.. _Boot VM with a Trunk Port: https://docs.openstack.org/kuryr-kubernetes/latest/installation/trunk_ports.html -.. _How to enable ports pool with devstack: https://docs.openstack.org/kuryr-kubernetes/latest/installation/devstack/ports-pool.html diff --git a/doc/source/installation/devstack/ovn-octavia.rst b/doc/source/installation/devstack/ovn-octavia.rst deleted file mode 100644 index 6982e5d03..000000000 --- a/doc/source/installation/devstack/ovn-octavia.rst +++ /dev/null @@ -1,105 +0,0 @@ -======================================================= -How to enable OVN Octavia provider driver with devstack -======================================================= - -To enable the utilization of OVN as the provider driver for Octavia through -devstack: - -#. You can start with the sample DevStack configuration file for OVN - that kuryr-kubernetes comes with. - - .. code-block:: console - - $ curl https://opendev.org/openstack/kuryr-kubernetes/raw/branch/master/devstack/local.conf.sample \ - -o devstack/local.conf - -#. In case you want more Kuryr specific features than provided by the default - handlers and more handlers are enabled, for example, the following enables - NetworkPolicies in addition to the default features: - - .. code-block:: bash - - KURYR_ENABLED_HANDLERS=vif,kuryrport,service,endpoints,kuryrloadbalancer, - namespace,pod_label,policy,kuryrnetworkpolicy,kuryrnetwork - - Then, the proper subnet drivers need to be set: - - .. code-block:: bash - - KURYR_SG_DRIVER=policy - KURYR_SUBNET_DRIVER=namespace - -#. Run DevStack. - - .. code-block:: console - - $ ./stack.sh - - -Enabling Kuryr support for OVN Octavia driver via ConfigMap ------------------------------------------------------------ - -Alternatively, you can enable Kuryr support for the OVN Octavia driver on the -Kuryr ConfigMap, in case the options are not set at the local.conf file. On -DevStack deployment, the Kuryr ConfigMap can be edited using: - -.. code-block:: console - - $ kubectl -n kube-system edit cm kuryr-config - -The following options need to be set at the ConfigMap: - -.. code-block:: bash - - [kubernetes] - endpoints_driver_octavia_provider = ovn - - [octavia_defaults] - lb_algorithm = SOURCE_IP_PORT - enforce_sg_rules = False - member_mode = L2 - -Make sure to keep correct indentation when doing changes. To enforce the new -settings, you need to restart kuryr-controller by simply killing existing pod. -Deployment controller will make sure to restart the pod with new configuration. - -Kuryr automatically handles the recreation of already created services/load -balancers, so that all of them have the same Octavia provider. - - -Testing ovn-octavia driver support ----------------------------------- - -Once the environment is ready, you can test that network connectivity works -and verify that Kuryr creates the load balancer for the service with the OVN -provider specified in the ConfigMap. -To do that check out :doc:`../testing_connectivity`. - -You can also manually create a load balancer in Openstack: - -.. code-block:: console - - $ openstack loadbalancer create --vip-network-id public --provider ovn - +---------------------+--------------------------------------+ - | Field | Value | - +---------------------+--------------------------------------+ - | admin_state_up | True | - | availability_zone | None | - | created_at | 2020-12-09T14:45:08 | - | description | | - | flavor_id | None | - | id | 94e7c431-912b-496c-a247-d52875d44ac7 | - | listeners | | - | name | | - | operating_status | OFFLINE | - | pools | | - | project_id | af820b57868c4864957d523fb32ccfba | - | provider | ovn | - | provisioning_status | PENDING_CREATE | - | updated_at | None | - | vip_address | 172.24.4.9 | - | vip_network_id | ee97665d-69d0-4995-a275-27855359956a | - | vip_port_id | c98e52d0-5965-4b22-8a17-a374f4399193 | - | vip_qos_policy_id | None | - | vip_subnet_id | 3eed0c05-6527-400e-bb80-df6e59d248f1 | - +---------------------+--------------------------------------+ diff --git a/doc/source/installation/devstack/ovn_support.rst b/doc/source/installation/devstack/ovn_support.rst deleted file mode 100644 index c66e5dd9b..000000000 --- a/doc/source/installation/devstack/ovn_support.rst +++ /dev/null @@ -1,190 +0,0 @@ -================================ -Kuryr Kubernetes OVN Integration -================================ - -OVN provides virtual networking for Open vSwitch and is a component of the Open -vSwitch project. - -OpenStack can use OVN as its network management provider through the Modular -Layer 2 (ML2) north-bound plug-in. - -Integrating of OVN allows Kuryr to be used to bridge (both baremetal and -nested) containers and VM networking in a OVN-based OpenStack deployment. - - -Testing with DevStack ---------------------- - -The next points describe how to test OpenStack with OVN using DevStack. -We will start by describing how to test the baremetal case on a single host, -and then cover a nested environment where containers are created inside VMs. - - -Single Node Test Environment -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -#. Create a test system. - - It's best to use a throwaway dev system for running DevStack. Your best bet - is to use latest Ubuntu LTS (20.04, Focal). - -#. Optionally create the ``stack`` user. You'll need user account with - passwordless ``sudo`` command. - - .. code-block:: console - - $ git clone https://opendev.org/openstack-dev/devstack.git - $ sudo ./devstack/tools/create-stack-user.sh - $ sudo su - stack - -#. Clone DevStack. - - .. code-block:: console - - $ sudo su - stack - $ git clone https://opendev.org/openstack-dev/devstack.git - $ git clone https://opendev.org/openstack/kuryr-kubernetes.git - -#. Configure DevStack to use OVN. - - kuryr-kubernetes comes with a sample DevStack configuration file for OVN you - can start with. For example, you may want to set some values for the various - PASSWORD variables in that file, or change the LBaaS service provider to - use. Feel free to edit it if you'd like, but it should work as-is. - - .. code-block:: console - - $ curl https://opendev.org/openstack/kuryr-kubernetes/raw/branch/master/devstack/local.conf.sample \ - -o devstack/local.conf - - Note that due to OVN compiling OVS from source at - /usr/local/var/run/openvswitch we need to state at the local.conf that the - path is different from the default one (i.e., /var/run/openvswitch). - - Optionally, the ports pool functionality can be enabled by following: - :doc:`./ports-pool` - - .. note:: - - Kuryr-kubernetes is using OVN by default - -#. Run DevStack. - - This is going to take a while. It installs a bunch of packages, clones a - bunch of git repos, and installs everything from these git repos. - - .. code-block:: console - - $ devstack/stack.sh - - Once DevStack completes successfully, you should see output that looks - something like this: - - .. code-block:: console - - This is your host IP address: 192.168.5.10 - This is your host IPv6 address: ::1 - Keystone is serving at http://192.168.5.10/identity/ - The default users are: admin and demo - The password: pass - -#. Extra configurations. - - DevStack does not wire up the public network by default so we must do some - extra steps for floating IP usage as well as external connectivity: - - .. code-block:: console - - $ sudo ip link set br-ex up - $ sudo ip route add 172.24.4.0/24 dev br-ex - $ sudo ip addr add 172.24.4.1/24 dev br-ex - - Then you can create forwarding and NAT rules that will cause "external" - traffic from your instances to get rewritten to your network controller's ip - address and sent out on the network: - - .. code-block:: console - - $ sudo iptables -A FORWARD -d 172.24.4.0/24 -j ACCEPT - $ sudo iptables -A FORWARD -s 172.24.4.0/24 -j ACCEPT - $ sudo iptables -t nat -I POSTROUTING 1 -s 172.24.4.1/24 -j MASQUERADE - - -Inspect default Configuration -+++++++++++++++++++++++++++++ - -In order to check the default configuration, in term of networks, subnets, -security groups and loadbalancers created upon a successful DevStack stacking, -you can check the :doc:`../default_configuration` - -Testing Network Connectivity -++++++++++++++++++++++++++++ - -Once the environment is ready, we can test that network connectivity works -among pods. To do that check out :doc:`../testing_connectivity` - - -Nested Containers Test Environment (VLAN) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Another deployment option is the nested-vlan where containers are created -inside OpenStack VMs by using the Trunk ports support. Thus, first we need to -deploy an undercloud DevStack environment with the needed components to -create VMs (e.g., Glance, Nova, Neutron, Keystone, ...), as well as the needed -OVN configurations such as enabling the trunk support that will be needed for -the VM. And then install the overcloud deployment inside the VM with the kuryr -components. - - -Undercloud deployment -+++++++++++++++++++++ - -The steps to deploy the undercloud environment are the same described above -for the `Single Node Test Environment` with the different of the sample -local.conf to use (step 4), in this case: - -.. code-block:: console - - $ curl https://opendev.org/openstack/kuryr-kubernetes/raw/branch/master/devstack/local.conf.pod-in-vm.undercloud.ovn.sample \ - -o devstack/local.conf - -The main differences with the default ovn local.conf sample are that: - -- There is no need to enable the kuryr-kubernetes plugin as this will be - installed inside the VM (overcloud). -- There is no need to enable the kuryr related services as they will also be - installed inside the VM: kuryr-kubernetes, kubelet, kubernetes-api, - kubernetes-controller-manager, kubernetes-scheduler and kubelet. -- Nova and Glance components need to be enabled to be able to create the VM - where we will install the overcloud. -- OVN Trunk service plugin need to be enable to ensure Trunk ports support. - -Once the undercloud deployment has finished, the next steps are related to -create the overcloud VM by using a parent port of a Trunk so that containers -can be created inside with their own networks. To do that we follow the next -steps detailed at :doc:`../trunk_ports` - - -Overcloud deployment -++++++++++++++++++++ - -Once the VM is up and running, we can start with the overcloud configuration. -The steps to perform are the same as without OVN integration, i.e., the -same steps as for ML2/OVS: - -#. Log in into the VM: - - .. code-block:: console - - $ ssh -i id_rsa_demo ubuntu@FLOATING_IP - -#. Deploy devstack following steps 3 and 4 detailed at :doc:`./nested-vlan` - - -Testing Nested Network Connectivity -+++++++++++++++++++++++++++++++++++ - -Similarly to the baremetal testing, we can create a demo deployment at the -overcloud VM, scale it to any number of pods and expose the service to check if -the deployment was successful. To do that check out -:doc:`../testing_nested_connectivity` diff --git a/doc/source/installation/devstack/ports-pool.rst b/doc/source/installation/devstack/ports-pool.rst deleted file mode 100644 index f2cd53a15..000000000 --- a/doc/source/installation/devstack/ports-pool.rst +++ /dev/null @@ -1,40 +0,0 @@ -====================================== -How to enable ports pool with devstack -====================================== - -To enable the utilization of the ports pool feature through devstack, the next -options needs to be set at the local.conf file: - -#. First, you need to enable the pools by setting: - - .. code-block:: bash - - KURYR_USE_PORT_POOLS=True - -#. Then, the proper pool driver needs to be set. This means that for the - baremetal case you need to ensure the pod vif driver and the vif pool driver - are set to the right baremetal drivers, for instance: - - .. code-block:: bash - - KURYR_POD_VIF_DRIVER=neutron-vif - KURYR_VIF_POOL_DRIVER=neutron - - And if the use case is the nested one, then they should be set to: - - .. code-block:: bash - - KURYR_POD_VIF_DRIVER=nested-vlan - KURYR_VIF_POOL_DRIVER=nested - -#. Then, in case you want to set a limit to the maximum number of ports, or - increase/reduce the default one for the minimum number, as well as to modify - the way the pools are repopulated, both in time as well as regarding bulk - operation sizes, the next option can be included and modified accordingly: - - .. code-block:: bash - - KURYR_PORT_POOL_MIN=5 - KURYR_PORT_POOL_MAX=0 - KURYR_PORT_POOL_BATCH=10 - KURYR_PORT_POOL_UPDATE_FREQ=20 diff --git a/doc/source/installation/https_kubernetes.rst b/doc/source/installation/https_kubernetes.rst deleted file mode 100644 index 7c8c84e93..000000000 --- a/doc/source/installation/https_kubernetes.rst +++ /dev/null @@ -1,29 +0,0 @@ -========================================= -Watching Kubernetes api-server over HTTPS -========================================= - -Add absolute path of client side cert file and key file for Kubernetes server -in ``kuryr.conf``: - -.. code-block:: ini - - [kubernetes] - api_root = https://your_server_address:server_ssl_port - ssl_client_crt_file = - ssl_client_key_file = - -If server ssl certification verification is also to be enabled, add absolute -path to the ca cert: - -.. code-block:: ini - - [kubernetes] - ssl_ca_crt_file = - ssl_verify_server_crt = True - -If want to query HTTPS Kubernetes api server with ``--insecure`` mode: - -.. code-block:: ini - - [kubernetes] - ssl_verify_server_crt = False diff --git a/doc/source/installation/index.rst b/doc/source/installation/index.rst deleted file mode 100644 index 6cb0cd6ef..000000000 --- a/doc/source/installation/index.rst +++ /dev/null @@ -1,49 +0,0 @@ -.. - 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. - - Convention for heading levels in Neutron devref: - ======= Heading 0 (reserved for the title in a document) - ------- Heading 1 - ~~~~~~~ Heading 2 - +++++++ Heading 3 - ''''''' Heading 4 - (Avoid deeper levels because they do not render well.) - - -Installation -============ - -This section describes how you can install and configure kuryr-kubernetes - -.. toctree:: - :maxdepth: 2 - - manual - https_kubernetes - ports-pool - services - ipv6 - upgrades - devstack/index - default_configuration - trunk_ports - network_namespace - network_policy - testing_connectivity - testing_nested_connectivity - containerized - multi_vif_with_npwg_spec - testing_udp_services - testing_sctp_services - listener_timeouts - multiple_tenants diff --git a/doc/source/installation/ipv6.rst b/doc/source/installation/ipv6.rst deleted file mode 100644 index aea52f6ba..000000000 --- a/doc/source/installation/ipv6.rst +++ /dev/null @@ -1,265 +0,0 @@ -=============== -IPv6 networking -=============== - -Kuryr Kubernetes can be used with IPv6 networking. In this guide we'll show how -you can create the Neutron resources and configure Kubernetes and -Kuryr-Kubernetes to achieve an IPv6 only Kubernetes cluster. - - -Setting it up -------------- - -#. Create pods network: - - .. code-block:: console - - $ openstack network create pods - +---------------------------+--------------------------------------+ - | Field | Value | - +---------------------------+--------------------------------------+ - | admin_state_up | UP | - | availability_zone_hints | | - | availability_zones | | - | created_at | 2017-08-11T10:51:25Z | - | description | | - | dns_domain | None | - | id | 4593045c-4233-4b4c-8527-35608ab0eaae | - | ipv4_address_scope | None | - | ipv6_address_scope | None | - | is_default | False | - | is_vlan_transparent | None | - | mtu | 1450 | - | name | pods | - | port_security_enabled | True | - | project_id | 90baf12877ba49a786419b2cacc2c954 | - | provider:network_type | vxlan | - | provider:physical_network | None | - | provider:segmentation_id | 21 | - | qos_policy_id | None | - | revision_number | 2 | - | router:external | Internal | - | segments | None | - | shared | False | - | status | ACTIVE | - | subnets | | - | tags | [] | - | updated_at | 2017-08-11T10:51:25Z | - +---------------------------+--------------------------------------+ - -#. Create the pod subnet: - - .. code-block:: console - - $ openstack subnet create --network pods --no-dhcp \ - --subnet-range fd10:0:0:1::/64 \ - --ip-version 6 \ - pod_subnet - +-------------------------+-------------------------------------------+ - | Field | Value | - +-------------------------+-------------------------------------------+ - | allocation_pools | fd10:0:0:1::2-fd10::1:ffff:ffff:ffff:ffff | - | cidr | fd10:0:0:1::/64 | - | created_at | 2017-08-11T17:02:20Z | - | description | | - | dns_nameservers | | - | enable_dhcp | False | - | gateway_ip | fd10:0:0:1::1 | - | host_routes | | - | id | eef12d65-4d02-4344-b255-295f9adfd4e9 | - | ip_version | 6 | - | ipv6_address_mode | None | - | ipv6_ra_mode | None | - | name | pod_subnet | - | network_id | 4593045c-4233-4b4c-8527-35608ab0eaae | - | project_id | 90baf12877ba49a786419b2cacc2c954 | - | revision_number | 0 | - | segment_id | None | - | service_types | | - | subnetpool_id | None | - | tags | [] | - | updated_at | 2017-08-11T17:02:20Z | - | use_default_subnet_pool | None | - +-------------------------+-------------------------------------------+ - - -#. Create services network: - - .. code-block:: console - - $ openstack network create services - +---------------------------+--------------------------------------+ - | Field | Value | - +---------------------------+--------------------------------------+ - | admin_state_up | UP | - | availability_zone_hints | | - | availability_zones | | - | created_at | 2017-08-11T10:53:36Z | - | description | | - | dns_domain | None | - | id | 560df0c2-537c-41c0-b22c-40ef3d752574 | - | ipv4_address_scope | None | - | ipv6_address_scope | None | - | is_default | False | - | is_vlan_transparent | None | - | mtu | 1450 | - | name | services | - | port_security_enabled | True | - | project_id | 90baf12877ba49a786419b2cacc2c954 | - | provider:network_type | vxlan | - | provider:physical_network | None | - | provider:segmentation_id | 94 | - | qos_policy_id | None | - | revision_number | 2 | - | router:external | Internal | - | segments | None | - | shared | False | - | status | ACTIVE | - | subnets | | - | tags | [] | - | updated_at | 2017-08-11T10:53:37Z | - +---------------------------+--------------------------------------+ - -#. Create services subnet. We reserve the first half of the subnet range for the - VIPs and the second half for the loadbalancer vrrp ports. - - .. code-block:: console - - $ openstack subnet create --network services --no-dhcp \ - --gateway fd10:0:0:2:0:0:0:fffe \ - --ip-version 6 \ - --allocation-pool start=fd10:0:0:2:0:0:0:8000,end=fd10:0:0:2:0:0:0:fffd \ - --subnet-range fd10:0:0:2::/112 \ - service_subnet - +-------------------------+--------------------------------------+ - | Field | Value | - +-------------------------+--------------------------------------+ - | allocation_pools | fd10:0:0:2::8000-fd10:0:0:2::fffd | - | cidr | fd10:0:0:2::/112 | - | created_at | 2017-08-14T19:08:34Z | - | description | | - | dns_nameservers | | - | enable_dhcp | False | - | gateway_ip | fd10:0:0:2::fffe | - | host_routes | | - | id | 3c53ff94-40e2-4399-bc45-6e210f1e8064 | - | ip_version | 6 | - | ipv6_address_mode | None | - | ipv6_ra_mode | None | - | name | service_subnet | - | network_id | 560df0c2-537c-41c0-b22c-40ef3d752574 | - | project_id | 90baf12877ba49a786419b2cacc2c954 | - | revision_number | 0 | - | segment_id | None | - | service_types | | - | subnetpool_id | None | - | tags | [] | - | updated_at | 2017-08-14T19:08:34Z | - | use_default_subnet_pool | None | - +-------------------------+--------------------------------------+ - -#. Create a router: - - .. code-block:: console - - $ openstack router create k8s-ipv6 - +-------------------------+--------------------------------------+ - | Field | Value | - +-------------------------+--------------------------------------+ - | admin_state_up | UP | - | availability_zone_hints | | - | availability_zones | | - | created_at | 2017-08-11T13:17:10Z | - | description | | - | distributed | False | - | external_gateway_info | None | - | flavor_id | None | - | ha | False | - | id | f802a968-2f83-4006-80cb-5070415f69bf | - | name | k8s-ipv6 | - | project_id | 90baf12877ba49a786419b2cacc2c954 | - | revision_number | None | - | routes | | - | status | ACTIVE | - | tags | [] | - | updated_at | 2017-08-11T13:17:10Z | - +-------------------------+--------------------------------------+ - -#. Add the router to the pod subnet: - - .. code-block:: console - - $ openstack router add subnet k8s-ipv6 pod_subnet - -#. Add the router to the service subnet: - - .. code-block:: console - - $ openstack router add subnet k8s-ipv6 service_subnet - -#. Modify Kubernetes API server command line so that it points to the right - CIDR: - - .. code-block:: console - - --service-cluster-ip-range=fd10:0:0:2::/113 - - Note that it is /113 because the other half of the /112 will be used by the - Octavia LB vrrp ports. - -#. Follow the :ref:`k8s_lb_reachable` guide but using IPv6 addresses instead - for the host Kubernetes API. You should also make sure that the Kubernetes - API server binds on the IPv6 address of the host. - - -Troubleshooting ---------------- - -* **Pods can talk to each other with IPv6 but they can't talk to services.** - - This means that most likely you forgot to create a security group or rule - for the pods to be accessible by the service CIDR. You can find an example - here: - - .. code-block:: console - - $ openstack security group create service_pod_access_v6 - +-----------------+-------------------------------------------------------------------------------------------------------------------------------------------------------+ - | Field | Value | - +-----------------+-------------------------------------------------------------------------------------------------------------------------------------------------------+ - | created_at | 2017-08-16T10:01:45Z | - | description | service_pod_access_v6 | - | id | f0b6f0bd-40f7-4ab6-a77b-3cf9f7cc28ac | - | name | service_pod_access_v6 | - | project_id | 90baf12877ba49a786419b2cacc2c954 | - | revision_number | 2 | - | rules | created_at='2017-08-16T10:01:45Z', direction='egress', ethertype='IPv4', id='bd759b4f-c0f5-4cff-a30a-3cd8544d2822', updated_at='2017-08-16T10:01:45Z' | - | | created_at='2017-08-16T10:01:45Z', direction='egress', ethertype='IPv6', id='c89c3f3e-a326-4902-ba26-5315e2d95320', updated_at='2017-08-16T10:01:45Z' | - | updated_at | 2017-08-16T10:01:45Z | - +-----------------+-------------------------------------------------------------------------------------------------------------------------------------------------------+ - - $ openstack security group rule create --remote-ip fd10:0:0:2::/112 \ - --ethertype IPv6 f0b6f0bd-40f7-4ab6-a77b-3cf9f7cc28ac - +-------------------+--------------------------------------+ - | Field | Value | - +-------------------+--------------------------------------+ - | created_at | 2017-08-16T10:04:57Z | - | description | | - | direction | ingress | - | ether_type | IPv6 | - | id | cface77f-666f-4a4c-8a15-a9c6953acf08 | - | name | None | - | port_range_max | None | - | port_range_min | None | - | project_id | 90baf12877ba49a786419b2cacc2c954 | - | protocol | tcp | - | remote_group_id | None | - | remote_ip_prefix | fd10:0:0:2::/112 | - | revision_number | 0 | - | security_group_id | f0b6f0bd-40f7-4ab6-a77b-3cf9f7cc28ac | - | updated_at | 2017-08-16T10:04:57Z | - +-------------------+--------------------------------------+ - - Then remember to add the new security groups to the comma-separated - *pod_security_groups* setting in the section *[neutron_defaults]* of - /etc/kuryr/kuryr.conf diff --git a/doc/source/installation/listener_timeouts.rst b/doc/source/installation/listener_timeouts.rst deleted file mode 100644 index d43bdbb1c..000000000 --- a/doc/source/installation/listener_timeouts.rst +++ /dev/null @@ -1,68 +0,0 @@ -===================================================== -How to configure Listener timeouts for Load Balancers -===================================================== - -By default, Kuryr uses the default Octavia timeout-client-data and -timeout-member-data values when creating or modifying loadbalancers. -To change the timeout values used in creating or modifying a particular -service: - -Set the new timeout values for openstack.org/kuryr-timeout-client-data and -openstack.org/kuryr-timeout-member-data in the service annotation as seen in -the service manifest below. This specification sets the timeout-client-data -and the timeout-member-data to '70000' and '75000' respectively. - -.. code-block:: yaml - - apiVersion: v1 - kind: Service - metadata: - name: kuryr-demo - annotations: - openstack.org/kuryr-timeout-client-data: '70000' - openstack.org/kuryr-timeout-member-data: '75000' - spec: - selector: - app: server - ports: - - protocol: TCP - port: 80 - targetPort: 8080 - -.. note:: - - The listener timeout values can be reset to the defaults by removing the - sevice annotations for the timeout values. - -Setting the timeouts via ConfigMap ----------------------------------- - -Alternatively, you can change the value of the timeout-client-data and/or the -timeout-member-data on the Kuryr ConfigMap. This option is ideal if the new -timeout values will be used for multiple loadbalancers. On DevStack deployment, -the Kuryr ConfigMap can be edited using: - -.. code-block:: console - - $ kubectl -n kube-system edit cm kuryr-config - -The listener timeouts then needs to be changed at the ConfigMap: - -.. code-block:: bash - - [octavia_defaults] - timeout_member_data = 0 - timeout_client_data = 0 - -Another option is to set the listener timeouts at the local.conf file. - -.. code-block:: bash - - #KURYR_TIMEOUT_CLIENT_DATA=0 - #KURYR_TIMEOUT_MEMBER_DATA=0 - -.. note:: - - The listener timeouts values set via the ConfigMap or set at the local.conf - can be overridden by values set in the service annotations for a particular - service. diff --git a/doc/source/installation/manual.rst b/doc/source/installation/manual.rst deleted file mode 100644 index 2fc659324..000000000 --- a/doc/source/installation/manual.rst +++ /dev/null @@ -1,332 +0,0 @@ -==================================== -Installing kuryr-kubernetes manually -==================================== - -Configure kuryr-k8s-controller ------------------------------- - -Install ``kuryr-k8s-controller`` in a virtualenv: - -.. code-block:: console - - $ mkdir kuryr-k8s-controller - $ cd kuryr-k8s-controller - $ python3 -m venv env - $ git clone https://opendev.org/openstack/kuryr-kubernetes - $ . env/bin/activate - $ pip install -e kuryr-kubernetes - -In neutron or in horizon create subnet for pods, subnet for services and a -security-group for pods. You may use existing if you like. In case that you -decide to create new networks and subnets with the cli, you can follow the -services guide, specifically its :ref:`k8s_default_configuration` section. - -Create ``/etc/kuryr/kuryr.conf``: - -.. code-block:: console - - $ cd kuryr-kubernetes - $ ./tools/generate_config_file_samples.sh - $ sudo mkdir /etc/kuryr - $ sudo cp etc/kuryr.conf.sample /etc/kuryr/kuryr.conf - -Edit ``kuryr.conf``: - -.. code-block:: ini - - [DEFAULT] - use_stderr = true - bindir = {path_to_env}/libexec/kuryr - - [kubernetes] - api_root = http://{ip_of_kubernetes_apiserver}:8080 - ssl_client_crt_file = {path-to-kuryr-k8s-user-cert-file} - ssl_client_key_file = {path-to-kuryr-k8s-user-key-file} - ssl_ca_crt_file = {path-to-k8s-api-ca-cert-file} - - [neutron] - auth_url = http://127.0.0.1:35357/v3/ - username = admin - user_domain_name = Default - password = ADMIN_PASSWORD - project_name = service - project_domain_name = Default - auth_type = password - - [neutron_defaults] - ovs_bridge = br-int - pod_security_groups = {id_of_secuirity_group_for_pods} - pod_subnet = {id_of_subnet_for_pods} - project = {id_of_project} - service_subnet = {id_of_subnet_for_k8s_services} - -.. note:: - - If you want Kuryr to connect to Kubernetes through an unauthenticated - endpoint make sure to set ``[kubernetes]ssl_ca_crt_file`` and - ``[kubernetes]token_file`` to ``""`` as they default to the locations where - Kubernetes puts those files for pods. Also don't set - ``[kubernetes]ssl_client_crt_file`` and ``[kubernetes]ssl_client_key_file``. - - If you use tokens to authenticate use ``[kubernetes]token_file`` to specify - a file having it. - -.. note:: - - If your Kubernetes cluster has RBAC enabled, make sure the Kuryr user has - access to required resources: - - .. code-block:: yaml - - rules: - - apiGroups: - - "" - verbs: ["*"] - resources: - - endpoints - - pods - - nodes - - services - - services/status - - namespaces - - apiGroups: - - openstack.org - verbs: ["*"] - resources: - - kuryrnetworks - - kuryrnetworkpolicies - - kuryrloadbalancers - - apiGroups: ["networking.k8s.io"] - resources: - - networkpolicies - verbs: - - get - - list - - watch - - update - - patch - - apiGroups: ["k8s.cni.cncf.io"] - resources: - - network-attachment-definitions - verbs: - - get - - You can generate ``ServiceAccount`` definition with correct ``ClusterRole`` - using instructions on :ref:`containerized-generate` page. - -Note that the service_subnet and the pod_subnet *should be routable* and that -the pods should allow service subnet access. - -Octavia supports two ways of performing the load balancing between the -Kubernetes load balancers and their members: - -* Layer2: Octavia, apart from the VIP port in the services subnet, creates a - Neutron port to the subnet of each of the members. This way the traffic from - the Service Haproxy to the members will not go through the router again, only - will have gone through the router to reach the service. -* Layer3: Octavia only creates the VIP port. The traffic from the service VIP - to the members will go back to the router to reach the pod subnet. It is - important to note that it will have some performance impact depending on the - SDN. - -To support the L3 mode (both for Octavia and for the deprecated -Neutron-LBaaSv2): - -* There should be a router between the two subnets. -* The pod_security_groups setting should include a security group with a rule - granting access to all the CIDR of the service subnet, e.g.: - - .. code-block:: console - - $ openstack security group create --project k8s_cluster_project \ - service_pod_access_sg - $ openstack security group rule create --project k8s_cluster_project \ - --remote-ip cidr_of_service_subnet --ethertype IPv4 --protocol tcp \ - service_pod_access_sg - -* The uuid of this security group id should be added to the comma separated - list of pod security groups. *pod_security_groups* in *[neutron_defaults]*. - -Alternatively, to support Octavia L2 mode: - -* The pod security_groups setting should include a security group with a rule - granting access to all the CIDR of the pod subnet, e.g.: - - .. code-block:: console - - $ openstack security group create --project k8s_cluster_project \ - octavia_pod_access_sg - $ openstack security group rule create --project k8s_cluster_project \ - --remote-ip cidr_of_pod_subnet --ethertype IPv4 --protocol tcp \ - octavia_pod_access_sg - -* The uuid of this security group id should be added to the comma separated - list of pod security groups. *pod_security_groups* in *[neutron_defaults]*. - -Run kuryr-k8s-controller: - -.. code-block:: console - - $ kuryr-k8s-controller --config-file /etc/kuryr/kuryr.conf -d - -Alternatively you may run it in screen: - -.. code-block:: console - - $ screen -dm kuryr-k8s-controller --config-file /etc/kuryr/kuryr.conf -d - - -Configure kuryr-cni -------------------- - -On every kubernetes minion node (and on master if you intend to run containers -there) you need to configure kuryr-cni. - -Install ``kuryr-cni`` in a virtualenv: - -.. code-block:: console - - $ mkdir kuryr-k8s-cni - $ cd kuryr-k8s-cni - $ virtualenv env - $ . env/bin/activate - $ git clone https://opendev.org/openstack/kuryr-kubernetes - $ pip install -e kuryr-kubernetes - -Create ``/etc/kuryr/kuryr.conf``: - -.. code-block:: console - - $ cd kuryr-kubernetes - $ ./tools/generate_config_file_samples.sh - $ cp etc/kuryr.conf.sample /etc/kuryr/kuryr.conf - -Edit ``kuryr.conf``: - -.. code-block:: ini - - [DEFAULT] - use_stderr = true - bindir = {path_to_env}/libexec/kuryr - [kubernetes] - api_root = http://{ip_of_kubernetes_apiserver}:8080 - -Link the CNI binary to CNI directory, where kubelet would find it: - -.. code-block:: console - - $ mkdir -p /opt/cni/bin - $ ln -s $(which kuryr-cni) /opt/cni/bin/ - -Create the CNI config file for kuryr-cni: ``/etc/cni/net.d/10-kuryr.conflist``. -Kubelet would only use the lexicographically first file in that directory, so -make sure that it is kuryr's config file: - -.. code-block:: json - - { - "name": "kuryr", - "cniVersion": "0.3.1", - "plugins": [ - { - "type": "kuryr-cni", - "kuryr_conf": "/etc/kuryr/kuryr.conf", - "debug": true - } - ] - } - -Install ``os-vif`` and ``oslo.privsep`` libraries globally. These modules -are used to plug interfaces and would be run with raised privileges. ``os-vif`` -uses ``sudo`` to raise privileges, and they would need to be installed globally -to work correctly: - -.. code-block:: console - - $ deactivate - $ sudo pip install 'oslo.privsep>=1.20.0' 'os-vif>=1.5.0' - - -Configure Kuryr CNI Daemon --------------------------- - -Kuryr CNI Daemon is a service designed to increased scalability of the Kuryr -operations done on Kubernetes nodes. More information can be found on -:ref:`cni-daemon` page. - -Kuryr CNI Daemon, should be installed on every Kubernetes node, so following -steps need to be repeated. - -.. note:: - - You can tweak configuration of some timeouts to match your environment. It's - crucial for scalability of the whole deployment. In general the timeout to - serve CNI request from kubelet to Kuryr is 180 seconds. After that time - kubelet will retry the request. Additionally there are two configuration - options: - - .. code-block:: ini - - [cni_daemon] - vif_annotation_timeout=60 - pyroute2_timeout=10 - - ``vif_annotation_timeout`` is time the Kuryr CNI Daemon will wait for Kuryr - Controller to create a port in Neutron and add information about it to Pod's - metadata. If either Neutron or Kuryr Controller doesn't keep up with high - number of requests, it's advised to increase this timeout. Please note that - increasing it over 180 seconds will not have any effect as the request will - time out anyway and will be retried (which is safe). - - ``pyroute2_timeout`` is internal timeout of pyroute2 library, that is - responsible for doing modifications to Linux Kernel networking stack (e.g. - moving interfaces to Pod's namespaces, adding routes and ports or assigning - addresses to interfaces). When serving a lot of ADD/DEL CNI requests on a - regular basis it's advised to increase that timeout. Please note that the - value denotes *maximum* time to wait for kernel to complete the operations. - If operation succeeds earlier, request isn't delayed. - -Run kuryr-daemon: - -.. code-block:: console - - $ kuryr-daemon --config-file /etc/kuryr/kuryr.conf -d - -Alternatively you may run it in screen: - -.. code-block:: console - - $ screen -dm kuryr-daemon --config-file /etc/kuryr/kuryr.conf -d - - -Kuryr CNI Daemon health checks -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The CNI daemon health checks allow the deployer or the orchestration layer -(like for example Kubernetes or OpenShift) to probe the CNI daemon for liveness -and readiness. - -If you want to make use of all of its facilities, you should run the -kuryr-daemon in its own cgroup. It will get its own cgroup if you: - -* Run it as a systemd service, -* run it containerized, -* create a memory cgroup for it. - -In order to make the daemon run in its own cgroup, you can do the following: - -.. code-block:: console - - systemd-run --unit=kuryr-daemon --scope --slice=kuryr-cni \ - kuryr-daemon --config-file /etc/kuryr/kuryr.conf -d - -After this, with the CNI daemon running inside its own cgroup, we can enable -the CNI daemon memory health check. This health check allows us to limit the -memory consumption of the CNI Daemon. The health checks will fail if CNI starts -taking more memory that it is set and the orchestration layer should restart. -The setting is: - -.. code-block:: ini - - [cni_health_server] - max_memory_usage = 4096 # Set the memory limit to 4GiB diff --git a/doc/source/installation/multi_vif_with_npwg_spec.rst b/doc/source/installation/multi_vif_with_npwg_spec.rst deleted file mode 100644 index 119df604a..000000000 --- a/doc/source/installation/multi_vif_with_npwg_spec.rst +++ /dev/null @@ -1,95 +0,0 @@ -======================================== -Configure Pod with Additional Interfaces -======================================== - -To create pods with additional Interfaces follow the `Kubernetes Network Custom -Resource Definition De-facto Standard Version 1`_, the next steps can be -followed: - -#. Create Neutron net/subnets which you want the additional interfaces attach - to. - - .. code-block:: console - - $ openstack network create net-a - $ openstack subnet create subnet-a --subnet-range 192.0.2.0/24 --network net-a - -#. Create CRD of 'NetworkAttachmentDefinition' as defined in NPWG spec. - - .. code-block:: console - - $ cat << EOF > nad.yaml - apiVersion: apiextensions.k8s.io/v1beta1 - kind: CustomResourceDefinition - metadata: - name: network-attachment-definitions.k8s.cni.cncf.io - spec: - group: k8s.cni.cncf.io - version: v1 - scope: Namespaced - names: - plural: network-attachment-definitions - singular: network-attachment-definition - kind: NetworkAttachmentDefinition - shortNames: - - net-attach-def - validation: - openAPIV3Schema: - properties: - spec: - properties: - config: - type: string - EOF - $ kubectl apply -f nad.yaml - -#. Create NetworkAttachmentDefinition object with the UUID of Neutron subnet - defined in step 1. - - .. code-block:: console - - $ cat << EOF > net-a.yaml - apiVersion: "k8s.cni.cncf.io/v1" - kind: NetworkAttachmentDefinition - metadata: - name: "net-a" - annotations: - openstack.org/kuryr-config: '{ - "subnetId": "uuid-of-neutron-subnet-a" - }' - EOF - $ kubectl apply -f net-a.yaml - -#. Enable the multi-vif driver by setting 'multi_vif_drivers' in kuryr.conf. - Then restart kuryr-controller. - - .. code-block:: ini - - [kubernetes] - multi_vif_drivers = npwg_multiple_interfaces - -5. Add additional interfaces to pods definition. e.g. - - .. code-block:: console - - $ cat << EOF > pod.yaml - apiVersion: v1 - kind: Pod - metadata: - name: nginx4 - annotations: - k8s.v1.cni.cncf.io/networks: net-a - spec: - containers: - - name: nginx - image: nginx:1.7.9 - ports: - - containerPort: 80 - EOF - $ kubectl apply -f pod.yaml - -You may put a list of network separated with comma to attach Pods to more -networks. - - -.. _Kubernetes Network Custom Resource Definition De-facto Standard Version 1: https://docs.google.com/document/d/1Ny03h6IDVy_e_vmElOqR7UdTPAG_RNydhVE1Kx54kFQ/edit?usp=sharing diff --git a/doc/source/installation/multiple_tenants.rst b/doc/source/installation/multiple_tenants.rst deleted file mode 100644 index 5f796a32a..000000000 --- a/doc/source/installation/multiple_tenants.rst +++ /dev/null @@ -1,98 +0,0 @@ -======================== -Multiple tenants support -======================== - - -Annotation project driver -------------------------- - -We introduced an annotation project driver, by the driver you can specify a -openstack project for a k8s namespace, kuryr will take along the project id -when it creates openstack resources (port, subnet, LB, etc.) for the namespace -and the resources (pod, service, etc.) of the namespace. - -Configure to enable the driver in kuryr.conf: - - .. code-block:: ini - - [kubernetes] - pod_project_driver = annotation - service_project_driver = annotation - namespace_project_driver = annotation - network_policy_project_driver = annotation - - -User workflow -~~~~~~~~~~~~~ - -#. Retrieve your own openstack project's id: - - .. code-block:: console - - $ openstack project show test-user - +-------------+----------------------------------+ - | Field | Value | - +-------------+----------------------------------+ - | description | | - | domain_id | default | - | enabled | True | - | id | b5e0a1ae99a34aa0b6a6dad59c95dea7 | - | is_domain | False | - | name | test-user | - | options | {} | - | parent_id | default | - | tags | [] | - +-------------+----------------------------------+ - -#. Create a k8s namespace with the project id - - The manifest file of the namespace: - - .. code-block:: yaml - - apiVersion: v1 - kind: Namespace - metadata: - name: testns - annotations: - openstack.org/kuryr-project: b5e0a1ae99a34aa0b6a6dad59c95dea7 - - Modify the annotation ``openstack.org/kuryr-project``'s value to your own - project id. - -#. Create a pod in the created namespaces: - - .. code-block:: console - - $ kubectl create deployment -n testns --image quay.io/kuryr/demo demo - deployment.apps/demo created - - $ kubectl -n testns get pod -o wide - NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES - demo-6cb99dfd4d-mkjh2 1/1 Running 0 3m15s 10.0.1.76 yjf-dev-kuryr - -#. Retrieve the related openstack resource: - - .. code-block:: console - - $ openstack network list --project b5e0a1ae99a34aa0b6a6dad59c95dea7 - +--------------------------------------+---------------+--------------------------------------+ - | ID | Name | Subnets | - +--------------------------------------+---------------+--------------------------------------+ - | f7e3f025-6d03-40db-b6a8-6671b0874646 | ns/testns-net | d9995087-1363-4671-86da-51b4d17712d8 | - +--------------------------------------+---------------+--------------------------------------+ - - $ openstack subnet list --project b5e0a1ae99a34aa0b6a6dad59c95dea7 - +--------------------------------------+------------------+--------------------------------------+--------------+ - | ID | Name | Network | Subnet | - +--------------------------------------+------------------+--------------------------------------+--------------+ - | d9995087-1363-4671-86da-51b4d17712d8 | ns/testns-subnet | f7e3f025-6d03-40db-b6a8-6671b0874646 | 10.0.1.64/26 | - +--------------------------------------+------------------+--------------------------------------+--------------+ - - $ openstack port list --project b5e0a1ae99a34aa0b6a6dad59c95dea7 - +--------------------------------------+------------------------------+-------------------+--------------------------------------------------------------------------+--------+ - | ID | Name | MAC Address | Fixed IP Addresses | Status | - +--------------------------------------+------------------------------+-------------------+--------------------------------------------------------------------------+--------+ - | 1ce9d0b7-de47-40bb-9bc3-2a8e179681b2 | | fa:16:3e:90:2a:a7 | | DOWN | - | abddd00b-383b-4bf2-9b72-0734739e733d | testns/demo-6cb99dfd4d-mkjh2 | fa:16:3e:a4:c0:f7 | ip_address='10.0.1.76', subnet_id='d9995087-1363-4671-86da-51b4d17712d8' | ACTIVE | - +--------------------------------------+------------------------------+-------------------+--------------------------------------------------------------------------+--------+ diff --git a/doc/source/installation/network_namespace.rst b/doc/source/installation/network_namespace.rst deleted file mode 100644 index 75e50a92f..000000000 --- a/doc/source/installation/network_namespace.rst +++ /dev/null @@ -1,164 +0,0 @@ -============================================================= -Enable network per namespace functionality (handler + driver) -============================================================= - -To enable the subnet driver that creates a new network for each new namespace -the next steps are needed: - -#. Enable the namespace handler to reach to namespace events, in this case, - creation and deletion. To do that you need to add it to the list of the - enabled handlers at kuryr.conf (details on how to edit this for - containerized deployment can be found at :doc:`./devstack/containerized`): - - .. code-block:: ini - - [kubernetes] - enabled_handlers=vif,endpoints,service,kuryrloadbalancer,namespace, - kuryrnetwork,kuryrport - - Note that if you also want to enable prepopulation of ports pools upon - creation of first pod on pods network in a namespace, you need to also - add the kuryrnetwork_population handler (more details on :doc:`./ports-pool`): - - .. code-block:: ini - - [kubernetes] - enabled_handlers=vif,endpoints,service,kuryrloadbalancer,namespace, - kuryrnetwork,kuryrport,kuryrnetwork_population - -#. Enable the namespace subnet driver by modifying the default - pod_subnet_driver option at kuryr.conf: - - .. code-block:: ini - - [kubernetes] - pod_subnets_driver = namespace - -#. Select (and create if needed) the subnet pool from where the new subnets - will get their CIDR (e.g., the default on devstack deployment is - shared-default-subnetpool-v4): - - .. code-block:: ini - - [namespace_subnet] - pod_subnet_pool = SUBNET_POOL_ID - -#. Select (and create if needed) the router where the new subnet will be - connected (e.g., the default on devstack deployments is router1): - - .. code-block:: ini - - [namespace_subnet] - pod_router = ROUTER_ID - - Note that if a new router is created, it must ensure the connectivity - requirements between pod, service and public subnets, as in the case for - the default subnet driver. - -Note you need to restart the kuryr controller after applying the above -detailed steps. For devstack non-containerized deployments: - -.. code-block:: console - - $ sudo systemctl restart devstack@kuryr-kubernetes.service - -And for containerized deployments: - -.. code-block:: console - - $ kubectl -n kube-system get pod | grep kuryr-controller - $ kubectl -n kube-system delete pod KURYR_CONTROLLER_POD_NAME - -For directly enabling the driver when deploying with devstack, you just need -to add the namespace handler and state the namespace subnet driver with: - -.. code-block:: console - - KURYR_SUBNET_DRIVER=namespace - KURYR_ENABLED_HANDLERS=vif,endpoints,service,kuryrloadbalancer,namespace, - kuryrnetwork,kuryrport - -.. note:: - - If the loadbalancer maintains the source IP (such as ovn-octavia driver), - there is no need to enforce sg rules at the load balancer level. - To disable the enforcement, you need to set the following variable: - KURYR_ENFORCE_SG_RULES=False - - -Testing the network per namespace functionality ------------------------------------------------ - -#. Create two namespaces: - - .. code-block:: console - - $ kubectl create namespace test1 - $ kubectl create namespace test2 - -#. Check resources has been created: - - .. code-block:: console - - $ kubectl get namespaces - NAME STATUS AGE - test1 Active 14s - test2 Active 5s - ... ... ... - - $ kubectl get kuryrnetworks -A - NAME AGE - ns-test1 1m - ns-test2 1m - - $ openstack network list | grep test1 - | 7c7b68c5-d3c4-431c-9f69-fbc777b43ee5 | ns/test1-net | 8640d134-5ea2-437d-9e2a-89236f6c0198 | - - $ openstack subnet list | grep test1 - | 8640d134-5ea2-437d-9e2a-89236f6c0198 | ns/test1-subnet | 7c7b68c5-d3c4-431c-9f69-fbc777b43ee5 | 10.0.1.128/26 | - -#. Create a pod in the created namespaces: - - .. code-block:: console - - $ kubectl create deployment -n test1 --image quay.io/kuryr/demo demo - deployment "demo" created - - $ kubectl create deployment -n test2 --image quay.io/kuryr/demo demo - deployment "demo" created - - $ kubectl -n test1 get pod -o wide - NAME READY STATUS RESTARTS AGE IP NODE - demo-5995548848-lmmjc 1/1 Running 0 7s 10.0.1.136 node1 - - $ kubectl -n test2 get pod -o wide - NAME READY STATUS RESTARTS AGE IP NODE - demo-5135352253-dfghd 1/1 Running 0 7s 10.0.1.134 node1 - -#. Create a service: - - .. code-block:: console - - $ kubectl expose -n test1 deploy/demo --port 80 --target-port 8080 - service "demo" exposed - - $ kubectl -n test1 get svc - NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE - demo ClusterIP 10.0.0.141 80/TCP 18s - -#. Test service connectivity from both namespaces: - - .. code-block:: console - - $ kubectl exec -n test1 -it demo-5995548848-lmmjc /bin/sh - test-1-pod$ curl 10.0.0.141 - demo-5995548848-lmmjc: HELLO! I AM ALIVE!!! - -#. And finally, to remove the namespace and all its resources, including - openstack networks, kuryrnetwork CRD, svc, pods, you just need to - do: - - .. code-block:: console - - $ kubectl delete namespace test1 - $ kubectl delete namespace test2 diff --git a/doc/source/installation/network_policy.rst b/doc/source/installation/network_policy.rst deleted file mode 100644 index 702157170..000000000 --- a/doc/source/installation/network_policy.rst +++ /dev/null @@ -1,350 +0,0 @@ -=========================================== -Enable network policy support functionality -=========================================== - -Enable policy, pod_label and namespace handlers to respond to network policy -events. As this is not done by default you'd have to explicitly add that to -the list of enabled handlers at kuryr.conf (further info on how to do this can -be found at :doc:`./devstack/containerized`): - -.. code-block:: ini - - [kubernetes] - enabled_handlers=vif,endpoints,service,kuryrloadbalancer,policy, - pod_label,namespace,kuryrnetwork,kuryrnetworkpolicy, - kuryrport - -Note that if you also want to enable prepopulation of ports pools upon creation -of first pod on pods network in a namespace, you need to also add the -kuryrnetwork_population handler -(more details on :doc:`./ports-pool`): - -.. code-block:: ini - - [kubernetes] - enabled_handlers=vif,endpoints,service,kuryrloadbalancer,policy, - pod_label,namespace,kuryrnetworkpolicy,kuryrnetwork, - kuryrport,kuryrnetwork_population - -After that, enable also the security group drivers for policies: - -.. code-block:: ini - - [kubernetes] - service_security_groups_driver = policy - pod_security_groups_driver = policy - -.. warning:: - - The correct behavior for pods that have no network policy applied is to - allow all ingress and egress traffic. If you want that to be enforced, - please make sure to create an SG allowing all traffic and add it to - ``[neutron_defaults]pod_security_groups`` setting in ``kuryr.conf``: - - .. code-block:: ini - - [neutron_defaults] - pod_security_groups = ALLOW_ALL_SG_ID - -Enable the namespace subnet driver by modifying the default pod_subnet_driver -option: - -.. code-block:: ini - - [kubernetes] - pod_subnets_driver = namespace - -Select the subnet pool from where the new subnets will get their CIDR: - -.. code-block:: ini - - [namespace_subnet] - pod_subnet_pool = SUBNET_POOL_ID - -Lastly, select the router where the new subnet will be connected: - -.. code-block:: ini - - [namespace_subnet] - pod_router = ROUTER_ID - -Note you need to restart the kuryr controller after applying the above step. -For devstack non-containerized deployments: - -.. code-block:: console - - $ sudo systemctl restart devstack@kuryr-kubernetes.service - -Same for containerized deployments: - -.. code-block:: console - - $ kubectl -n kube-system get pod | grep kuryr-controller - $ kubectl -n kube-system delete pod KURYR_CONTROLLER_POD_NAME - -For directly enabling the driver when deploying with devstack, you just need -to add the policy, pod_label and namespace handler and drivers with: - -.. code-block:: bash - - KURYR_ENABLED_HANDLERS=vif,kuryrport,endpoints,service,kuryrloadbalancer,policy,pod_label,kuryrnetworkpolicy,namespace,kuryrnetwork - KURYR_SG_DRIVER=policy - KURYR_SUBNET_DRIVER=namespace - -.. note:: - - If the loadbalancer maintains the source IP (such as ovn-octavia driver), - there is no need to enforce sg rules at the load balancer level. To disable - the enforcement, you need to set the following variable in DevStack's - local.conf: - - .. code-block:: bash - - KURYR_ENFORCE_SG_RULES=False - - To set that directly in kuryr.conf, the config to be set is: - - .. code-block:: ini - - [octavia_defaults] - enforce_sg_rules=False - -Testing the network policy support functionality ------------------------------------------------- - -#. Given a yaml file with a network policy, such as: - - .. code-block:: yaml - - apiVersion: networking.k8s.io/v1 - kind: NetworkPolicy - metadata: - name: test-network-policy - namespace: default - spec: - podSelector: - matchLabels: - project: default - policyTypes: - - Ingress - - Egress - ingress: - - from: - - namespaceSelector: - matchLabels: - project: default - ports: - - protocol: TCP - port: 6379 - egress: - - to: - - namespaceSelector: - matchLabels: - project: default - ports: - - protocol: TCP - port: 5978 - -#. Apply the network policy: - - .. code-block:: console - - $ kubectl apply -f network_policy.yml - -#. Check that the resources has been created: - - .. code-block:: console - - $ kubectl get kuryrnetworkpolicies - NAME AGE - test-network-policy 2s - - $ kubectl get networkpolicies - NAME POD-SELECTOR AGE - test-network-policy role=db 2s - - $ openstack security group list | grep sg-test-network-policy - | dabdf308-7eed-43ef-a058-af84d1954acb | sg-test-network-policy - -#. Check that the rules are in place for the security group: - - .. code-block:: console - - $ kubectl get kuryrnetworkpolicy test-network-policy -o yaml - - apiVersion: openstack.org/v1 - kind: KuryrNetworkPolicy - metadata: - annotations: - networkPolicyLink: - clusterName: "" - creationTimestamp: 2018-10-02T11:17:02Z - generation: 0 - name: test-network-policy - namespace: default - resourceVersion: "2117" - uid: afb99326-c634-11e8-b63d-002564fdd760 - spec: - egressSgRules: - - sgRule: - description: Kuryr-Kubernetes NetPolicy SG rule - direction: egress - ethertype: IPv4 - port_range_max: 5978 - port_range_min: 5978 - protocol: tcp - ingressSgRules: - - sgRule: - description: Kuryr-Kubernetes NetPolicy SG rule - direction: ingress - ethertype: IPv4 - port_range_max: 6379 - port_range_min: 6379 - protocol: tcp - status: - securityGroupId: cdee7815-3b49-4a3e-abc8-31e384ab75c5 - securityGroupRules: - … - - $ openstack security group rule list sg-test-network-policy --protocol tcp -c "IP Protocol" -c "Port Range" -c "Direction" --long - +-------------+------------+-----------+ - | IP Protocol | Port Range | Direction | - +-------------+------------+-----------+ - | tcp | 6379:6379 | ingress | - | tcp | 5978:5978 | egress | - +-------------+------------+-----------+ - -#. Create a pod: - - .. code-block:: console - - $ kubectl create deployment --image quay.io/kuryr/demo demo - deployment "demo" created - - $ kubectl get pod -o wide - NAME READY STATUS RESTARTS AGE IP - demo-5558c7865d-fdkdv 1/1 Running 0 44s 10.0.0.68 - -#. Get the pod port and check its security group rules: - - .. code-block:: console - - $ openstack port list --fixed-ip ip-address=10.0.0.68 -f value -c ID - 5d29b83c-714c-4579-8987-d0c0558420b3 - - $ openstack port show 5d29b83c-714c-4579-8987-d0c0558420b3 | grep security_group_ids - | security_group_ids | bb2ac605-56ff-4688-b4f1-1d045ad251d0 - - $ openstack security group rule list bb2ac605-56ff-4688-b4f1-1d045ad251d0 - --protocol tcp -c "IP Protocol" -c "Port Range" - +-------------+------------+-----------+ - | IP Protocol | Port Range | Direction | - +-------------+------------+-----------+ - | tcp | 6379:6379 | ingress | - | tcp | 5978:5978 | egress | - +-------------+------------+-----------+ - -#. Try to curl the pod on port 8080 (hint: it won't work!): - - .. code-block:: console - - $ curl 10.0.0.68:8080 - -#. Update network policy to allow ingress 8080 port: - - .. code-block:: console - - $ kubectl patch networkpolicy test-network-policy -p '{"spec":{"ingress":[{"ports":[{"port": 8080,"protocol": "TCP"}]}]}}' - networkpolicy "test-network-policy" patched - - $ kubectl get knp test-network-policy -o yaml - apiVersion: openstack.org/v1 - kind: KuryrNetworkPolicy - metadata: - annotations: - networkPolicyLink: - clusterName: "" - creationTimestamp: 2018-10-02T11:17:02Z - generation: 0 - name: test-network-policy - namespace: default - resourceVersion: "1546" - uid: afb99326-c634-11e8-b63d-002564fdd760 - spec: - egressSgRules: - - sgRule: - description: Kuryr-Kubernetes NetPolicy SG rule - direction: egress - ethertype: IPv4 - port_range_max: 5978 - port_range_min: 5978 - protocol: tcp - ingressSgRules: - - sgRule: - description: Kuryr-Kubernetes NetPolicy SG rule - direction: ingress - ethertype: IPv4 - port_range_max: 8080 - port_range_min: 8080 - protocol: tcp - status: - securityGroupId: cdee7815-3b49-4a3e-abc8-31e384ab75c5 - securityGroupRules: - … - - $ openstack security group rule list sg-test-network-policy -c "IP Protocol" -c "Port Range" -c "Direction" --long - +-------------+------------+-----------+ - | IP Protocol | Port Range | Direction | - +-------------+------------+-----------+ - | tcp | 8080:8080 | ingress | - | tcp | 5978:5978 | egress | - +-------------+------------+-----------+ - -#. Try to curl the pod ip after patching the network policy: - - .. code-block:: console - - $ curl 10.0.0.68:8080 - demo-5558c7865d-fdkdv: HELLO! I AM ALIVE!!! - - Note the curl only works from pods (neutron ports) on a namespace that has - the label `project: default` as stated on the policy namespaceSelector. - -#. We can also create a single pod, without a label and check that there is no - connectivity to it, as it does not match the network policy podSelector: - - .. code-block:: console - - $ cat sample-pod.yml - apiVersion: v1 - kind: Pod - metadata: - name: demo-pod - spec: - containers: - - image: quay.io/kuryr/demo - imagePullPolicy: Always - name: demo-pod - - $ kubectl apply -f sample-pod.yml - $ curl demo-pod-IP:8080 - NO REPLY - -#. If we add to the pod a label that match a network policy podSelector, in - this case 'project: default', the network policy will get applied on the - pod, and the traffic will be allowed: - - .. code-block:: console - - $ kubectl label pod demo-pod project=default - $ curl demo-pod-IP:8080 - demo-pod-XXX: HELLO! I AM ALIVE!!! - -#. Confirm the teardown of the resources once the network policy is removed: - - .. code-block:: console - - $ kubectl delete -f network_policy.yml - $ kubectl get kuryrnetworkpolicies - $ kubectl get networkpolicies - $ openstack security group list | grep sg-test-network-policy diff --git a/doc/source/installation/ports-pool.rst b/doc/source/installation/ports-pool.rst deleted file mode 100644 index aa43eaa87..000000000 --- a/doc/source/installation/ports-pool.rst +++ /dev/null @@ -1,177 +0,0 @@ -================================ -How to enable ports pool support -================================ - -To enable the utilization of the ports pool feature, the selected pool driver -needs to be included at the kuryr.conf at the kubernetes section. So, for the -baremetal deployment: - -.. code-block:: ini - - [kubernetes] - vif_pool_driver = neutron - -And for the nested (VLAN+Trunk) case: - -.. code-block:: ini - - [kubernetes] - vif_pool_driver = nested - -On the other hand, there are a few extra (optional) configuration options -regarding the maximum and minimum desired sizes of the pools, where the -maximum size can be disabled by setting it to 0: - -.. code-block:: ini - - [vif_pool] - ports_pool_max = 10 - ports_pool_min = 5 - -In addition the size of the bulk operation, e.g., the number of ports created -in a bulk request upon pool population, can be modified: - -.. code-block:: ini - - [vif_pool] - ports_pool_batch = 5 - -Note this value should be smaller than the ports_pool_max (if the -ports_pool_max is enabled). - -Finally, to define the frequency (in seconds) of ports recycle to allow them -to be reused by future pods, configure the following option: - -.. code-block:: ini - - [vif_pool] - ports_pool_update_frequency = 20 - -After these configurations, the final step is to restart the -kuryr-k8s-controller. At devstack deployment: - -.. code-block:: console - - $ sudo systemctl restart devstack@kuryr-kubernetes.service - -And for RDO packaging based installations: - -.. code-block:: console - - $ sudo systemctl restart kuryr-controller - -Note that for the containerized deployment, you need to edit the associated -ConfigMap to change the kuryr.conf files with: - -.. code-block:: console - - $ kubectl -n kube-system edit cm kuryr-config - -Then modify the kuryr.conf to modify the controller configuration regarding -the pools. After that, to have the new configuration applied you need to -restart the kuryr-controller just by killing the existing pod: - -.. code-block:: console - - $ kubectl -n kube-system get pod | grep kuryr-controller - $ kubectl -n kube-system delete pod KURYR_CONTROLLER_POD_NAME - - -Ports loading into pools ------------------------- - -Pre-created ports for the pools will be loaded and put back into their -respective pools upon controller restart. This allows the pre-creation of -neutron ports (or subports for the nested case) with a script or any other -preferred tool (e.g., heat templates) and load them into their respective -pools just by restarting the kuryr-controller (or even before installing it). -To do that you just need to ensure the ports are created with the right -device_owner: - -- For neutron pod driver: compute:kuryr (of the value at - kuryr.lib.constants.py) -- For nested-vlan pod driver: trunk:subport or compute:kuryr (or the value at - kuryr.lib.constants.py). But in this case they also need to be attached to an - active neutron trunk port, i.e., they need to be subports of an existing - trunk - - -Subports pools management tool ------------------------------- - -Note there is a developers tool available at `contrib/pools-management` to -create/delete ports in the desired pool(s) as well as to control the amount of -existing ports loaded into each pool. For more details on this read the readme -file on that folder. - - -Multi pod-vif drivers support with pools ----------------------------------------- - -There is a multi pool driver that supports hybrid environments where some -nodes are Bare Metal while others are running inside VMs, therefore having -different VIF drivers (e.g., neutron and nested-vlan). - -This new multi pool driver is the default pool driver used even if a different -vif_pool_driver is set at the config option. However if the configuration about -the mappings between the different pod vif and pools drivers is not provided at -the vif_pool_mapping config option of vif_pool configuration section only one -pool driver will be loaded -- using the standard pod_vif_driver and -vif_pool_driver config options, i.e., using the one selected at kuryr.conf -options. - -To enable the option of having different pools depending on the node's pod vif -types, you need to state the type of pool that you want for each pod vif -driver, e.g.: - -.. code-block:: ini - - [vif_pool] - vif_pool_mapping=nested-vlan:nested,neutron-vif:neutron - -This will use a pool driver nested to handle the pods whose vif driver is -nested-vlan, and a pool driver neutron to handle the pods whose vif driver is -neutron-vif. When the controller is requesting a vif for a pod in node X, it -will first read the node's annotation about pod_vif driver to use, e.g., -pod_vif: nested-vlan, and then use the corresponding pool driver -- which has -the right pod-vif driver set. - -.. note:: - - Previously, `pools_vif_drivers` configuration option provided similar - functionality, but is now deprecated and not recommended. It stored a - mapping from pool_driver => pod_vif_driver instead, disallowing the use of a - single pool driver as keys for multiple pod_vif_drivers. - - .. code-block:: ini - - [vif_pool] - pools_vif_drivers=nested:nested-vlan,neutron:neutron-vif - -Note that if no annotation is set on a node, the default pod_vif_driver is -used. - - -Populate pools on subnets creation for namespace subnet driver --------------------------------------------------------------- - -When the namespace subnet driver is used (either for namespace isolation or -for network policies) a new subnet is created for each namespace. The ports -associated to each namespace will therefore be on different pools. In order -to prepopulate the pools associated to a newly created namespace (i.e., -subnet), the next handler needs to be enabled: - -.. code-block:: ini - - [kubernetes] - enabled_handlers=vif,endpoints,service,kuryrloadbalancer,namespace, - *kuryrnetwork* - - -This can be enabled at devstack deployment time to by adding the next to the -local.conf: - -.. code-block:: bash - - KURYR_ENABLED_HANDLERS=vif,endpoints,service,kuryrloadbalancer,namespace, - *kuryrnetwork* diff --git a/doc/source/installation/services.rst b/doc/source/installation/services.rst deleted file mode 100644 index d61b809a8..000000000 --- a/doc/source/installation/services.rst +++ /dev/null @@ -1,787 +0,0 @@ -============================== -Kubernetes services networking -============================== - -Kuryr-Kubernetes default handler for handling Kubernetes `services`_ and -endpoints uses the OpenStack `Octavia API`_ in order to have each service -be implemented in the following way: - -* **Service**: It is translated to a single **LoadBalancer** and as many - **Listeners** and **Pools** as ports the Kubernetes Service spec defines. -* **ClusterIP**: It is translated to a LoadBalancer's VIP. -* **loadBalancerIP**: Translated to public IP associated with the - LoadBalancer's VIP. -* **Endpoints**: The Endpoint object is translated to a LoadBalancer's VIP. - -.. figure:: ../../images/lbaas_translation.svg - :width: 100% - :alt: Graphical depiction of the translation explained above - - In this diagram you can see how the Kubernetes entities in the top left - corner are implemented in plain Kubernetes networking (top-right) and in - Kuryr's default configuration (bottom) - -If you are paying attention and are familiar with the `Octavia API`_ you -probably noticed that we have separate pools for each exposed port in a -service. This is probably not optimal and we would probably benefit from -keeping a single Neutron pool that lists each of the per port listeners. - -Kuryr-Kubernetes uses OpenStack Octavia as the load balancing solution for -OpenStack and to provide connectivity to the Kubernetes Services. - -It is beyond the scope of this document to explain in detail the inner -workings of Openstack Octavia thus, only a brief explanation will be offered. - - -Octavia -------- - -OpenStack Octavia is a project that provides advanced Load Balancing by using -pre-existing OpenStack services. The requirements for running Kuryr with -OpenStack Octavia are the following: - -* Nova -* Neutron -* Glance -* Barbican (if TLS offloading functionality is enabled) -* Keystone -* Rabbit -* MySQL - -You can find a good explanation about the involved steps to install Octavia in -the `Octavia installation docs`_. - -Simplifying a lot, Octavia works by instantiating a compute resource, i.e. a -Nova VM, and running HAProxy inside. These single load balancer Nova VMs are -called *Amphorae*. Each *Amphora* has a separate linux network namespace where -HAProxy runs and that is connected to the Kuryr services network. The VM host -network namespace is used by Octavia to reconfigure and monitor the Load -Balancer, which it talks to via HAProxy's control unix domain socket. - -Running Kuryr with Octavia means that each Kubernetes service that runs in the -cluster will need at least one Load Balancer VM, i.e., an *Amphora*. To avoid -single point of failure at Amphora, Octavia should be configured to support -active/standby loadbalancer topology. In addition, it is important to -configure the right Octavia flavor for your deployment and to size the compute -nodes appropriately so that Octavia can operate well. - -Another important consideration is where do the Amphorae run, i.e., whether the -worker nodes should also be compute nodes so that they run the Amphorae or if -Amphorae should be run separately. If your compute nodes are big enough, it -would help avoiding extra hops if the amphorae were scheduled in the worker -nodes, but how much significant that is, depends on your latency and throughput -requirements. - -Octavia uses `Load Balancer drivers`_ to handle all communications with -*Amphorae*. By default, Kuryr-Kubernetes uses the reference Octavia driver -which is the `Amphora driver`_. Kuryr also supports the use of -`OVN Octavia driver`_. - - -OVN Octavia Provider Driver ---------------------------- - -Kuryr supports the creation of a load balancer with OVN provider driver. When -'ovn' provider is enabled as one of the Octavia Load Balancer providers, the -load balancing is executed by the virtual switch data-path engine and there is -no need to create VMs. This means there is no additional overhead of VMs as is -required when using Octavia with the default Amphora driver. - -You can find additional information about the driver, its limitations and how -to create OVN-based load balancers in `OVN as Provider Driver for Octavia`_. - - -.. _k8s_default_configuration: - -Default configuration -~~~~~~~~~~~~~~~~~~~~~ - -Kuryr can use Octavia in two ways: - -* The one that is commonly referred to as **Layer 3**, this means that Kuryr - will tell Octavia not to add a Neutron port to the pod network for each - load balancer. Instead, **it relies on the pod and the service subnets being - routable**. This means that the communication from Pods to Services and back - will go through the router. Depending on the SDN of your choice, this may - have performance implications. -* The **Layer 2** way, where kuryr will tell Octavia to add a Neutron port to - the pod network for each load balancer. Therefore the communication from - Services to its Pods members and back will go directly through L2 layer. The - drawback of this approach is the extra usage of neutron ports in the Pods - subnet, that needs to be accordingly dimensioned. - -The services and pods subnets should be created. - -#. Create pod network: - - .. code-block:: console - - $ openstack network create pod - +---------------------------+--------------------------------------+ - | Field | Value | - +---------------------------+--------------------------------------+ - | admin_state_up | UP | - | availability_zone_hints | | - | availability_zones | | - | created_at | 2017-08-11T10:51:25Z | - | description | | - | dns_domain | None | - | id | 4593045c-4233-4b4c-8527-35608ab0eaae | - | ipv4_address_scope | None | - | ipv6_address_scope | None | - | is_default | False | - | is_vlan_transparent | None | - | mtu | 1450 | - | name | pod | - | port_security_enabled | True | - | project_id | 90baf12877ba49a786419b2cacc2c954 | - | provider:network_type | vxlan | - | provider:physical_network | None | - | provider:segmentation_id | 21 | - | qos_policy_id | None | - | revision_number | 2 | - | router:external | Internal | - | segments | None | - | shared | False | - | status | ACTIVE | - | subnets | | - | tags | [] | - | updated_at | 2017-08-11T10:51:25Z | - +---------------------------+--------------------------------------+ - -#. Create pod subnet: - - .. code-block:: console - - $ openstack subnet create --network pod --no-dhcp \ - --gateway 10.1.255.254 \ - --subnet-range 10.1.0.0/16 \ - pod_subnet - +-------------------------+--------------------------------------+ - | Field | Value | - +-------------------------+--------------------------------------+ - | allocation_pools | 10.1.0.1-10.1.255.253 | - | cidr | 10.1.0.0/16 | - | created_at | 2017-08-11T10:55:25Z | - | description | | - | dns_nameservers | | - | enable_dhcp | False | - | gateway_ip | 10.1.255.254 | - | host_routes | | - | id | e0a888ab-9915-4685-a600-bffe240dc58b | - | ip_version | 4 | - | ipv6_address_mode | None | - | ipv6_ra_mode | None | - | name | pod_subnet | - | network_id | 4593045c-4233-4b4c-8527-35608ab0eaae | - | project_id | 90baf12877ba49a786419b2cacc2c954 | - | revision_number | 0 | - | segment_id | None | - | service_types | | - | subnetpool_id | None | - | tags | [] | - | updated_at | 2017-08-11T10:55:25Z | - | use_default_subnet_pool | None | - +-------------------------+--------------------------------------+ - -#. Create services network: - - .. code-block:: console - - $ openstack network create services - +---------------------------+--------------------------------------+ - | Field | Value | - +---------------------------+--------------------------------------+ - | admin_state_up | UP | - | availability_zone_hints | | - | availability_zones | | - | created_at | 2017-08-11T10:53:36Z | - | description | | - | dns_domain | None | - | id | 560df0c2-537c-41c0-b22c-40ef3d752574 | - | ipv4_address_scope | None | - | ipv6_address_scope | None | - | is_default | False | - | is_vlan_transparent | None | - | mtu | 1450 | - | name | services | - | port_security_enabled | True | - | project_id | 90baf12877ba49a786419b2cacc2c954 | - | provider:network_type | vxlan | - | provider:physical_network | None | - | provider:segmentation_id | 94 | - | qos_policy_id | None | - | revision_number | 2 | - | router:external | Internal | - | segments | None | - | shared | False | - | status | ACTIVE | - | subnets | | - | tags | [] | - | updated_at | 2017-08-11T10:53:37Z | - +---------------------------+--------------------------------------+ - -#. Create service subnet. We reserve the first half of the subnet range for the - VIPs and the second half for the loadbalancer vrrp ports: - - .. code-block:: console - - $ openstack subnet create --network services --no-dhcp \ - --gateway 10.2.255.254 \ - --ip-version 4 \ - --allocation-pool start=10.2.128.1,end=10.2.255.253 \ - --subnet-range 10.2.0.0/16 \ - service_subnet - +-------------------------+--------------------------------------+ - | Field | Value | - +-------------------------+--------------------------------------+ - | allocation_pools | 10.2.128.1-10.2.255.253 | - | cidr | 10.2.0.0/16 | - | created_at | 2017-08-11T11:02:24Z | - | description | | - | dns_nameservers | | - | enable_dhcp | False | - | gateway_ip | 10.2.255.254 | - | host_routes | | - | id | d6438a81-22fa-4a88-9b05-c4723662ef36 | - | ip_version | 4 | - | ipv6_address_mode | None | - | ipv6_ra_mode | None | - | name | service_subnet | - | network_id | 560df0c2-537c-41c0-b22c-40ef3d752574 | - | project_id | 90baf12877ba49a786419b2cacc2c954 | - | revision_number | 0 | - | segment_id | None | - | service_types | | - | subnetpool_id | None | - | tags | [] | - | updated_at | 2017-08-11T11:02:24Z | - | use_default_subnet_pool | None | - +-------------------------+--------------------------------------+ - -#. Create a router to give L3 connectivity between the pod and the service - subnets. If you already have one, you can use it: - - .. code-block:: console - - $ openstack router create kuryr-kubernetes - +-------------------------+--------------------------------------+ - | Field | Value | - +-------------------------+--------------------------------------+ - | admin_state_up | UP | - | availability_zone_hints | | - | availability_zones | | - | created_at | 2017-08-11T11:06:21Z | - | description | | - | distributed | False | - | external_gateway_info | None | - | flavor_id | None | - | ha | False | - | id | d2a06d95-8abd-471b-afbe-9dfe475dd8a4 | - | name | kuryr-kubernetes | - | project_id | 90baf12877ba49a786419b2cacc2c954 | - | revision_number | None | - | routes | | - | status | ACTIVE | - | tags | [] | - | updated_at | 2017-08-11T11:06:21Z | - +-------------------------+--------------------------------------+ - -#. Create router ports in the pod and service subnets: - - .. code-block:: console - - $ openstack port create --network pod --fixed-ip ip-address=10.1.255.254 pod_subnet_router - +-----------------------+---------------------------------------------------------------------------+ - | Field | Value | - +-----------------------+---------------------------------------------------------------------------+ - | admin_state_up | UP | - | allowed_address_pairs | | - | binding_host_id | | - | binding_profile | | - | binding_vif_details | | - | binding_vif_type | unbound | - | binding_vnic_type | normal | - | created_at | 2017-08-11T11:10:47Z | - | data_plane_status | None | - | description | | - | device_id | | - | device_owner | | - | dns_assignment | None | - | dns_name | None | - | extra_dhcp_opts | | - | fixed_ips | ip_address='10.1.255.254', subnet_id='e0a888ab-9915-4685-a600-bffe240dc58b' | - | id | 0a82dfff-bf45-4738-a1d2-36d4ad81a5fd | - | ip_address | None | - | mac_address | fa:16:3e:49:70:b5 | - | name | pod_subnet_router | - | network_id | 4593045c-4233-4b4c-8527-35608ab0eaae | - | option_name | None | - | option_value | None | - | port_security_enabled | True | - | project_id | 90baf12877ba49a786419b2cacc2c954 | - | qos_policy_id | None | - | revision_number | 3 | - | security_group_ids | 2d6e006e-572a-4939-93b8-0f45b40777f7 | - | status | DOWN | - | subnet_id | None | - | tags | [] | - | trunk_details | None | - | updated_at | 2017-08-11T11:10:47Z | - +-----------------------+---------------------------------------------------------------------------+ - - $ openstack port create --network services \ - --fixed-ip ip-address=10.2.255.254 \ - service_subnet_router - +-----------------------+-----------------------------------------------------------------------------+ - | Field | Value | - +-----------------------+-----------------------------------------------------------------------------+ - | admin_state_up | UP | - | allowed_address_pairs | | - | binding_host_id | | - | binding_profile | | - | binding_vif_details | | - | binding_vif_type | unbound | - | binding_vnic_type | normal | - | created_at | 2017-08-11T11:16:56Z | - | data_plane_status | None | - | description | | - | device_id | | - | device_owner | | - | dns_assignment | None | - | dns_name | None | - | extra_dhcp_opts | | - | fixed_ips | ip_address='10.2.255.254', subnet_id='d6438a81-22fa-4a88-9b05-c4723662ef36' | - | id | 572cee3d-c30a-4ee6-a59c-fe9529a6e168 | - | ip_address | None | - | mac_address | fa:16:3e:65:de:e5 | - | name | service_subnet_router | - | network_id | 560df0c2-537c-41c0-b22c-40ef3d752574 | - | option_name | None | - | option_value | None | - | port_security_enabled | True | - | project_id | 90baf12877ba49a786419b2cacc2c954 | - | qos_policy_id | None | - | revision_number | 3 | - | security_group_ids | 2d6e006e-572a-4939-93b8-0f45b40777f7 | - | status | DOWN | - | subnet_id | None | - | tags | [] | - | trunk_details | None | - | updated_at | 2017-08-11T11:16:57Z | - +-----------------------+-----------------------------------------------------------------------------+ - -#. Add the router to the service and the pod subnets: - - .. code-block:: console - - $ openstack router add port \ - d2a06d95-8abd-471b-afbe-9dfe475dd8a4 \ - 0a82dfff-bf45-4738-a1d2-36d4ad81a5fd - - $ openstack router add port \ - d2a06d95-8abd-471b-afbe-9dfe475dd8a4 \ - 572cee3d-c30a-4ee6-a59c-fe9529a6e168 - -#. Configure kuryr.conf pod subnet and service subnet to point to their - respective subnets created in step (2) and (4): - - .. code-block:: ini - - [neutron_defaults] - pod_subnet = e0a888ab-9915-4685-a600-bffe240dc58b - service_subnet = d6438a81-22fa-4a88-9b05-c4723662ef36 - -#. Configure Kubernetes API server to use only a subset of the service - addresses, **10.2.0.0/17**. The rest will be used for loadbalancer *vrrp* - ports managed by Octavia. To configure Kubernetes with this CIDR range you - have to add the following parameter to its command line invocation: - - .. code-block:: console - - --service-cluster-ip-range=10.2.0.0/17 - - As a result of this, Kubernetes will allocate the **10.2.0.1** address to - the Kubernetes API service, i.e., the service used for pods to talk to the - Kubernetes API server. It will be able to allocate service addresses up - until **10.2.127.254**. The rest of the addresses, as stated above, will be - for Octavia load balancer *vrrp* ports. **If this subnetting was not done, - Octavia would allocate *vrrp* ports with the Neutron IPAM from the same - range as Kubernetes service IPAM and we'd end up with conflicts**. - -#. Once you have Kubernetes installed and you have the API host reachable from - the pod subnet, follow the `Making the Pods be able to reach the Kubernetes - API`_ section - -#. For the external services (type=LoadBalancer) case, - two methods are supported: - - + Pool - external IPs are allocated from pre-defined pool - + User - user specify the external IP address - - In case 'Pool' method should be supported, execute the next steps: - - #. Create an external/provider network - #. Create subnet/pool range of external CIDR - #. Connect external subnet to kuryr-kubernetes router - #. Configure external network details in Kuryr.conf as follows: - - .. code-block:: ini - - [neutron_defaults] - external_svc_net= - # 'external_svc_subnet' field is optional, set this field in case - # multiple subnets attached to 'external_svc_net' - external_svc_subnet= - - From this point for each K8s service of type=LoadBalancer and in which - 'load-balancer-ip' is not specified, an external IP from - 'external_svc_subnet' will be allocated. - - For the 'User' case, user should first create an external/floating IP: - - .. code-block:: console - - $ #openstack floating ip create --subnet - $ openstack floating ip create --subnet 48ddcfec-1b29-411b-be92-8329cc09fc12 3b4eb25e-e103-491f-a640-a6246d588561 - +---------------------------+--------------------------------------+ - | Field | Value | - +---------------------+--------------------------------------+ - | created_at | 2017-10-02T09:22:37Z | - | description | | - | fixed_ip_address | None | - | floating_ip_address | 172.24.4.13 | - | floating_network_id | 3b4eb25e-e103-491f-a640-a6246d588561 | - | id | 1157e2fd-de64-492d-b955-88ea203b4c37 | - | name | 172.24.4.13 | - | port_id | None | - | project_id | 6556471f4f7b40e2bde1fc6e4aba0eef | - | revision_number | 0 | - | router_id | None | - | status | DOWN | - | updated_at | 2017-10-02T09:22:37Z | - +---------------------+--------------------------------------+ - - and then create k8s service with type=LoadBalancer and - load-balancer-ip= (e.g: 172.24.4.13) - - In both 'User' and 'Pool' methods, the external IP address could be found - in k8s service status information (under loadbalancer/ingress/ip) - - -Alternative configuration -~~~~~~~~~~~~~~~~~~~~~~~~~ - -It is actually possible to avoid this routing by performing a deployment change -that was successfully pioneered by the people at EasyStack Inc. which consists -of doing the following: - -#. Create the pod network and subnet so that it has enough addresses for both - the pod ports and the service ports. We are limiting the allocation range - out of the service range so that nor Octavia nor Kuryr-Kubernetes pod - allocation create ports in the part reserved for services. - - Create the network: - - .. code-block:: console - - $ openstack network create k8s - +---------------------------+--------------------------------------+ - | Field | Value | - +---------------------------+--------------------------------------+ - | admin_state_up | UP | - | availability_zone_hints | | - | availability_zones | | - | created_at | 2017-08-10T15:58:19Z | - | description | | - | dns_domain | None | - | id | 9fa35362-0bf7-4b5b-8921-f0c7f60a7dd3 | - | ipv4_address_scope | None | - | ipv6_address_scope | None | - | is_default | False | - | is_vlan_transparent | None | - | mtu | 1450 | - | name | k8s | - | port_security_enabled | True | - | project_id | 90baf12877ba49a786419b2cacc2c954 | - | provider:network_type | vxlan | - | provider:physical_network | None | - | provider:segmentation_id | 69 | - | qos_policy_id | None | - | revision_number | 2 | - | router:external | Internal | - | segments | None | - | shared | False | - | status | ACTIVE | - | subnets | | - | tags | [] | - | updated_at | 2017-08-10T15:58:20Z | - +---------------------------+--------------------------------------+ - - Create the subnet. Note that we disable dhcp as Kuryr-Kubernetes pod subnets - have no need for them for Pod networking. We also put the gateway on the - last IP of the subnet range so that the beginning of the range can be kept - for Kubernetes driven service IPAM: - - .. code-block:: console - - $ openstack subnet create --network k8s --no-dhcp \ - --gateway 10.0.255.254 \ - --ip-version 4 \ - --allocation-pool start=10.0.64.0,end=10.0.255.253 \ - --subnet-range 10.0.0.0/16 \ - k8s_subnet - +-------------------------+--------------------------------------+ - | Field | Value | - +-------------------------+--------------------------------------+ - | allocation_pools | 10.0.64.0-10.0.255.253 | - | cidr | 10.0.0.0/16 | - | created_at | 2017-08-10T16:07:11Z | - | description | | - | dns_nameservers | | - | enable_dhcp | False | - | gateway_ip | 10.0.255.254 | - | host_routes | | - | id | 3a1df0d9-f738-4293-8de6-6c624f742980 | - | ip_version | 4 | - | ipv6_address_mode | None | - | ipv6_ra_mode | None | - | name | k8s_subnet | - | network_id | 9fa35362-0bf7-4b5b-8921-f0c7f60a7dd3 | - | project_id | 90baf12877ba49a786419b2cacc2c954 | - | revision_number | 0 | - | segment_id | None | - | service_types | | - | subnetpool_id | None | - | tags | [] | - | updated_at | 2017-08-10T16:07:11Z | - | use_default_subnet_pool | None | - +-------------------------+--------------------------------------+ - -#. Configure kuryr.conf pod subnet and service subnet to point to the same - subnet created in step (1): - - .. code-block:: ini - - [neutron_defaults] - pod_subnet = 3a1df0d9-f738-4293-8de6-6c624f742980 - service_subnet = 3a1df0d9-f738-4293-8de6-6c624f742980 - -#. Configure Kubernetes API server to use only a subset of the addresses for - services, **10.0.0.0/18**. The rest will be used for pods. To configure - Kubernetes with this CIDR range you have to add the following parameter to - its command line invocation: - - .. code-block:: console - - --service-cluster-ip-range=10.0.0.0/18 - - As a result of this, Kubernetes will allocate the **10.0.0.1** address to - the Kubernetes API service, i.e., the service used for pods to talk to the - Kubernetes API server. It will be able to allocate service addresses up - until **10.0.63.255**. The rest of the addresses will be for pods or Octavia - load balancer *vrrp* ports. - -#. Once you have Kubernetes installed and you have the API host reachable from - the pod subnet, follow the `Making the Pods be able to reach the Kubernetes - API`_ section - - -.. _k8s_lb_reachable: - -Making the Pods be able to reach the Kubernetes API ---------------------------------------------------- - -Once you have Kubernetes installed and you have the API host reachable from the -pod subnet (that means you should add 10.0.255.254 to a router that gives -access to it), you should create a load balancer configuration for the -Kubernetes service to be accessible to Pods. - -#. Create the load balancer (Kubernetes always picks the first address of the - range we gave in *--service-cluster-ip-range*): - - .. code-block:: console - - $ openstack loadbalancer create --vip-address 10.0.0.1 \ - --vip-subnet-id 3a1df0d9-f738-4293-8de6-6c624f742980 \ - --name default/kubernetes - +---------------------+--------------------------------------+ - | Field | Value | - +---------------------+--------------------------------------+ - | admin_state_up | True | - | created_at | 2017-08-10T16:16:30 | - | description | | - | flavor | | - | id | 84c1c0da-2065-43c5-86c9-f2235566b111 | - | listeners | | - | name | default/kubernetes | - | operating_status | OFFLINE | - | pools | | - | project_id | 90baf12877ba49a786419b2cacc2c954 | - | provider | octavia | - | provisioning_status | PENDING_CREATE | - | updated_at | None | - | vip_Address | 10.0.0.1 | - | vip_network_id | 9fa35362-0bf7-4b5b-8921-f0c7f60a7dd3 | - | vip_port_id | d1182d33-686b-4bcc-9754-8d46e373d647 | - | vip_subnet_id | 3a1df0d9-f738-4293-8de6-6c624f742980 | - +---------------------+--------------------------------------+ - -#. Create the Pool for all the Kubernetes API hosts: - - .. code-block:: console - - $ openstack loadbalancer pool create --name default/kubernetes:HTTPS:443 \ - --protocol HTTPS --lb-algorithm LEAST_CONNECTIONS \ - --loadbalancer 84c1c0da-2065-43c5-86c9-f2235566b111 - +---------------------+--------------------------------------+ - | Field | Value | - +---------------------+--------------------------------------+ - | admin_state_up | True | - | created_at | 2017-08-10T16:21:52 | - | description | | - | healthmonitor_id | | - | id | 22ae71be-1d71-4a6d-9dd8-c6a4f8e87061 | - | lb_algorithm | LEAST_CONNECTIONS | - | listeners | | - | loadbalancers | 84c1c0da-2065-43c5-86c9-f2235566b111 | - | members | | - | name | default/kubernetes:HTTPS:443 | - | operating_status | OFFLINE | - | project_id | 90baf12877ba49a786419b2cacc2c954 | - | protocol | HTTPS | - | provisioning_status | PENDING_CREATE | - | session_persistence | None | - | updated_at | None | - +---------------------+--------------------------------------+ - -#. Add a member for each Kubernetes API server. We recommend setting the name - to be the hostname of the host where the Kubernetes API runs: - - .. code-block:: console - - $ openstack loadbalancer member create \ - --name k8s-master-0 \ - --address 192.168.1.2 \ - --protocol-port 6443 \ - 22ae71be-1d71-4a6d-9dd8-c6a4f8e87061 - +---------------------+--------------------------------------+ - | Field | Value | - +---------------------+--------------------------------------+ - | address | 192.168.1.2 | - | admin_state_up | True | - | created_at | 2017-08-10T16:40:57 | - | id | 9ba24740-3666-49e8-914d-233068de6423 | - | name | k8s-master-0 | - | operating_status | NO_MONITOR | - | project_id | 90baf12877ba49a786419b2cacc2c954 | - | protocol_port | 6443 | - | provisioning_status | PENDING_CREATE | - | subnet_id | None | - | updated_at | None | - | weight | 1 | - | monitor_port | None | - | monitor_address | None | - +---------------------+--------------------------------------+ - -#. Create a listener for the load balancer that defaults to the created pool: - - .. code-block:: console - - $ openstack loadbalancer listener create \ - --name default/kubernetes:HTTPS:443 \ - --protocol HTTPS \ - --default-pool 22ae71be-1d71-4a6d-9dd8-c6a4f8e87061 \ - --protocol-port 443 \ - 84c1c0da-2065-43c5-86c9-f2235566b111 - +---------------------------+--------------------------------------+ - | Field | Value | - +---------------------------+--------------------------------------+ - | admin_state_up | True | - | connection_limit | -1 | - | created_at | 2017-08-10T16:46:55 | - | default_pool_id | 22ae71be-1d71-4a6d-9dd8-c6a4f8e87061 | - | default_tls_container_ref | None | - | description | | - | id | f18b9af6-6336-4a8c-abe5-cb7b89c6b621 | - | insert_headers | None | - | l7policies | | - | loadbalancers | 84c1c0da-2065-43c5-86c9-f2235566b111 | - | name | default/kubernetes:HTTPS:443 | - | operating_status | OFFLINE | - | project_id | 90baf12877ba49a786419b2cacc2c954 | - | protocol | HTTPS | - | protocol_port | 443 | - | provisioning_status | PENDING_CREATE | - | sni_container_refs | [] | - | updated_at | 2017-08-10T16:46:55 | - +---------------------------+--------------------------------------+ - - -.. _services_troubleshooting: - -Troubleshooting ---------------- - -* **Pods can talk to each other with IPv6 but they can't talk to services.** - - This means that most likely you forgot to create a security group or rule - for the pods to be accessible by the service CIDR. You can find an example - here: - - .. code-block:: console - - $ openstack security group create service_pod_access - +-----------------+-------------------------------------------------------------------------------------------------------------------------------------------------------+ - | Field | Value | - +-----------------+-------------------------------------------------------------------------------------------------------------------------------------------------------+ - | created_at | 2017-08-16T10:01:45Z | - | description | service_pod_access | - | id | f0b6f0bd-40f7-4ab6-a77b-3cf9f7cc28ac | - | name | service_pod_access | - | project_id | 90baf12877ba49a786419b2cacc2c954 | - | revision_number | 2 | - | rules | created_at='2017-08-16T10:01:45Z', direction='egress', ethertype='IPv4', id='bd759b4f-c0f5-4cff-a30a-3cd8544d2822', updated_at='2017-08-16T10:01:45Z' | - | | created_at='2017-08-16T10:01:45Z', direction='egress', ethertype='IPv6', id='c89c3f3e-a326-4902-ba26-5315e2d95320', updated_at='2017-08-16T10:01:45Z' | - | updated_at | 2017-08-16T10:01:45Z | - +-----------------+-------------------------------------------------------------------------------------------------------------------------------------------------------+ - - $ openstack security group rule create --remote-ip 10.2.0.0/16 \ - --ethertype IPv4 f0b6f0bd-40f7-4ab6-a77b-3cf9f7cc28ac - +-------------------+--------------------------------------+ - | Field | Value | - +-------------------+--------------------------------------+ - | created_at | 2017-08-16T10:04:57Z | - | description | | - | direction | ingress | - | ether_type | IPv4 | - | id | cface77f-666f-4a4c-8a15-a9c6953acf08 | - | name | None | - | port_range_max | None | - | port_range_min | None | - | project_id | 90baf12877ba49a786419b2cacc2c954 | - | protocol | tcp | - | remote_group_id | None | - | remote_ip_prefix | 10.2.0.0/16 | - | revision_number | 0 | - | security_group_id | f0b6f0bd-40f7-4ab6-a77b-3cf9f7cc28ac | - | updated_at | 2017-08-16T10:04:57Z | - +-------------------+--------------------------------------+ - - Then remember to add the new security groups to the comma-separated - *pod_security_groups* setting in the section *[neutron_defaults]* of - /etc/kuryr/kuryr.conf. After making the kuryr.conf edits, you need to - restart the kuryr controller for the changes to take effect. - - If you want your current pods to get this change applied, the most - comfortable way to do that is to delete them and let the Kubernetes - Deployment create them automatically for you. - - -.. _services: https://kubernetes.io/docs/concepts/services-networking/service/ -.. _Octavia API: https://docs.openstack.org/api-ref/load-balancer/v2/ -.. _Octavia installation docs: https://docs.openstack.org/octavia/latest/contributor/guides/dev-quick-start.html -.. _Load Balancer drivers: https://docs.openstack.org/octavia/latest/ -.. _Amphora driver: https://docs.openstack.org/octavia/latest/ -.. _OVN Octavia driver: https://docs.openstack.org/ovn-octavia-provider/latest/ -.. _OVN as Provider Driver for Octavia: https://docs.openstack.org/networking-ovn/stein/admin/loadbalancer.html diff --git a/doc/source/installation/testing_connectivity.rst b/doc/source/installation/testing_connectivity.rst deleted file mode 100644 index c04ee5b09..000000000 --- a/doc/source/installation/testing_connectivity.rst +++ /dev/null @@ -1,240 +0,0 @@ -============================ -Testing Network Connectivity -============================ - -Once the environment is ready, we can test that network connectivity works -among pods. First we check the status of the kubernetes cluster: - -.. code-block:: console - - $ kubectl get nodes - NAME STATUS AGE VERSION - masterodl-vm Ready 1h v1.6.2 - - $ kubectl get pods - No resources found. - - $ kubectl get svc - NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE - kubernetes 10.0.0.129 443/TCP 1h - -As we can see, this is a one node cluster with currently no pods running, and -with the kubernetes API service listening on port 443 at 10.0.0.129 (which -matches the ip assigned to the load balancer created for it). - -To test proper configuration and connectivity we firstly create a sample -deployment with: - -.. code-block:: console - - $ kubectl create deployment demo --image=quay.io/kuryr/demo - deployment "demo" created - -After a few seconds, the container is up an running, and a neutron port was -created with the same IP that got assigned to the pod: - -.. code-block:: console - - $ kubectl get pods - NAME READY STATUS RESTARTS AGE - demo-7dd477695c-25s99 1/1 Running 0 1m - - $ kubectl describe pod demo-2293951457-j29nb | grep IP: - IP: 10.0.1.122 - - $ openstack port list | grep demo - | 468d3d7e-4dd1-4e42-9200-e3eb97d603e6 | default/demo-7dd477695c-25s99 | fa:16:3e:24:ba:40 | ip_address='10.0.1.122', subnet_id='15cfabf7-c7e0-4964-a3c0-0545e9e4ea2f' | ACTIVE | - -We can then scale the deployment to 2 pods, and check connectivity between -them: - -.. code-block:: console - - $ kubectl scale deploy/demo --replicas=2 - deployment "demo" scaled - - $ kubectl get pods - NAME READY STATUS RESTARTS AGE - demo-7dd477695c-25s99 1/1 Running 0 36m - demo-7dd477695c-fbq4r 1/1 Running 0 30m - - - $ openstack port list | grep demo - | 468d3d7e-4dd1-4e42-9200-e3eb97d603e6 | default/demo-7dd477695c-25s99 | fa:16:3e:24:ba:40 | ip_address='10.0.1.122', subnet_id='15cfabf7-c7e0-4964-a3c0-0545e9e4ea2f' | ACTIVE | - | b54da942-2241-4f07-8e2e-e45a7367fa69 | default/demo-7dd477695c-fbq4r | fa:16:3e:41:57:a4 | ip_address='10.0.1.116', subnet_id='15cfabf7-c7e0-4964-a3c0-0545e9e4ea2f' | ACTIVE | - - $ kubectl exec -it demo-7dd477695c-25s99 -- /bin/sh - - sh-4.2$ curl 10.0.1.122:8080 - demo-7dd477695c-25s99: HELLO, I AM ALIVE!!! - - - sh-4.2$ curl 10.0.1.116:8080 - demo-7dd477695c-fbq4r: HELLO, I AM ALIVE!!! - - - sh-4.2$ ping 10.0.1.116 - PING 10.0.1.116 (10.0.1.116) 56(84) bytes of data. - 64 bytes from 10.0.1.116: icmp_seq=1 ttl=64 time=1.14 ms - 64 bytes from 10.0.1.116: icmp_seq=2 ttl=64 time=0.250 ms - -Next, we expose the service so that a neutron load balancer is created and -the service is exposed and load balanced among the available pods: - -.. code-block:: console - - $ kubectl get svc - NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE - kubernetes 10.0.0.129 443/TCP 1h - - $ kubectl expose deploy/demo --port=80 --target-port=8080 - service "demo" exposed - - $ kubectl get svc - NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE - demo 10.0.0.140 80/TCP 6s - kubernetes 10.0.0.129 443/TCP 1h - - $ openstack loadbalancer list - +--------------------------------------+---------------------+----------------------------------+-------------+---------------------+------------------+----------+ - | id | name | project_id | vip_address | provisioning_status | operating_status | provider | - +--------------------------------------+---------------------+----------------------------------+-------------+---------------------+------------------+----------+ - | e4949ba4-7f73-43ad-8091-d123dea12dae | default/kubernetes | 1ea4a08913d74aff8ed3e3bf31851236 | 10.0.0.129 | ACTIVE | ONLINE | amphora | - | 994893a7-d67f-4af2-b2fe-5a03f03102b1 | default/demo | 1ea4a08913d74aff8ed3e3bf31851236 | 10.0.0.140 | ACTIVE | ONLINE | amphora | - +--------------------------------------+---------------------+----------------------------------+-------------+---------------------+------------------+----------+ - - - $ openstack loadbalancer listener list - +--------------------------------------+--------------------------------------+----------------------------+----------------------------------+----------+---------------+----------------+ - | id | default_pool_id | name | project_id | protocol | protocol_port | admin_state_up | - +--------------------------------------+--------------------------------------+----------------------------+----------------------------------+----------+---------------+----------------+ - | 3223bf4a-4cdd-4d0f-9922-a3d3eb6f5e4f | 6212ecc2-c118-434a-8564-b4e763e9fa74 | default/kubernetes:443 | 1ea4a08913d74aff8ed3e3bf31851236 | HTTPS | 443 | True | - | 8aebeb5e-bccc-4519-8b68-07847c1b5b73 | f5a61ce7-3e2f-4a33-bd1f-8f12b8d6a6aa | default/demo:TCP:80 | 1ea4a08913d74aff8ed3e3bf31851236 | TCP | 80 | True | - +--------------------------------------+--------------------------------------+----------------------------+----------------------------------+----------+---------------+----------------+ - - $ openstack loadbalancer pool list - +--------------------------------------+----------------------------+----------------------------------+---------------------+----------+--------------+----------------+ - | id | name | project_id | provisioning_status | protocol | lb_algorithm | admin_state_up | - +--------------------------------------+----------------------------+----------------------------------+---------------------+----------+--------------+----------------+ - | 6212ecc2-c118-434a-8564-b4e763e9fa74 | default/kubernetes:443 | 1ea4a08913d74aff8ed3e3bf31851236 | ACTIVE | HTTPS | ROUND_ROBIN | True | - | f5a61ce7-3e2f-4a33-bd1f-8f12b8d6a6aa | default/demo:TCP:80 | 1ea4a08913d74aff8ed3e3bf31851236 | ACTIVE | TCP | ROUND_ROBIN | True | - +--------------------------------------+----------------------------+----------------------------------+---------------------+----------+--------------+----------------+ - - - $ openstack loadbalancer member list default/demo:TCP:80 - +--------------------------------------+------------------------------------+----------------------------------+---------------------+------------+---------------+------------------+--------+ - | id | name | project_id | provisioning_status | address | protocol_port | operating_status | weight | - +--------------------------------------+------------------------------------+----------------------------------+---------------------+------------+---------------+------------------+--------+ - | 8aff18b1-1e5b-45df-ade1-44ed0e75ca5e | default/demo-7dd477695c-fbq4r:8080 | 1ea4a08913d74aff8ed3e3bf31851236 | ACTIVE | 10.0.1.116 | 8080 | NO_MONITOR | 1 | - | 2c2c7a54-ad38-4182-b34f-daec03ee0a9a | default/demo-7dd477695c-25s99:8080 | 1ea4a08913d74aff8ed3e3bf31851236 | ACTIVE | 10.0.1.122 | 8080 | NO_MONITOR | 1 | - +--------------------------------------+------------------------------------+----------------------------------+---------------------+------------+---------------+------------------+--------+ - - $ kubectl get klb demo -o yaml - apiVersion: openstack.org/v1 - kind: KuryrLoadBalancer - metadata: - creationTimestamp: "2020-12-21T15:31:48Z" - finalizers: - - kuryr.openstack.org/kuryrloadbalancer-finalizers - generation: 7 - name: demo - namespace: default - resourceVersion: "714" - selfLink: /apis/openstack.org/v1/namespaces/default/kuryrloadbalancers/demo - uid: 3a97dfad-ad19-45da-8544-72d837ca704a - spec: - endpointSlices: - - endpoints: - - addresses: - - 10.0.1.116 - conditions: - ready: true - targetRef: - kind: Pod - name: demo-7dd477695c-fbq4r - namespace: default - resourceVersion: "592" - uid: 35d2b8ef-1f0b-4859-b6a2-f62e35418d22 - - addresses: - - 10.0.1.122 - conditions: - ready: true - targetRef: - kind: Pod - name: demo-7dd477695c-25s99 - namespace: default - resourceVersion: "524" - uid: 27437c01-488b-43cd-bba3-9a70c1778598 - ports: - - port: 8080 - protocol: TCP - ip: 10.0.0.140 - ports: - - port: 80 - protocol: TCP - targetPort: "8080" - project_id: 1ea4a08913d74aff8ed3e3bf31851236 - provider: amphora - security_groups_ids: - - 30cd7a25-3628-449c-992f-d23bdc4d1086 - - aaffa1a5-4b7e-4257-a444-1d39fb61ea22 - subnet_id: 3e043d77-c1b1-4374-acd5-a87a5f7a8c25 - type: ClusterIP - status: - listeners: - - id: 8aebeb5e-bccc-4519-8b68-07847c1b5b73 - loadbalancer_id: 994893a7-d67f-4af2-b2fe-5a03f03102b1 - name: default/demo:TCP:80 - port: 80 - project_id: 1ea4a08913d74aff8ed3e3bf31851236 - protocol: TCP - loadbalancer: - id: 994893a7-d67f-4af2-b2fe-5a03f03102b1 - ip: 10.0.0.140 - name: default/demo - port_id: 967688f5-55a7-4f84-a021-0fdf64152a8b - project_id: 1ea4a08913d74aff8ed3e3bf31851236 - provider: amphora - security_groups: - - 30cd7a25-3628-449c-992f-d23bdc4d1086 - - aaffa1a5-4b7e-4257-a444-1d39fb61ea22 - subnet_id: 3e043d77-c1b1-4374-acd5-a87a5f7a8c25 - members: - - id: 8aff18b1-1e5b-45df-ade1-44ed0e75ca5e - ip: 10.0.1.116 - name: default/demo-7dd477695c-fbq4r:8080 - pool_id: f5a61ce7-3e2f-4a33-bd1f-8f12b8d6a6aa - port: 8080 - project_id: 1ea4a08913d74aff8ed3e3bf31851236 - subnet_id: 3e043d77-c1b1-4374-acd5-a87a5f7a8c25 - - id: 2c2c7a54-ad38-4182-b34f-daec03ee0a9a - ip: 10.0.1.122 - name: default/demo-7dd477695c-25s99:8080 - pool_id: f5a61ce7-3e2f-4a33-bd1f-8f12b8d6a6aa - port: 8080 - project_id: 1ea4a08913d74aff8ed3e3bf31851236 - subnet_id: 3e043d77-c1b1-4374-acd5-a87a5f7a8c25 - pools: - - id: f5a61ce7-3e2f-4a33-bd1f-8f12b8d6a6aa - listener_id: 8aebeb5e-bccc-4519-8b68-07847c1b5b73 - loadbalancer_id: 994893a7-d67f-4af2-b2fe-5a03f03102b1 - name: default/demo:TCP:80 - project_id: 1ea4a08913d74aff8ed3e3bf31851236 - protocol: TCP - -We can see that both pods are included as members and that the demo cluster-ip -matches with the loadbalancer vip_address. Also we can see the loadbalancer CRD -after the load balancer was created. In order to check loadbalancing among them, -we are going to curl the cluster-ip from one of the pods and see that each of -the pods is replying at a time: - -.. code-block:: console - - $ kubectl exec -it demo-7dd477695c-25s99 -- /bin/sh - - sh-4.2$ curl 10.0.0.140 - demo-7dd477695c-fbq4r: HELLO, I AM ALIVE!!! - - - sh-4.2$ curl 10.0.0.140 - demo-7dd477695c-25s99: HELLO, I AM ALIVE!!! diff --git a/doc/source/installation/testing_nested_connectivity.rst b/doc/source/installation/testing_nested_connectivity.rst deleted file mode 100644 index 0f3022b3a..000000000 --- a/doc/source/installation/testing_nested_connectivity.rst +++ /dev/null @@ -1,57 +0,0 @@ -=================================== -Testing Nested Network Connectivity -=================================== - -Similarly to the baremetal testing, we can create a demo deployment, scale it -to any number of pods and expose the service to check if the deployment was -successful: - -.. code-block:: console - - $ kubectl create deployment demo --image=quay.io/kuryr/demo - $ kubectl scale deploy/demo --replicas=2 - $ kubectl expose deploy/demo --port=80 --target-port=8080 - -After a few seconds you can check that the pods are up and running and the -neutron subports have been created (and in ACTIVE status) at the undercloud: - -.. code-block:: console - - (OVERCLOUD) $ kubectl get pods - NAME READY STATUS RESTARTS AGE - demo-1575152709-4k19q 1/1 Running 0 2m - demo-1575152709-vmjwx 1/1 Running 0 12s - - (UNDERCLOUD) $ openstack port list | grep demo - | 1019bc07-fcdd-4c78-adbd-72a04dffd6ba | demo-1575152709-4k19q | fa:16:3e:b5:de:1f | ip_address='10.0.0.65', subnet_id='b98d40d1-57ac-4909-8db5-0bf0226719d8' | ACTIVE | - | 33c4d79f-4fde-4817-b672-a5ec026fa833 | demo-1575152709-vmjwx | fa:16:3e:32:58:38 | ip_address='10.0.0.70', subnet_id='b98d40d1-57ac-4909-8db5-0bf0226719d8' | ACTIVE | - -Then, we can check that the service has been created, as well as the -respective loadbalancer at the undercloud: - -.. code-block:: console - - (OVERCLOUD) $ kubectl get svc - NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE - svc/demo 10.0.0.171 80/TCP 1m - svc/kubernetes 10.0.0.129 443/TCP 45m - - (UNDERCLOUD) $ openstack loadbalancer list - +--------------------------------------+--------------------+----------------------------------+-------------+---------------------+----------+ - | id | name | tenant_id | vip_address | provisioning_status | provider | - +--------------------------------------+--------------------+----------------------------------+-------------+---------------------+----------+ - | a3b85089-1fbd-47e1-a697-bbdfd0fa19e3 | default/kubernetes | 672bc45aedfe4ec7b0e90959b1029e30 | 10.0.0.129 | ACTIVE | haproxy | - | e55b3f75-15dc-4bc5-b4f4-bce65fc15aa4 | default/demo | e4757688696641218fba0bac86ff7117 | 10.0.0.171 | ACTIVE | haproxy | - +--------------------------------------+--------------------+----------------------------------+-------------+---------------------+----------+ - - -Finally, you can log in into one of the containers and curl the service IP to -check that each time a different pod answer the request: - -.. code-block:: console - - $ kubectl exec -it demo-1575152709-4k19q -- /bin/sh - sh-4.2$ curl 10.0.0.171 - demo-1575152709-4k19q: HELLO, I AM ALIVE!!! - sh-4.2$ curl 10.0.0.771 - demo-1575152709-vmjwx: HELLO, I AM ALIVE!!! diff --git a/doc/source/installation/testing_sctp_services.rst b/doc/source/installation/testing_sctp_services.rst deleted file mode 100644 index 8ca072703..000000000 --- a/doc/source/installation/testing_sctp_services.rst +++ /dev/null @@ -1,209 +0,0 @@ -===================== -Testing SCTP Services -===================== - -In this example, we will use the `kuryr-sctp-demo`_ image. This image -implements a SCTP server that listens on port 9090, and responds to client -when a packet is received. - -We first create a deployment named sctp-demo using the deployment manifest -(deploy.yml) below: - -.. code-block:: yaml - - apiVersion: apps/v1 - kind: Deployment - metadata: - name: sctp-demo - labels: - app: server - spec: - replicas: 2 - selector: - matchLabels: - app: server - template: - metadata: - labels: - app: server - spec: - containers: - - name: sctp-demo - image: tabbie/kuryr-sctp-demo:v2.1 - ports: - - containerPort: 9090 - -.. code-block:: console - - $ kubectl apply -f deploy.yml - deployment.apps/sctp-demo created - -At this point we should have two pods running the `kuryr-sctp-demo`_ image: - -.. code-block:: console - - $ kubectl get pods - NAME READY STATUS RESTARTS AGE - sctp-demo-65fcf85ddb-8vnrq 1/1 Running 0 40s - sctp-demo-65fcf85ddb-zg7nq 1/1 Running 0 109s - -Next, we expose the deployment as a service, setting SCTP port to 90: - -.. note:: - - In order to successfully expose the deployment as a service, ensure that - the Octavia provider in use by Kuryr has SCTP support. - -.. code-block:: console - - $ kubectl get svc - NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE - kubernetes ClusterIP 10.0.0.129 443/TCP 36h - - $ kubectl expose deploy/sctp-demo --protocol=SCTP --port=90 --target-port=9090 - service/sctp-demo exposed - - $ kubectl get svc - NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE - kubernetes ClusterIP 10.0.0.129 443/TCP 36h - sctp-demo ClusterIP 10.0.0.158 90/SCTP 42s - -Now, let's check the OpenStack load balancer created by Kuryr for **sctp-demo** -service. - -.. code-block:: console - - $ openstack loadbalancer list - +--------------------------------------+--------------------+----------------------------------+-------------+---------------------+----------+ - | id | name | project_id | vip_address | provisioning_status | provider | - +--------------------------------------+--------------------+----------------------------------+-------------+---------------------+----------+ - | 4d219ac7-2592-4d33-8afa-12994c5d82ec | default/kubernetes | 2e89a9e0a50d42d1be8054a80530b836 | 10.0.0.129 | ACTIVE | amphora | - | 96b38be3-1183-41c5-a0db-d246ef1d07cb | default/sctp-demo | 2e89a9e0a50d42d1be8054a80530b836 | 10.0.0.158 | ACTIVE | amphora | - +--------------------------------------+--------------------+----------------------------------+-------------+---------------------+----------+ - - $ openstack loadbalancer show default/sctp-demo - +---------------------+--------------------------------------+ - | Field | Value | - +---------------------+--------------------------------------+ - | admin_state_up | True | - | availability_zone | None | - | created_at | 2021-01-11T10:01:15 | - | description | | - | flavor_id | None | - | id | 96b38be3-1183-41c5-a0db-d246ef1d07cb | - | listeners | eda5caa0-083a-4c45-a2e5-38c243b2c970 | - | name | default/sctp-demo | - | operating_status | ONLINE | - | pools | 0935f099-d901-4f39-8090-392a527cbc35 | - | project_id | 2e89a9e0a50d42d1be8054a80530b836 | - | provider | amphora | - | provisioning_status | ACTIVE | - | updated_at | 2021-01-11T10:05:30 | - | vip_address | 10.0.0.158 | - | vip_network_id | 13190422-869c-4259-ba3b-6a41be79a671 | - | vip_port_id | 64da8e72-8469-4ac6-a0e6-ec60ca02b96a | - | vip_qos_policy_id | None | - | vip_subnet_id | 0041469e-371c-417f-83df-94ca8f202eab | - +---------------------+--------------------------------------+ - -Checking the load balancer's details, we can see that the load balancer is -listening on SCTP port 90: - -.. code-block:: console - - $ openstack loadbalancer listener show eda5caa0-083a-4c45-a2e5-38c243b2c970 - +-----------------------------+--------------------------------------+ - | Field | Value | - +-----------------------------+--------------------------------------+ - | admin_state_up | True | - | connection_limit | -1 | - | created_at | 2021-01-11T10:04:31 | - | default_pool_id | 0935f099-d901-4f39-8090-392a527cbc35 | - | default_tls_container_ref | None | - | description | | - | id | eda5caa0-083a-4c45-a2e5-38c243b2c970 | - | insert_headers | None | - | l7policies | | - | loadbalancers | 96b38be3-1183-41c5-a0db-d246ef1d07cb | - | name | default/sctp-demo:SCTP:90 | - | operating_status | ONLINE | - | project_id | 2e89a9e0a50d42d1be8054a80530b836 | - | protocol | SCTP | - | protocol_port | 90 | - | provisioning_status | ACTIVE | - | sni_container_refs | [] | - | timeout_client_data | 50000 | - | timeout_member_connect | 5000 | - | timeout_member_data | 50000 | - | timeout_tcp_inspect | 0 | - | updated_at | 2021-01-11T10:05:30 | - | client_ca_tls_container_ref | None | - | client_authentication | NONE | - | client_crl_container_ref | None | - | allowed_cidrs | None | - | tls_ciphers | None | - | tls_versions | None | - | alpn_protocols | None | - +-----------------------------+--------------------------------------+ - -And the load balancer has a pool with two members listening on SCTP port 9090: - -.. code-block:: console - - $ openstack loadbalancer pool list - +--------------------------------------+---------------------------+----------------------------------+---------------------+----------+--------------+----------------+ - | id | name | project_id | provisioning_status | protocol | lb_algorithm | admin_state_up | - +--------------------------------------+---------------------------+----------------------------------+---------------------+----------+--------------+----------------+ - | c69a87a5-078e-4c2b-84d4-0a2691c58f07 | default/kubernetes:443 | 2e89a9e0a50d42d1be8054a80530b836 | ACTIVE | HTTPS | ROUND_ROBIN | True | - | 0935f099-d901-4f39-8090-392a527cbc35 | default/sctp-demo:SCTP:90 | 2e89a9e0a50d42d1be8054a80530b836 | ACTIVE | SCTP | ROUND_ROBIN | True | - +--------------------------------------+---------------------------+----------------------------------+---------------------+----------+--------------+----------------+ - - $ openstack loadbalancer member list default/sctp-demo:SCTP:90 - +--------------------------------------+-----------------------------------------+----------------------------------+---------------------+-----------+---------------+------------------+--------+ - | id | name | project_id | provisioning_status | address | protocol_port | operating_status | weight | - +--------------------------------------+-----------------------------------------+----------------------------------+---------------------+-----------+---------------+------------------+--------+ - | abeec334-56b1-4535-a238-71424d78590e | default/sctp-demo-65fcf85ddb-zg7nq:9090 | 2e89a9e0a50d42d1be8054a80530b836 | ACTIVE | 10.0.0.75 | 9090 | NO_MONITOR | 1 | - | 826345b0-1264-421d-b9e0-8756f7bc0d21 | default/sctp-demo-65fcf85ddb-8vnrq:9090 | 2e89a9e0a50d42d1be8054a80530b836 | ACTIVE | 10.0.0.88 | 9090 | NO_MONITOR | 1 | - +--------------------------------------+-----------------------------------------+----------------------------------+---------------------+-----------+---------------+------------------+--------+ - -At this point, we have both the kubernetes service and corresponding OpenStack -load balancer running, and we are ready to run the client application. - -For the client application we will use the `sctp_client`_ python script. The -SCTP client script sends SCTP message towards specific IP and port, and waits -for a response from the server. The client application communicates with the -server by leveraging OpenStack load balancer functionality. - -For the client application to work, python SCTP module needs to be installed -in our environment. We need a SCTP-aware kernel (most are). First we install -the following packages: libsctp-dev, libsctp1, lksctp-tools and then install -the module. - -.. code-block:: console - - $ sudo apt-get install libsctp-dev libsctp1 lksctp-tools - $ pip3 install pysctp - - -And we need the SCTP server service IP and port: - -.. code-block:: console - - $ kubectl get svc sctp-demo - NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE - sctp-demo ClusterIP 10.0.0.158 90/SCTP 67m - -Last step will be to connect to the SCTP server service: - -.. code-block:: console - - $ python3 sctp_client.py 10.0.0.158 90 - Sending Message - sctp-demo-65fcf85ddb-zg7nq: HELLO, I AM ALIVE!!! - - $ python3 sctp_client.py 10.0.0.158 90 - Sending Message - sctp-demo-65fcf85ddb-8vnrq: HELLO, I AM ALIVE!!! - -.. _kuryr-sctp-demo: https://hub.docker.com/repository/docker/tabbie/kuryr-sctp-demo -.. _sctp_client: https://github.com/openstack/kuryr-kubernetes/blob/master/contrib/sctp_client.py diff --git a/doc/source/installation/testing_udp_services.rst b/doc/source/installation/testing_udp_services.rst deleted file mode 100644 index 50b23b68b..000000000 --- a/doc/source/installation/testing_udp_services.rst +++ /dev/null @@ -1,177 +0,0 @@ -==================== -Testing UDP Services -==================== - -In this example, we will use the `kuryr-udp-demo`_ image. This image -implements a simple UDP server that listens on port 9090, and replies towards -client when a packet is received. - -We first create a deployment named demo: - -.. code-block:: console - - $ kubectl create deployment --image=yboaron/kuryr-udp-demo demo - deployment "demo" created - -As the next step, we will scale the deployment to 2 pods: - -.. code-block:: console - - $ kubectl scale deploy/demo --replicas=2 - deployment "demo" scaled - -At this point we should have two pods running the `kuryr-udp-demo`_ image: - -.. code-block:: console - - $ kubectl get pods - NAME READY STATUS RESTARTS AGE - demo-fbb89f54c-92ttl 1/1 Running 0 31s - demo-fbb89f54c-q9fq7 1/1 Running 0 1m - -Next, we expose the deployment as a service, setting UDP port to 90: - -.. code-block:: console - - $ kubectl get svc - NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE - kubernetes ClusterIP 10.0.0.129 443/TCP 17m - - $ kubectl expose deploy/demo --protocol UDP --port 90 --target-port 9090 - service "demo" exposed - - $ kubectl get svc - NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE - demo ClusterIP 10.0.0.150 90/UDP 16s - kubernetes ClusterIP 10.0.0.129 443/TCP 17m - -Now, let's check the OpenStack load balancer created by Kuryr for **demo** -service: - -.. code-block:: console - - $ openstack loadbalancer list - +--------------------------------------+--------------------+----------------------------------+-------------+---------------------+----------+ - | id | name | project_id | vip_address | provisioning_status | provider | - +--------------------------------------+--------------------+----------------------------------+-------------+---------------------+----------+ - | eb5123e8-6bb5-4680-ac64-dcf25c57ced3 | default/kubernetes | fdc9ac3b36474fbf8c7ab77f4f783ec5 | 10.0.0.129 | ACTIVE | amphora | - | 67f19a39-dfb9-4a7a-bafe-7d6789982d91 | default/demo | fdc9ac3b36474fbf8c7ab77f4f783ec5 | 10.0.0.150 | ACTIVE | amphora | - +--------------------------------------+--------------------+----------------------------------+-------------+---------------------+----------+ - - $ openstack loadbalancer show default/demo - +---------------------+--------------------------------------+ - | Field | Value | - +---------------------+--------------------------------------+ - | admin_state_up | True | - | created_at | 2018-10-09T06:06:14 | - | description | | - | flavor | | - | id | 67f19a39-dfb9-4a7a-bafe-7d6789982d91 | - | listeners | 7b374ecf-80c4-44be-a725-9b0c3fa2d0fa | - | name | default/demo | - | operating_status | ONLINE | - | pools | d549df5b-e008-49a6-8695-b6578441553e | - | project_id | fdc9ac3b36474fbf8c7ab77f4f783ec5 | - | provider | amphora | - | provisioning_status | ACTIVE | - | updated_at | 2018-10-09T06:07:53 | - | vip_address | 10.0.0.150 | - | vip_network_id | eee6af72-9fbb-48b5-8e52-9f8bdf61cbab | - | vip_port_id | ccd8be94-c65e-4bb2-afe7-44aa3d0617ea | - | vip_qos_policy_id | None | - | vip_subnet_id | 3376291d-6c23-48cb-b6c6-37cefd57f914 | - +---------------------+--------------------------------------+ - -Checking the load balancer's details, we can see that the load balancer is -listening on UDP port 90: - -.. code-block:: console - - $ openstack loadbalancer listener show 7b374ecf-80c4-44be-a725-9b0c3fa2d0fa - +---------------------------+--------------------------------------+ - | Field | Value | - +---------------------------+--------------------------------------+ - | admin_state_up | True | - | connection_limit | -1 | - | created_at | 2018-10-09T06:07:37 | - | default_pool_id | d549df5b-e008-49a6-8695-b6578441553e | - | default_tls_container_ref | None | - | description | | - | id | 7b374ecf-80c4-44be-a725-9b0c3fa2d0fa | - | insert_headers | None | - | l7policies | | - | loadbalancers | 67f19a39-dfb9-4a7a-bafe-7d6789982d91 | - | name | default/demo:UDP:90 | - | operating_status | ONLINE | - | project_id | fdc9ac3b36474fbf8c7ab77f4f783ec5 | - | protocol | UDP | - | protocol_port | 90 | - | provisioning_status | ACTIVE | - | sni_container_refs | [] | - | timeout_client_data | 50000 | - | timeout_member_connect | 5000 | - | timeout_member_data | 50000 | - | timeout_tcp_inspect | 0 | - | updated_at | 2018-10-09T06:07:53 | - +---------------------------+--------------------------------------+ - -And the load balancer has two members listening on UDP port 9090: - -.. code-block:: console - - $ openstack loadbalancer member list d549df5b-e008-49a6-8695-b6578441553e - +--------------------------------------+-----------------------------------+----------------------------------+---------------------+-----------+---------------+------------------+--------+ - | id | name | project_id | provisioning_status | address | protocol_port | operating_status | weight | - +--------------------------------------+-----------------------------------+----------------------------------+---------------------+-----------+---------------+------------------+--------+ - | b2c63e7b-47ed-4a6f-b8bb-acaa6742a0ad | default/demo-fbb89f54c-q9fq7:9090 | fdc9ac3b36474fbf8c7ab77f4f783ec5 | ACTIVE | 10.0.0.74 | 9090 | ONLINE | 1 | - | 7fa773b1-cf76-4a0b-8004-153423e59ef6 | default/demo-fbb89f54c-92ttl:9090 | fdc9ac3b36474fbf8c7ab77f4f783ec5 | ACTIVE | 10.0.0.88 | 9090 | ONLINE | 1 | - +--------------------------------------+-----------------------------------+----------------------------------+---------------------+-----------+---------------+------------------+--------+ - -At this point, we have both the kubernetes **demo** service and corresponding -openstack load balancer running, and we are ready to run the client -application. - -For the client application we will use the `udp-client`_ python script. The UDP -client script sends UDP message towards specific IP and port, and waits for a -response from the server. The way that the client application can communicate -with the server is by leveraging the Kubernetes service functionality. - -First we clone the client script: - -.. code-block:: console - - $ git clone https://github.com/yboaron/udp-client-script.git - Cloning into 'udp-client-script'... - remote: Enumerating objects: 15, done. - remote: Counting objects: 100% (15/15), done. - remote: Compressing objects: 100% (13/13), done. - remote: Total 15 (delta 4), reused 3 (delta 1), pack-reused 0 - Unpacking objects: 100% (15/15), done. - $ - -And we need the UDP server service IP and port: - -.. code-block:: console - - $ kubectl get svc demo - NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE - demo ClusterIP 10.0.0.150 90/UDP 20m - $ - -Last step will be to ping the UDP server service: - -.. code-block:: console - - $ python udp-client-script/client.py 10.0.0.150 90 - demo-fbb89f54c-92ttl: HELLO, I AM ALIVE!!! - - $ python udp-client-script/client.py 10.0.0.150 90 - demo-fbb89f54c-q9fq7: HELLO, I AM ALIVE!!! - -Since the `kuryr-udp-demo`_ application concatenates the pod's name to the -replyed message, it is plain to see that both service's pods are replying to -the requests from the client. - - -.. _kuryr-udp-demo: https://hub.docker.com/r/yboaron/kuryr-udp-demo/ -.. _udp-client: https://github.com/yboaron/udp-client-script diff --git a/doc/source/installation/trunk_ports.rst b/doc/source/installation/trunk_ports.rst deleted file mode 100644 index f69803991..000000000 --- a/doc/source/installation/trunk_ports.rst +++ /dev/null @@ -1,53 +0,0 @@ -========================= -Boot VM with a Trunk Port -========================= - -To create a VM that makes use of the Neutron Trunk port support, the next -steps can be followed: - -#. Use the demo tenant and create a key to be used to log in into the overcloud - VM: - - .. code-block:: console - - $ source ~/devstack/openrc demo - $ openstack keypair create demo > id_rsa_demo - $ chmod 600 id_rsa_demo - -#. Ensure the demo default security group allows ping and ssh access: - - .. code-block:: console - - $ openstack security group rule create --protocol icmp default - $ openstack security group rule create --protocol tcp --dst-port 22 default - -#. Download and import an image that allows vlans, as cirros does not support - it: - - .. code-block:: console - - $ wget http://cloud.centos.org/centos/7/images/CentOS-7-x86_64-GenericCloud.qcow2 - $ openstack image create --container-format bare --disk-format qcow2 --file CentOS-7-x86_64-GenericCloud.qcow2 centos7 - -#. Create a port for the overcloud VM and create the trunk with that port as - the parent port (untagged traffic): - - .. code-block:: console - - $ openstack port create --network private --security-group default port0 - $ openstack network trunk create --parent-port port0 trunk0 - -#. Create the overcloud VM and assign a floating ip to it to be able to log in - into it: - - .. code-block:: console - - $ openstack server create --image centos7 --flavor ds4G --nic port-id=port0 --key-name demo overcloud_vm - $ openstack floating ip create --port port0 public - - Note subports can be added to the trunk port, and be used inside the VM with - the specific vlan, 102 in the example, by doing: - - .. code-block:: console - - $ openstack network trunk set --subport port=subport0,segmentation-type=vlan,segmentation-id=102 trunk0 diff --git a/doc/source/installation/upgrades.rst b/doc/source/installation/upgrades.rst deleted file mode 100644 index 43a92b1fb..000000000 --- a/doc/source/installation/upgrades.rst +++ /dev/null @@ -1,91 +0,0 @@ -========================== -Upgrading kuryr-kubernetes -========================== - -Kuryr-Kubernetes supports standard OpenStack utility for checking upgrade is -possible and safe: - -.. code-block:: console - - $ kuryr-k8s-status upgrade check - +---------------------------------------+ - | Upgrade Check Results | - +---------------------------------------+ - | Check: Pod annotations | - | Result: Success | - | Details: All annotations are updated. | - +---------------------------------------+ - -If any issue will be found, the utility will give you explanation and possible -remediations. Also note that *Warning* results aren't blocking an upgrade, but -are worth investigating. - - -Stein (0.6.x) to T (0.7.x) upgrade ----------------------------------- - -In T we want to drop support for old format of Pod annotations (switch was -motivated by multi-vif support feature implemented in Rocky). To make sure that -you don't have unsupported Pod annotations you need to run ``kuryr-k8s-status -upgrade check`` utility **before upgrading Kuryr-Kubernetes services to T**. - -.. note:: - - In case of running Kuryr-Kubernetes containerized you can use ``kubectl - exec`` to run kuryr-k8s-status - - .. code-block:: console - - $ kubectl -n kube-system exec -it kuryr-k8s-status upgrade check - -.. code-block:: console - - $ kuryr-k8s-status upgrade check - +---------------------------------------+ - | Upgrade Check Results | - +---------------------------------------+ - | Check: Pod annotations | - | Result: Success | - | Details: All annotations are updated. | - +---------------------------------------+ - -In case of *Failure* result of *Pod annotations* check you should run -``kuryr-k8s-status upgrade update-annotations`` command and check again: - -.. code-block:: console - - $ kuryr-k8s-status upgrade check - +----------------------------------------------------------------------+ - | Upgrade Check Results | - +----------------------------------------------------------------------+ - | Check: Pod annotations | - | Result: Failure | - | Details: You have 3 Kuryr pod annotations in old format. You need to | - | run `kuryr-k8s-status upgrade update-annotations` | - | before proceeding with the upgrade. | - +----------------------------------------------------------------------+ - $ kuryr-k8s-status upgrade update-annotations - +-----------------------+--------+ - | Stat | Number | - +-----------------------+--------+ - | Updated annotations | 3 | - +-----------------------+--------+ - | Malformed annotations | 0 | - +-----------------------+--------+ - | Annotations left | 0 | - +-----------------------+--------+ - $ kuryr-k8s-status upgrade check - +---------------------------------------+ - | Upgrade Check Results | - +---------------------------------------+ - | Check: Pod annotations | - | Result: Success | - | Details: All annotations are updated. | - +---------------------------------------+ - -It's possible that some annotations were somehow malformed. That will generate -a warning that should be investigated, but isn't blocking upgrading to T -(it won't make things any worse). - -If in any case you need to rollback those changes, there is ``kuryr-k8s-status -upgrade downgrade-annotations`` command as well. diff --git a/doc/source/nested_vlan_mode.rst b/doc/source/nested_vlan_mode.rst deleted file mode 100644 index c32be36c9..000000000 --- a/doc/source/nested_vlan_mode.rst +++ /dev/null @@ -1,66 +0,0 @@ -================================= -Kuryr-Kubernetes nested VLAN mode -================================= - -Kuryr-Kubernetes can work in two basic modes - nested and standalone. The main -use case of the project, which is to support Kubernetes running on OpenStack -VMs is implemented with nested mode. The standalone mode is mostly used for -testing. - -This document describes nested VLAN mode. - - -Requirements -============ - -Nested VLAN mode requires Neutron to have `trunk` extension enabled, which adds -trunk port functionality to Neutron API. - - -Principle -========= - -This mode aims at use case of kuryr-kubernetes providing networking for a -Kubernetes cluster running in VMs on OpenStack. - -.. note:: - - A natural consideration here is running kuryr-kubernetes in containers on - that K8s cluster. For more see :ref:`containerized` section. - -The principle of nested VLAN is that Kuryr-Kubernetes will require that main -interface of the K8s worker VMs is a trunk port. Then each of the pods will -get a subport of that attached into its network namespace. - - -How to configure -================ - -You need to set several options in the kuryr.conf: - -.. code-block:: ini - - [binding] - default_driver = kuryr.lib.binding.drivers.vlan - # Name of the trunk port interface on VMs. If not provided Kuryr will try - # to autodetect it. - link_iface = ens3 - - [kubernetes] - pod_vif_driver = nested-vlan - vif_pool_driver = nested # If using port pools. - - [pod_vif_nested] - # ID of the subnet in which worker node VMs are running (if multiple join - # with a comma). - worker_nodes_subnets = - -Also if you want to run several Kubernetes cluster in one OpenStack tenant you -need to make sure Kuryr-Kubernetes instances are able to distinguish their own -resources from resources created by other instances. In order to do that you -need to configure Kuryr-Kubernetes to tag resources with unique ID: - -.. code-block:: ini - - [neutron_defaults] - resource_tags = diff --git a/doc/source/readme.rst b/doc/source/readme.rst deleted file mode 100644 index a6210d3d8..000000000 --- a/doc/source/readme.rst +++ /dev/null @@ -1 +0,0 @@ -.. include:: ../../README.rst diff --git a/doc/source/specs/pike/contrail_support.rst b/doc/source/specs/pike/contrail_support.rst deleted file mode 100644 index 082696ffa..000000000 --- a/doc/source/specs/pike/contrail_support.rst +++ /dev/null @@ -1,80 +0,0 @@ -========================================= -Kuryr Kubernetes OpenContrail Integration -========================================= - -https://blueprints.launchpad.net/kuryr-kubernetes/+spec/kuryr-k8s-contrail-integration - -This spec proposes how to integrate OpenContrail with Kuryr-Kubernetes. -OpenContrail is an open source project that provides network virtualization -functionality to OpenStack. Integrating these will allow Kuryr to be used to -bridge container-VM networking in a Contrail-based OpenStack deployment. - -Problem Description -=================== - -OpenContrail is one of the largest SDN platforms, but it currently does not -work with Kuryr-Kubernetes. The goal of this blueprint is to provide Kuryr with -the correct driver so that a Kubernetes-hosted container can use -Kuryr-Kubernetes to correctly interface with an OpenContrail-based network. In -this configuration, OpenContrail will take place of the Open Virtual Switch, -L2/L3 functionality, etc. that normally comes with using Neutron as the default -implementation. - -Use Cases ---------- - -Kuryr will act as the container networking interface for OpenContrail. This -patch set will allow a bare-metal, Kubernetes-hosted container to interact with -VMs in an OpenStack virtual network. This means we have to have a way to plug, -unplug and bridge the container. - -Use Case 1: Enable container based work loads to communicate with OpenStack -hosted VM workloads in Contrail SDN environments - -Use Case 2: Allow Kubernetes workloads to leverage advanced OpenContrail based -networking - -Use Case 3: Enable Kubernetes to create virtual networks via Contrail - -Proposed Change -=============== -This change will add a driver to Kuryr-Kubernetes that has all of the -functionality of the CNI specifically for OpenContrail. The driver will feature -the plug() and unplug() commands that grant the container network access. - -Community Impact ----------------- - -This spec invites the community to collaborate on a unified solution to support -contrail integration within Kuryr-Kubernetes. - -Implementation -============== - -Assignee(s) ------------ - -Darla Ahlert -Steve Kipp - -Work Items ----------- - -1. Implement an os-vif bare bones plugin similar to [1] only worrying about -plug and unplug. We will implement this within Kuryr-Kubernetes for now and -eventually merge this to openstack/os-vif. -2. Look into serialization for OpenContrail and use [2] as a reference, -if needed. -3. Look into binding for OpenContrail similar to OVS binding [3] -4. Implement unit tests for added code -5. Add gate to install OpenContrail components - -Added Paths for New Code: - kuryr-kubernetes/cni/os-vif/opencontrail.py - -References -========== - -[1] https://github.com/openstack/os-vif/blob/master/vif_plug_ovs/ovs.py -[2] https://github.com/openstack/kuryr-kubernetes/blob/794ec706c5fbe0da6e49bf20ba2439d8eb39ae7e/kuryr_kubernetes/os_vif_util.py#L258-L281 -[3] https://github.com/openstack/kuryr-kubernetes/blob/794ec706c5fbe0da6e49bf20ba2439d8eb39ae7e/kuryr_kubernetes/cni/binding/bridge.py diff --git a/doc/source/specs/pike/fuxi_kubernetes.rst b/doc/source/specs/pike/fuxi_kubernetes.rst deleted file mode 100644 index 887201a62..000000000 --- a/doc/source/specs/pike/fuxi_kubernetes.rst +++ /dev/null @@ -1,176 +0,0 @@ -.. - This work is licensed under a Creative Commons Attribution 3.0 Unported - License. - - http://creativecommons.org/licenses/by/3.0/legalcode - -=========================== -Fuxi Kubernetes Integration -=========================== - -https://blueprints.launchpad.net/kuryr-kubernetes/+spec/fuxi-kubernetes - -This spec proposes an approach to integrate Kubernetes with various OpenStack -storage services, such as Cinder, Manila. - -Kubernetes is a platform for automating deployment, scaling and operations of -application containers across clusters of hosts. Kubernetes currently supports -Cinder plugin that manages Cinder volumes and makes them available for pods. -In addition, there are a number of third-party implementations of Kubernetes -volume plugins (i.e. Flocker, REX-Ray) that allows Kubernetes to manage volumes -backed by various cloud storage providers including OpenStack Cinder. All these -solutions are mainly for overcloud use cases, in which pods are deployed on -a set of cloud instances. In production, there is another class of use cases, -that were referred as the undercloud use cases, in which Kubernetes are -deployed as a control plane service and pods are running on servers other -than cloud instances (i.e. baremetal servers). - -This spec proposed a solution that will address both overcloud and undercloud -use cases. The specific requirements are as follows: - -- Integrate Kubernetes with various OpenStack storage services, such as Cinder - and Manila. -- Support various backends that are supported by the OpenStack storage - services. -- Support various volumes operations including but not limiting to provision, - de-provision, attach, de-attach, mount, un-mount, resize, snapshot, backup - and restore. -- Support Kubernetes Persistent Volume (PV) and Persistent Volume Claim (PVC) - [1]. -- Whenever possible, reuse existing services or frameworks. For example, - Kuryr-kubernetes has the framework to watch the Kubernetes API and handle - changes of resources. Fuxi has implemented the logic of interfacing with - various OpenStack storage services and their backends. This proposal - suggested to reuse Kuryr-kubernetes and Fuxi instead of re-inventing the - equivalent functionalities. -- Pluggable architecture. Allows integrating with custom storage solutions - via plugins. -- Massively scalable. Support large scale Kubernetes deployment (i.e. - hundreds of nodes) with massive workloads (i.e. thousands of containers). - -Problem Description -=================== -There are several ways to integrate Kubernetes with OpenStack. If Kubernetes -is hosted by the cloud servers provided by Nova, users can leverage the -Kubernetes cloud provider feature to provision and attach Cinder volumes to -hosts. However, if Kubernetes is hosted by servers other than Nova instances, -there is no perfect solution that connects Kubernetes to various OpenStack -storage services. - - -Proposed Change -=============== -In order to integrate Kubernetes with OpenStack and satisfy the requirements -above, this spec proposes to develop two components: A volume provisioner and -a FlexVolume driver. The volume provisioner runs on the Kubernetes control -plane and is responsible to watch for Kubernetes API for changes of PVCs and -provision PVs for PVCs. The FlexVolume driver will reside on each host that -runs Kubelet and it will be called out by Kubelet to perform local operations, -such as attach volumes to hosts, etc.. Both volume provisioner and FlexVolume -driver will consume OpenStack storage services via Fuxi server. - -.. image:: ../../../images/fuxi_k8s_components.png - :alt: integration components - :align: center - :width: 100% - - -Volume Provisioner ------------------- -Volume provisioner is responsible for watching Kubernetes API for PVCs and -make sure the corresponding storage assets (i.e. cinder volume) are -provisioned, updated, or deleted in OpenStack. The volume provisioner will -implement the 'ResourceEventHandler' interface of Kuryr-kubernetes for -handling PVC events. - -For each creation of PVC in Kubernetes, the Kuryr-kubernetes's API watcher will -trigger an event that will be eventually handled by volume provisioner. -On receiving the event, the volume provisioner will provision the appropriate -storage asset in OpenStack and create a PV in Kubernetes to represent the provisioned -storage asset. The volume provisioning workflow will be in compliance with -the Kubernetes's out-of-tree provisioning specification [2]. The provisioned -PV will be populated with necessary information for the volume driver to -connect to the provisioned storage asset later. - -The volume provisioner will call the REST API of fuxi server to do the actual -provisioning, and fuxi server will in term provision storage assets by using a -volume provider (i.e. cinder provider). Note that fuxi was originally designed -to be a remote docker volume plugin, and this proposal proposes to reuse it -for fuxi Kubernetes. - -Similarly, for each update or deletion of PVC, the volume provisioner will -call fuxi server to update or delete the corresponding storage assets at -OpenStack and PVs at Kubernetes. - - -FlexVolume Driver ------------------ -FlexVolume [3] is a Kubernetes volume plugin that allows vendor to write their own -driver to support custom storage solutions. This spec proposes to implement -a FlexVolume driver that enables Kubelet to consume the provisioned storage -assets. The FlexVolume driver will implement the FlexVolume's driver interface -that is consistent of a set of 'call-outs'. - -After the PVs are provisioned by the volume provisioner, they will be picked by -Kubelet and Kubelet will assign the PVs to a volume plugin based on its type. -In our case, all PVs provisioned by our volume provisioner will be set to -'flexVolume' type so FlexVolume will be invoked to handle these PVs. -FlexVolume will parse the PVs to retrieve information and pass down those -information to our FlexVolume driver via 'call-outs'. - -Generally speaking, PVs will serve as medium for the volume provisioner to -communicate with the FlexVolume driver. The volume provisioner is supposed -to populate PVs with all the data that will be consumed by the FlexVolume -driver later. For example, the volume provisioner might provision a Cinder -volume and set the Cinder volume's name in a field of the created PV, -so that the name can be passed down to the FlexVolume driver who will consume -the Cinder volume. - -The FlexVolume driver will leverage fuxi to do the actual processing (i.e. -connect to the volume). The initial implementation will assume an instance of -fuxi server is deployed to each host that run Kubelet/FlexVolume driver so that -the fuxi server and the FlexVolume driver can communicate via localhost. -In the second phrase, we will investigate the possibility to have a centralized -fuxi server which both volume provisioner and FlexVolume driver will consume. -This might require splitting fuxi into a server and a library. The library will -be leveraged by the FlexVolume driver to perform local operations (i.e. volume -bi-mounting) and the server will serve cluster-wide requests. - -Note that FlexVolume has several known drawbacks. For example, it invokes -drivers via shells, which requires executables pre-installed in the specified -path. This deployment model doesn't work with operating systems like CoreOS -in which the root file system is immutable. This proposal suggests to continue -monitoring the evolution of Kubernetes and switch to a better solution if there is -one showed up. - - -Alternatives -============ -An alternative to FlexVolume driver is provide an implementation of Kubernetes volume -plugin. An obstacle of this approach is that Kubernetes doesn't support out-of-tree -volume plugin (beside using FlexVolume) right now. Therefore, the fuxi volume -plugin needs to be reside in Kubernetes tree and released with a different schedule -from OpenStack. - - -Implementation -============== - -Assignee(s) ------------ - -Primary assignee: -Hongbin Lu - - -Work Items ----------- -1. Implement a Kubernetes volume provisioner. -2. Implement a Kubernetes FlexVolume driver. - - -References -========== -[1] https://kubernetes.io/docs/concepts/storage/persistent-volumes/ -[2] https://github.com/kubernetes/community/blob/master/contributors/design-proposals/volume-provisioning.md -[3] https://github.com/kubernetes/community/blob/master/contributors/devel/flexvolume.md diff --git a/doc/source/specs/queens/network_policy.rst b/doc/source/specs/queens/network_policy.rst deleted file mode 100644 index e90815039..000000000 --- a/doc/source/specs/queens/network_policy.rst +++ /dev/null @@ -1,613 +0,0 @@ -======================= -Network policy support -======================= - -By default all Kubernetes pods are non-isolated and they accept traffic -from any source. "Network policy" is Kubernetes specification that defines how -groups of pods are allowed to communicate with each other and other network -endpoints [1]_. - -This Spec suggests a design for supporting Kubernetes "Network policy" in -Kuryr. - - -Problem Description -=================== - -Kubernetes "Network policies" define which traffic is allowed to be -sent or received by group of pods. - -Each network policy has 3 main parts [2]_: - -* Pod selector: Use kubernetes "label selector" [5]_ that defines on which - pods this policy should be applied. The relationship between pod and policy - is N to M. - Each pod can be selected by multiple network-policies (when an OR operator - is applied between the policies), and each policy can be attached to - multiple pods. -* Ingress section: defines which traffic can be received by selected pods. - It's defined by a "Cartesian product" of (allowed peers) and (protocol). - - * There are 3 ways to define Allowed peer: - - * Ip-address-block: allowed CIDR (also enable to exclude an inner CIDR). - * Pod selector: allowed set of pods defined by "label selector" [5]_. - * Namespace selector: list of namespaces; all pods that belong to that - namespaces are defined as allowed-peers. - - * Port is defined by 2 fields: - - * L4 protocol (TCP/UDP/ICMP..) - * L4 destination port - -* Egress section: defines which traffic can be sent from the selected pods. - This is defined in the same way as the ingress section. - -In order to support network-policy, kuryr-kubernetes should handle all the -events that are related to the network-policies and translate them into -Neutron objects for apply an equivalent network-topology to the one defined by -the Kubernetes policies. Neutron doesn't have a security API that is equivalent -to the kubernetes-network-policy. The translation should be done carefully -in order to achieve eventually consistent required topology, and avoid -corner-cases and race conditions. - -Proposed solution -================= - -This spec suggests to implement Kubernetes network-policy by leveraging Neutron -security-groups [4]_. -There is some similarity between security-groups and network-policy, but there -are also some definitions inside the network-policy that require some more -complex translation work. - -In order to provide a full translation between kubernetes-policies to security -groups, there are 3 main issues that need to be consider: - -* Translation of the kubernetes-policy to Neutron security-group object. - -* Attaching the security-group to the relevant-ports according to - the policy pod-selector. - -* Response to changes in the group of pods that selected by pod selectors and - namespace selector: - - * when pod is created and matches the queries - * when pod is updated with new label, and that label matches to the query - -The next paragraphs describe the implementation proposal for each of the tasks -described above, including new Handler and Drivers, that should be added to -the Kuryr controller. - - -Translate Kubernetes policy to Neutron security-group ------------------------------------------------------ - -'Allow all' policy [3]_ -####################### -Network policy that allows all traffic, should be translated to -Security group with one rule that allows all traffic. - -Example for allow all egress traffic: - -.. code-block:: json - - { - "security_group_rule": { - "direction": "egress", - "protocol": null, - "security_group_id": "[id]" - } - } - -'Deny all' policy [6]_ -###################### - -Translate to an empty-security-group. - -Ingress/egress -############## - -Can be translated to security-group-rules ingress/egress traits. - -IpBlock: -######## - -Can be done by "remote ip prefix" trait in security-group-rule as both -use CIDRs. In case of Exceptions (It's an inner CIDR's of the ipBlock, that -should be excluded from the rule), the ip-range should be broken into pieces -that cover the all ip-block without the exception. -For example, if there is Ip-block :"1.1.1.0/24 except 1.1.1.0/26", Kuryr should -create security-groups-rules with 1.1.1.128/25 an 1.1.1.64/26). - -podSelectors -############ - -Pod selector uses kubernetes label-selectors [6]_ for choosing set of pods. -It is used in the policy for 2 purposes: - -* Define on which pods the policy should be applied. - -* Allow ingress/egress traffic from/to this set of pods. - -The first one defines on which ports the policy should be applied, so it will -be discussed in the next section. For the second, the translation mechanism can -use the security-group trait - "remote_policy_group", that allows to define as -valid source all ports that belong to another security-group. This means that -we could create security-group with no rules for each network-policy selector -and attach all ports corresponding to pods that selected by the pod query -to this security-group. -We assume that each port attached to this security-group will be attached to -at least one other group (default security-group), so that attachment will not -entirely block traffic to the port. - -namespaceSelector -################# - -Namespace selector is used for choosing all the pods that belong to the -namespaces that selected by the query for allowing ingress/egress traffic. -Should use the same security-group as the pod selector for allowing ingress/egress -traffic from the selected namespaces. - -Port, protocol -############## - -A port can be defined as a number or a name. When defined as a number, it's directly -translated to port on a protocol in security group rule. However, when defined with -a name, the container pods' name needs to be verified, and in case of matching the -named port it's translate to a security group rule with the port number of -the named port on a determined protocol. - -The choice of which pods to select to check the containers, depends on the direction -of the rule being applied. In case of a ingress rule, all the pods selected by -NetworkPolicySpec's podSelector are verified, in other words, the pods which the -Network Policy is applied. For a egress rule, the pods selected -by the NetworkPolicyEgressRule's selector are verified. - -To keep track of the pods that have container(s) matching a named port, -a new field, 'remote_ip_prefixes', needs to be added to the security group rule of the -KuryrNetPolicy CRD, containing the IP and the namespace of the affected resources. -This way, the process of creating, deleting or updating a security group rule -on pod events is facilitated. - -Lets assume the following pod and network policy are created -(based on Kubernetes Upstream e2e tests [11]_): - - .. code-block:: yaml - - apiVersion: v1 - kind: Pod - metadata: - name: server - labels: - pod-name: server - spec: - containers: - - env: - - name: SERVE_PORT_80 - value: foo - image: gcr.io/kubernetes-e2e-test-images/porter:1.0 - imagePullPolicy: IfNotPresent - name: server-container-80 - ports: - - containerPort: 80 - name: serve-80 - protocol: TCP - - env: - - name: SERVE_PORT_81 - value: foo - image: gcr.io/kubernetes-e2e-test-images/porter:1.0 - imagePullPolicy: IfNotPresent - name: server-container-81 - ports: - - containerPort: 81 - name: serve-81 - protocol: TCP - - --- - - apiVersion: networking.k8s.io/v1 - kind: NetworkPolicy - metadata: - name: allow-client-a-via-named-port-ingress-rule - namespace: default - spec: - podSelector: - matchLabels: - pod-name: server - policyTypes: - - Ingress - ingress: - - ports: - - protocol: TCP - port: serve-80 - -The following Custom Resources Definition is generated containing -all the Neutron resources created to ensure the policy is enforced. -Note that a 'remote_ip_prefixes' is added to keep track of the pod -that matched the named port. - - .. code-block:: yaml - - apiVersion: openstack.org/v1 - kind: KuryrNetPolicy - metadata: - annotations: - networkpolicy_name: allow-client-a-via-named-port-ingress-rule - networkpolicy_namespace: default - networkpolicy_uid: 65d54bbb-70d5-11e9-9986-fa163e6aa097 - creationTimestamp: "2019-05-07T14:35:46Z" - generation: 2 - name: np-allow-client-a-via-named-port-ingress-rule - namespace: default - resourceVersion: "66522" - uid: 66eee462-70d5-11e9-9986-fa163e6aa097 - spec: - egressSgRules: - - security_group_rule: - description: Kuryr-Kubernetes NetPolicy SG rule - direction: egress - ethertype: IPv4 - id: e19eefd9-c543-44b8-b933-4a82f0c300b9 - port_range_max: 65535 - port_range_min: 1 - protocol: tcp - security_group_id: f4b881ae-ce8f-4587-84ef-9d2867d00aec - ingressSgRules: - - remote_ip_prefixes: - "10.0.0.231:": default - security_group_rule: - description: Kuryr-Kubernetes NetPolicy SG rule - direction: ingress - ethertype: IPv4 - id: f61ab507-cf8c-4720-9a70-c83505bc430f - port_range_max: 80 - port_range_min: 80 - protocol: tcp - security_group_id: f4b881ae-ce8f-4587-84ef-9d2867d00aec - networkpolicy_spec: - ingress: - - ports: - - port: serve-80 - protocol: TCP - podSelector: - matchLabels: - pod-name: server - policyTypes: - - Ingress - podSelector: - matchLabels: - pod-name: server - securityGroupId: f4b881ae-ce8f-4587-84ef-9d2867d00aec - securityGroupName: sg-allow-client-a-via-named-port-ingress-rule - -Mix of ports and peer -##################### - -In this case security-group-rule should be created for each tuple of -peer and ports. Number of rules will be a Cartesian product of ports and -peers. - - -Match between security-groups and its ports -------------------------------------------- - -A security-group that derived from kubernetes-policy should be -tagged [7]_ with network_policy UID. In case of issue of length or characters-set -hashing should be applied, so security-group unique tag could be derived from the -policy-UID. - -For defining on which pods the policy should be applied Kubernetes defines a -pod-selector query [5]_. For applying the policy on the relevant ports, Kuryr -needs to know at any given moment which pods belong to that group. It can -happen when pod is created/updated/change-label. - -When policy is created, Kuryr should trigger a get query for applying the -policy on all pods that already match, -and add a watch for getting an update when POD added or removed from -network-policy and apply/remove the translated policy on the pod's port. - -For applying the policy on the pod, an annotation with the security-group-id -will be added to the pod. That will cause the "update pod" event. -The VIFHandler via security-group Driver will attach the pod to the port. - -We can't attach the security-group directly in the watch callback as it -will create a race condition between the watch and the VIFHandler as -the watch could be called before Kuryr notified that the pod is created. -With the annotation - when new pod is created, if the watch was called -before VIFHandler pod creation processing, VIFHandler will get the pod already -with the annotation. Otherwise, it will get pod with no -security-group-annotation and will attach it to the default security-group. -When the watch will update the annotation, the pod will be updated with the -correct security-group. - -When policy is updated, if policy pod-selectors changed, -a diff between the old and new selected-pod-set should be done, -and the pods security-groups annotations should be updated respectively. -Selector watches should be updated with the new queries. - - -Allow traffic from the pod ingress and egress selectors: --------------------------------------------------------- - -As mentioned above, "remote_group_id" will be used to allow ingress and -egress traffic from pods selected by the pod/namespace selectors. - -For the pod-selector and namespace-selector we need to -create a security-group per policy (one for ingress and one for egress). -The security-group should be tagged with tag that is derived from the -policy-UID and traffic direction (for example: [policy_UID]_EG for egress traffic). -In case of characters-sets or allowed-length issues, hash should be applied for -updating these security-groups. - -For each selector (namespace or pod) a watch should be set. The watch callback -will add/remove the relevant pods to/from the security-group. - -Controller Handlers and Drivers impact: ---------------------------------------- - -For supporting Network-policy Handler that watches network_policy events -will be added. - -Two new drivers will be added: - -* On the network_policy Handler: - - * "Network Policy Apply Driver", it will have 3 responsibilities: - - * Translate the network-policy to security-groups. - * Match the security-group to its relevant port - (by setting the watch and annotating the pod as described above). - * Update the security-groups for ingress/egress POD/namespace selector. - -* On VIF handler: - - * A security-group-policy Driver will be used instead of the default - security groups Driver. It will be responsible for: - - * Set the port security-group according to the annotation. - - -Controller startup ------------------- -The following should be done on Controller startup: - -* Retrieve from Kubernetes API all network-policies and set all the - relevant watches. This should happen before the Controller starts. - -* Need to do some sync operation to make-sure Neutron topology is synchronised - with Kubernetes Network Policy model. - - * for every network-policy: - - * Get it's main security-group and check if it's updated. This validation - will be done by the generation tag. Generation is part of k8s metadata - and increased by one on every policy change. When the policy will - applied by kuryr, it's should be annotated with current policy generation. - If the generation in the policy meta-data is newer than - the generation in the annotation, it's mean that the policy had been - changed and the security-group rules needs to rebuild. - - * for each pod selector in the policy: - - * get from kubernetes-api all pods that selected by this query. - * get all ports of the relevant security-groups. - * Do diff between port that needed to be attached to SG, - and add/remove pod-ports from security-groups. - - -Port pool impact ----------------- - -Changes in the security-policy can cause negative impact on the port-pools [9]_. -The combination of the security-groups of port is part of the pool key, and -changes in network-policy could make some pools not relevant any more. - -For example let's assume that we have 2 policies "a" and "b", and both policies -should be applied on pods with the label "role: db". When the first pod with -label "role: db" is created - a new port-pool is created and its pool key is -composed from the security-groups of "a" and "b". If policy "b" will be changed -and pods with label "role: db" would not be selected by the policy anymore, -then the port-pool that was created for the combination of "a" and "b" will not -be not useful any more. - -That can lead to the ports leak, as pool holds many ports that not useful -anymore. For handling this issue a new cleanup task should be added. This task -will release all ports from the pools that are not in use anymore. - -Another issue that needs to be treated is that the policies of pod can be -changed while pod is running. Currently when pod is deleted its' port is -returned to the pool that it was taken from. But if the pod's policies are -changed, this behaviour is incorrect. When port is released it should be -returned to the pool that matches to the current state of the pod. - - -Execution flow diagram ----------------------- - -See below the network policy attachment to the pod after pod creation: - -.. image:: ../../../images/net-policy.svg - :alt: Ingress creation flow diagram - :align: left - :width: 100% - - -Possible optimization: ----------------------- - -Kubernetes label-selector divided into 2 types of queries "match-labels", -and "match-expression" [10]_. "match-labels" selects a closed list of labels -while "match-expression" selects all pods that match to particular expression. - -This spec suggests to create a watch for each label-selector query, because in -"match-expression" queries it is not possible to determine if pod matches the -query, without implementing parser for each expression. By setting a watch we -are using kubernetes-api-server for the matching between pods and queries. - -The spec treats the "match-labels" and "match-expression" queries in the same -way for simplicity reasons. But future optimization may distinguish between -queries-types. "match-labels" queries watches may be removed and the matching -between pod to its' "match-labels" queries could be done directly -on the vif-handler. - - -Assumptions ------------ - -Security-groups are supported by the networking backend for all vif interfaces. -In case of special interfaces (SR-IOV, mac-vlan, etc ..), the network-policy -will be applied on the interface if and only if then networking backend -enables security-groups on those interfaces. - - -Execution flow-example ----------------------- - -This section describes system execution flow in the following scenarios: - -* POD is deployed on empty system. -* Network-policy that should be applied on the first pod is deployed. -* Another pod that belongs to the network-policy is deployed. - -Pod is deployed on empty system: - -* Pod is created with the following details: - - * name: p1, namespace: default, labels : {Role:db}. - -* VIF Handler: - - * Security-group-policy driver: - - * Assign default-policy to this pod (as we still - do not have any network-policy in the system). - * Create security-group for namespace 'default', - and add pod p1 port to that security-group. - -Network policy is deployed: - -Let's assume that following policy is created (taken from k8s tutorial [8]_): - - .. code-block:: yaml - - apiVersion: networking.k8s.io/v1 - kind: NetworkPolicy - metadata: - name: test-network-policy - namespace: default - spec: - podSelector: - matchLabels: - role: db - policyTypes: - - Ingress - - Egress - ingress: - - from: - - ipBlock: - cidr: 172.17.0.0/16 - except: - - 172.17.1.0/24 - - namespaceSelector: - matchLabels: - project: myproject - - podSelector: - matchLabels: - role: frontend - ports: - - protocol: TCP - port: 6379 - egress: - - to: - - ipBlock: - cidr: 10.0.0.0/24 - ports: - - protocol: TCP - port: 5978 - - -* Network policy Handler: - - * Network policy Driver: - - * Create security group with the following rules: - - * Ingress , tcp:6379 172.17.0.0/24 - * Ingress , tcp:6379 172.17.2.0/23 - * Ingress , tcp:6379 172.17.4.0.0/22 - * Ingress , tcp:6379 172.17.8.0/21 - * Ingress , tcp:6379 172.17.16.0/20 - * Ingress , tcp:6379 172.17.32.0/19 - * Ingress , tcp:6379 172.17.64.0/18 - * Ingress , tcp:6379 172.17.128.0/17 - * Ingress , tcp: 6379 , remote_group_id : [test-network-policy-uid]_in - * Egress, tcp:5978 10.0.0.0/24 - - * Create match for the policy: - - * Queries k8s-api about pods that match to {role:db} - - * Attach the annotation with security-policy-id to p1. - - * Set a watch on the query - "Match-label : {role:db}" , the watch - callback of this watch will update the security-group annotation on - the updated/new pods that are selected by this query. - - * Create a match for the ingress/egress group: - - * Set watch on the query : "match-labels {role:frontend}" , watch - callback will add all pods that are selected by this query to the - security-group [test-network-policy-uid]_in. - -* VIF Handler: - - * Will get update event on p1 as its' annotations is changed. - - * security-policy-group-driver: - - * Attach the interface to its security-group. - -Second pod is created: - -* pod is created with the details: - - * name: p2 , namespace: default , labels : {Role:db}. - -* Let's assume that VIF handler is called before watch-callback (as this case - is little more complicated). - -* VIF Handler: - - * Pod created event (still no namespace sg annotation on the pod). - - * Namespace security-group driver - - * Return the default network-policy. - - * Pod is created with default-policy. - -* Watch Callback: - - * p2 is selected by security-group net-policy-test, annotates the pod - with security-group-id that matches to the policy. - -* VIF Handler: - - * security-group policy driver - - * Update pod P2 port with network policy driver. - - -References -========== -.. [1] https://kubernetes.io/docs/concepts/services-networking/network-policies/ -.. [2] https://kubernetes.io/docs/api-reference/v1.8/#networkpolicy-v1-networking/ -.. [3] https://kubernetes.io/docs/concepts/services-networking/network-policies/#default-allow-all-ingress-traffic -.. [4] https://developer.openstack.org/api-ref/network/v2/index.html#security-group-rules-security-group-rules -.. [5] https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ -.. [6] https://kubernetes.io/docs/concepts/services-networking/network-policies/#default-deny-all-ingress-traffic -.. [7] https://docs.openstack.org/neutron/latest/contributor/internals/tag.html -.. [8] https://kubernetes.io/docs/concepts/services-networking/network-policies/#the-networkpolicy-resource -.. [9] https://github.com/openstack/kuryr-kubernetes/blob/master/doc/source/devref/port_manager.rst -.. [10] https://v1-8.docs.kubernetes.io/docs/api-reference/v1.8/#labelselector-v1-meta -.. [11] https://github.com/kubernetes/kubernetes/blob/master/test/e2e/network/network_policy.go diff --git a/doc/source/specs/rocky/npwg_spec_support.rst b/doc/source/specs/rocky/npwg_spec_support.rst deleted file mode 100644 index 48ec4df13..000000000 --- a/doc/source/specs/rocky/npwg_spec_support.rst +++ /dev/null @@ -1,190 +0,0 @@ -========================================================================== -Kubernetes Network Custom Resource Definition De-facto Standard v1 Support -========================================================================== - -https://blueprints.launchpad.net/kuryr-kubernetes/+spec/kuryr-npwg-spec-support - -This spec proposes an approach to support the mechanism defined in Kubernetes -Network Custom Resource Definition De-facto Standard Version 1 [#]_, which is -used to attach multiple VIFs to Pods. - -Problem Description -------------------- - -There is always a desire for Pods to be able to be attached to multiple -interfaces in NFV use cases. However CNI plugins which have implemented this -functionality are using different way of defining the additional interfaces in -Pods. There is no standard approach among those CNI plugins. - -Therefore, the Networking Plumbing Working Group [#]_ drafted a spec (the NPWG -spec) trying to standardize the way of attaching Pods to multiple networks. - -Proposed Change ---------------- - -The NPWG spec defines a "Network" Custom Resource object which describes how to -attach a Pod to the logical or physical network referenced by the object. - -The proposed change is based on VIF-Handler And Vif Drivers Design [#]_. A new -VIF driver 'npwg_multiple_interfaces' will be created to parse the annotation -of Pods and Network CRDs. The new VIF driver will be invoked by the multi-vif -driver as another sub-drivers. It should return a list of VIF objects. The -'npwg_multiple_interfaces' should invoke other VIF driver to create the vif -objects if it is necessary. - -The VIFHandler then updates the Pod annotation of 'openstack.org/kuryr-vif' -with the VIF objects. So that the Kuryr CNI can read these VIFs, and attaches -each of them to Pods namespace. If any of the additional interfaces failed to -be attached to the Pod, or any error happens during attachment, the CNI shall -return with error. - -Option in config file might look like this: - -.. code-block:: ini - - [kubernetes] - - enabled_vif_drivers = npwg_multiple_interfaces - -To define additional network in Pods, NPWG spec defines format of annotation. -Here's how a Pod Spec with additional networks requests might look like: - -.. code-block:: yaml - - kind: Pod - metadata: - name: my-pod - namespace: my-namespace - annotations: - k8s.v1.cni.cncf.io/networks: net-a,net-b,other-ns/net-c - -Or in JSON format like: - -.. code-block:: yaml - - kind: Pod - metadata: - name: my-pod - namespace: my-namespace - annotations: - k8s.v1.cni.cncf.io/networks: | - [ - {"name":"net-a"}, - {"name":"net-b"}, - { - "name":"net-c", - "namespace":"other-ns" - } - ] - -Then the VIF driver can parse the network information defined in 'Network' -objects. In NPWG spec, the 'NetworkAttachmentDefinition' object definition is -very flexible. Implementations that are not CNI delegating plugins can add -annotations to the Network object and use those to store non-CNI configuration. -And it is up to the implementation to define the content it requires. - -Here is how 'CustomResourceDefinition' CRD specified in the NPWG spec. - -.. code-block:: yaml - - apiVersion: apiextensions.k8s.io/v1beta1 - kind: CustomResourceDefinition - metadata: - name: network-attachment-definitions.k8s.cni.cncf.io - spec: - group: k8s.cni.cncf.io - version: v1 - scope: Namespaced - names: - plural: network-attachment-definitions - singular: network-attachment-definition - kind: NetworkAttachmentDefinition - shortNames: - - net-attach-def - validation: - openAPIV3Schema: - properties: - spec: - properties: - config: - type: string - -For Kuryr-kubernetes, users should define the 'Network' object with a Neutron -subnet created previously like: - -.. code-block:: yaml - - apiVersion: "kubernetes.cni.cncf.io/v1" - kind: Network - metadata: - name: a-bridge-network - annotations: - openstack.org/kuryr-config: '{ - "subnetId": "id_of_neutron_subnet_created_previously" - }' - -With information read from Pod annotation k8s.v1.cni.cncf.io/networks -and 'Network' objects, the Neutron ports could either be created or retrieved. -Then the Pod annotation openstack.org/kuryr-vif will be updated accordingly. - -Here's how openstack.org/kuryr-vif annotation with additional networks might -look like: - -.. code-block:: yaml - - kind: Pod - metadata: - name: my-pod - namespace: my-namespace - annotations: - openstack.org/kuryr-vif: { - # default interface remains intact - "eth0": { - ... Neutron vif object from default subnet ... - } - # additional interfaces appended by driver 'npwg_multiple_interfaces' - "eth1": { - ... Neutron vif object ... - } - "eth2": { - ... Neutron vif object ... - } - } - -Alternatives -~~~~~~~~~~~~ - -Currently, Kuryr-Kubernetes has already designed a way of defining additional -VIF. This spec will not change that part. Users can choose using which -format they want by configuring 'enabled_vif_drivers'. - -Other end user impact -~~~~~~~~~~~~~~~~~~~~~ -Pods always attach the default Kubernetes network as how Kuryr-Kubernetes works -today, and all networks specified in the Pod annotation are sidecars. - -Assignee(s) -~~~~~~~~~~~ - -Primary assignee: -Peng Liu - -Work Items -~~~~~~~~~~ - -* Implement a new NPWG spec compatible VIF driver. -* Document the procedure of using this new VIF driver. - -Possible Further Work -~~~~~~~~~~~~~~~~~~~~~ - -* To keep on track of the subsequent releases of NPWG spec. -* To allow defining new neutron network/subnet in 'Network' objects, so that - kuryr can create them in Neutron first, then attach Pod to it. - -References ----------- - -.. [#] https://docs.google.com/document/d/1Ny03h6IDVy_e_vmElOqR7UdTPAG_RNydhVE1Kx54kFQ/edit?usp=sharing -.. [#] https://groups.google.com/forum/?_escaped_fragment_=topic/kubernetes-sig-network/ANAjTyqVosw -.. [#] https://docs.openstack.org/kuryr-kubernetes/latest/devref/vif_handler_drivers_design.html diff --git a/doc/source/specs/stein/vhostuser.rst b/doc/source/specs/stein/vhostuser.rst deleted file mode 100644 index ec1eb5cf7..000000000 --- a/doc/source/specs/stein/vhostuser.rst +++ /dev/null @@ -1,197 +0,0 @@ - - -Kuryr Kubernetes vhost-user port integration -============================================ - -Open vSwitch or any other virtual switch can be built with DPDK datapath [3]_, -for this datapath virtual switch provisions vhost-user port. DPDK application -can use it just by accessing UNIX domain socket of vhost-user port. DPDK -applications which use vhost-user port have more network performance compared to -applications which use veth pair with tap interfaces. -DPDK application which uses vhost-user socket it is typical use case for -bare-metal installation in NFV world. -Also there is another use case, where vhost-user ports are passed to VM. In this -case DPDK application inside VM works with vhost-user port through VirtIO -device. This is Nested DPDK Support [1]_ use case. - -Problem statement ------------------ - -Now kuryr-kubernetes doesn't support vhostuser port creation on bare-metal -installation, but OpenStack can be configured to work with vhost-user ports. -In case of vhost-user port in bare-metal installation there is no device, DPDK -applications use unix domain socket created by Open vSwitch daemon, it's control -plain socket. Kuryr-kubernetes has to move this vhost-user socket file to path -available for pod. - -Proposed solution ------------------ - -Kuryr-kubernetes should create Neutron port as usual, NeutronPodVIFDriver will -be used. Then kuryr-kubernetes should handle vif_type vhost-user [2]_, it -already handles port with vif_type ovs for non-DPDK datapath with veth pair as -well as ports with ovs_hybrid_plug where linux bridge is used. No new pod vif -driver will be introduced. - -From user point of view there is no difference in pod definition. It's the same -as with tap based. To request vhost-user port as a main port no need to do -something special. - -When vhost-user port is additional interface it can be defined with Network -Attachment Definition [6]_. - -The type of port will be determined by neutron-openvswitch-agent configuration -file by datapath_type option [2]_, whether the veth is plugged to the OVS bridge -or vhostuser. That's why datapath is not featured in pod's definition, -kuryr-kubernetes will rely on pod's vif type. - -Open vSwitch supports DPDK ports only in special bridges with type netdev, -therefore integration bridge should have netdev type, otherwise OVS dpdk port -will not work. Kuryr-kubernetes uses os-vif, this library does all necessary -work for vif of VIFVhostuser type to set up bridge and create port. - -To be able to use that kind of port in container, socket of that port has to be -placed on the container's file system. It will be done by mountVolumes in pod -yaml file like that: - -.. _configuration: -.. code-block:: yaml - - volumeMounts: - - name: vhostuser - mountPath: /var/run/vhostuser - ... - volumes: - - name: openvswitch - hostPath: - path: /var/run/vhostuser - type: Directory - - -mountPath is defined in kuryr.conf on the minion host - -.. code-block:: ini - - [vhostuser] - mount_point = /var/run/vhostuser - ovs_vhu_path = /var/run/openvswitch - -Single mount point will be provided for several pods -(CONF.vhostuser.mount_point). It's the place where vhost-user socket file will -be moved from ovs_vhu_path. ovs_vhu_path it's a path where Open vSwitch stores -vhost-user socket by default in case when Open vSwitch creates socket. -mount_point and ovs_vhu_path should be on the same point of mount, -otherwise EXDEV (Cross-device link) will be raised and -connection by this socket will be refused. Unfortunately Open vSwitch daemon -can't remove moved socket by ovs-vsctl del-port command, in this case socket -file will be removed by VIFVHostUserDriver. - -Configuration file will be created there for DPDK application. It will contain -auxiliary information: socket name, mac address, ovs port mode. -It might look like: - -.. code-block:: json - - { - "vhostname": "vhu9c9cc8f5-88", - "vhostmac": "fa:16:3e:ef:b0:ed", - "mode": "server" - } - -Name of configuration file will contain container id concatenated by dash with -ifname for this interface. DPDK application will use vhostname to determine -vhost-user socket name. - -To get vhost user socket name inside container user has to read configuration -file. Container identifier will be required for it. Following bash -command will help to get it inside container. - -.. code-block:: bash - - CONTAINER_ID=`sed -ne '/hostname/p' /proc/1/task/1/mountinfo |\ - awk -F '/' '{print $4}'` - -Kuryr-kubernetes can produce multiple ports per one pod, following command can -be used to list all available ports. - -.. code-block:: bash - - ls $MOUNT_POINT/$CONTAINER_ID-eth* - -$MOUNT_POINT here is volumeMounts with name vhostuser defined in pod -configuration_ yaml file. -Value from vhostname field should be used for launching DPDK application. - -.. code-block:: bash - - testpmd -d librte_pmd_virtio.so.17.11 -m 1024 -c 0xC --file-prefix=testpmd_ \ - --vdev=net_virtio_user0,path=/$MOUNT_POINT/$VHU_PORT \ - --no-pci -- --no-lsc-interrupt --auto-start --tx-first \ - --stats-period 1 --disable-hw-vlan; - -vhost-user port has two different modes: client and server. -The type of vhost-user port to create is defined in vif_details by -vhostuser_mode field [4]_. vhost-user port's mode affects socket life cycle. -The client mode from kuryr-kubernetes point of view, it is mode when -ovs-vswitchd creates and listens the vhost-user socket, which is created by the -command below: - -.. code-block:: console - - ovs-vsctl add-port ovsbr0 vhost-user0 -- set Interface vhost-user0 \ - type=dpdkvhostuser - -In this case vhost_user_mode's value will be 'client'. This mode is not robust -because after restarting ovs-vswitchd will recreate sockets by initial path, all -clients have to reestablish connection and kuryr-kubernetes has to move sockets -again. It leads to more complicated solution, that's why another mode in -Open vSwitch was invented, in this mode ovs-vswitchd acts as a client, it tries -to connect by predefined path to vhost-user server (DPDK application in -container). From kuryr-kubernetes point of view it's server mode, -vhost_user_mode's value is 'server'. - -It imposes a restrictions: - -- Kuryr-kubernetes can specify socket path in 'server' mode, but can't in - 'client' mode, at socket creation time -- In case of 'client' mode it's better to recreate whole pod at restart of Open - vSwitch daemon - -This feature doesn't depend on HA behavior. But this feature affects -containeraized cni plugin, due to it requeres the same mount point for source -and destination of vhostuser socket file. - -vhost-user port is not a limited resource it can be scheduled in any nodes -without restrictions. Limited resource here is memory, in most cases number of -huge pages. To configure it see [5]_. - -Initial implementation doesn't cover security issues, DAC and MAC should be -defined by user properly. - - -Implementation -============== - -Work Items ----------- - -* check for vhostuser_mode in neutron_to_osvif_vif_ovs and create appropriate - VIF -* introduce new binding driver VIFVHostUserDriver -* add unit tests for new code -* add tempest test for vhostuser ports - -Assignee --------- - -Alexey Perevalov - - -References ----------- -.. [1] https://blueprints.launchpad.net/kuryr-kubernetes/+spec/nested-dpdk-support -.. [2] https://docs.openstack.org/neutron/pike/contributor/internals/ovs_vhostuser.html -.. [3] http://docs.openvswitch.org/en/latest/topics/dpdk/vhost-user/ -.. [4] https://specs.openstack.org/openstack/nova-specs/specs/kilo/implemented/libvirt_vif_vhostuser.html -.. [5] https://kubernetes.io/docs/tasks/manage-hugepages/scheduling-hugepages/ -.. [6] https://docs.openstack.org/kuryr-kubernetes/latest/specs/rocky/npwg_spec_support.html diff --git a/doc/source/usage.rst b/doc/source/usage.rst deleted file mode 100644 index 627150d95..000000000 --- a/doc/source/usage.rst +++ /dev/null @@ -1,7 +0,0 @@ -===== -Usage -===== - -To use kuryr-kubernetes in a project:: - - import kuryr_kubernetes diff --git a/etc/cni/net.d/10-kuryr.conflist b/etc/cni/net.d/10-kuryr.conflist deleted file mode 100644 index 94ac4193b..000000000 --- a/etc/cni/net.d/10-kuryr.conflist +++ /dev/null @@ -1,11 +0,0 @@ -{ - "name": "kuryr", - "cniVersion": "0.3.1", - "plugins": [ - { - "type": "kuryr-cni", - "kuryr_conf": "/etc/kuryr/kuryr.conf", - "debug": true - } - ] -} diff --git a/etc/cni/net.d/kuryr.conflist.template b/etc/cni/net.d/kuryr.conflist.template deleted file mode 100644 index 94ac4193b..000000000 --- a/etc/cni/net.d/kuryr.conflist.template +++ /dev/null @@ -1,11 +0,0 @@ -{ - "name": "kuryr", - "cniVersion": "0.3.1", - "plugins": [ - { - "type": "kuryr-cni", - "kuryr_conf": "/etc/kuryr/kuryr.conf", - "debug": true - } - ] -} diff --git a/etc/oslo-config-generator/kuryr.conf b/etc/oslo-config-generator/kuryr.conf deleted file mode 100644 index 37ec8ff80..000000000 --- a/etc/oslo-config-generator/kuryr.conf +++ /dev/null @@ -1,4 +0,0 @@ -[DEFAULT] -output_file = etc/kuryr.conf.sample -wrap_width = 79 -namespace = kuryr_kubernetes diff --git a/kubernetes_crds/kuryr_crds/kuryrloadbalancer.yaml b/kubernetes_crds/kuryr_crds/kuryrloadbalancer.yaml deleted file mode 100644 index d3b1c5dde..000000000 --- a/kubernetes_crds/kuryr_crds/kuryrloadbalancer.yaml +++ /dev/null @@ -1,241 +0,0 @@ -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - name: kuryrloadbalancers.openstack.org -spec: - group: openstack.org - scope: Namespaced - names: - plural: kuryrloadbalancers - singular: kuryrloadbalancer - kind: KuryrLoadBalancer - shortNames: - - klb - versions: - - name: v1 - served: true - storage: true - additionalPrinterColumns: - - name: PROJECT-ID - type: string - description: The ID of the PROJECT associated to the loadbalancer - jsonPath: .spec.project_id - - name: Age - type: date - jsonPath: .metadata.creationTimestamp - schema: - openAPIV3Schema: - type: object - properties: - spec: - type: object - properties: - endpointSlices: - type: array - items: - type: object - properties: - endpoints: - type: array - items: - type: object - properties: - addresses: - type: array - items: - type: string - conditions: - type: object - properties: - ready: - type: boolean - hostname: - type: string - targetRef: - type: object - properties: - kind: - type: string - name: - type: string - namespace: - type: string - resourceVersion: - type: string - uid: - type: string - topology: - type: object - ports: - type: array - items: - type: object - properties: - name: - type: string - port: - type: integer - protocol: - type: string - ip: - type: string - lb_ip: - type: string - ports: - type: array - items: - type: object - required: - - port - - protocol - - targetPort - properties: - name: - type: string - port: - type: integer - protocol: - type: string - targetPort: - type: string - project_id: - type: string - security_groups_ids: - type: array - items: - type: string - subnet_id: - type: string - type: - type: string - provider: - type: string - timeout_client_data: - type: integer - timeout_member_data: - type: integer - status: - type: object - properties: - listeners: - type: array - items: - type: object - required: - - id - - loadbalancer_id - - name - - port - - project_id - - protocol - properties: - id: - type: string - loadbalancer_id: - type: string - name: - type: string - port: - type: integer - project_id: - type: string - protocol: - type: string - timeout_client_data: - type: integer - timeout_member_data: - type: integer - loadbalancer: - type: object - required: - - id - - ip - - name - - port_id - - project_id - - provider - - security_groups - - subnet_id - properties: - id: - type: string - ip: - type: string - name: - type: string - port_id: - type: string - project_id: - type: string - provider: - type: string - security_groups: - type: array - items: - type: string - subnet_id: - type: string - members: - type: array - items: - type: object - required: - - id - - ip - - name - - pool_id - - port - - project_id - - subnet_id - properties: - id: - type: string - ip: - type: string - name: - type: string - pool_id: - type: string - port: - type: integer - project_id: - type: string - subnet_id: - type: string - pools: - type: array - items: - type: object - required: - - id - - listener_id - - loadbalancer_id - - name - - project_id - - protocol - properties: - id: - type: string - listener_id: - type: string - loadbalancer_id: - type: string - name: - type: string - project_id: - type: string - protocol: - type: string - service_pub_ip_info: - type: object - required: - - ip_id - - ip_addr - - alloc_method - properties: - ip_id: - type: string - ip_addr: - type: string - alloc_method: - type: string diff --git a/kubernetes_crds/kuryr_crds/kuryrnetwork.yaml b/kubernetes_crds/kuryr_crds/kuryrnetwork.yaml deleted file mode 100644 index f8754548a..000000000 --- a/kubernetes_crds/kuryr_crds/kuryrnetwork.yaml +++ /dev/null @@ -1,59 +0,0 @@ -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - name: kuryrnetworks.openstack.org -spec: - group: openstack.org - scope: Namespaced - names: - plural: kuryrnetworks - singular: kuryrnetwork - kind: KuryrNetwork - shortNames: - - kns - versions: - - name: v1 - served: true - storage: true - additionalPrinterColumns: - - name: SUBNET-CIDR - type: string - description: The subnet CIDR allocated to the namespace - jsonPath: .status.subnetCIDR - - name: Age - type: date - jsonPath: .metadata.creationTimestamp - schema: - openAPIV3Schema: - type: object - properties: - spec: - type: object - required: - - nsName - - projectId - - nsLabels - properties: - nsName: - type: string - projectId: - type: string - nsLabels: - x-kubernetes-preserve-unknown-fields: true - type: object - status: - type: object - properties: - netId: - type: string - populated: - type: boolean - routerId: - type: string - subnetCIDR: - type: string - subnetId: - type: string - nsLabels: - x-kubernetes-preserve-unknown-fields: true - type: object diff --git a/kubernetes_crds/kuryr_crds/kuryrnetworkpolicy.yaml b/kubernetes_crds/kuryr_crds/kuryrnetworkpolicy.yaml deleted file mode 100644 index 3726409d8..000000000 --- a/kubernetes_crds/kuryr_crds/kuryrnetworkpolicy.yaml +++ /dev/null @@ -1,158 +0,0 @@ -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - name: kuryrnetworkpolicies.openstack.org -spec: - group: openstack.org - scope: Namespaced - names: - plural: kuryrnetworkpolicies - singular: kuryrnetworkpolicy - kind: KuryrNetworkPolicy - shortNames: - - knp - versions: - - name: v1 - served: true - storage: true - additionalPrinterColumns: - - name: SG-ID - type: string - description: The ID of the SG associated to the policy - jsonPath: .status.securityGroupId - - name: Age - type: date - jsonPath: .metadata.creationTimestamp - schema: - openAPIV3Schema: - type: object - required: - - status - - spec - properties: - spec: - type: object - required: - - egressSgRules - - ingressSgRules - - podSelector - - policyTypes - properties: - egressSgRules: - type: array - items: - type: object - required: - - sgRule - properties: - affectedPods: - type: array - items: - type: object - properties: - podIP: - type: string - podNamespace: - type: string - required: - - podIP - - podNamespace - namespace: - type: string - sgRule: - type: object - properties: - description: - type: string - direction: - type: string - ethertype: - type: string - port_range_max: - type: integer - port_range_min: - type: integer - protocol: - type: string - remote_ip_prefix: - type: string - ingressSgRules: - type: array - items: - type: object - required: - - sgRule - properties: - affectedPods: - type: array - items: - type: object - properties: - podIP: - type: string - podNamespace: - type: string - required: - - podIP - - podNamespace - namespace: - type: string - sgRule: - type: object - properties: - description: - type: string - direction: - type: string - ethertype: - type: string - port_range_max: - type: integer - port_range_min: - type: integer - protocol: - type: string - remote_ip_prefix: - type: string - podSelector: - x-kubernetes-preserve-unknown-fields: true - type: object - policyTypes: - type: array - items: - type: string - status: - type: object - required: - - securityGroupRules - properties: - securityGroupId: - type: string - securityGroupRules: - type: array - items: - type: object - required: - - id - properties: - id: - type: string - description: - type: string - direction: - type: string - ethertype: - type: string - port_range_max: - type: integer - port_range_min: - type: integer - protocol: - type: string - remote_ip_prefix: - type: string - security_group_id: - type: string - podSelector: - x-kubernetes-preserve-unknown-fields: true - type: object diff --git a/kubernetes_crds/kuryr_crds/kuryrport.yaml b/kubernetes_crds/kuryr_crds/kuryrport.yaml deleted file mode 100644 index e320e048a..000000000 --- a/kubernetes_crds/kuryr_crds/kuryrport.yaml +++ /dev/null @@ -1,54 +0,0 @@ -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - name: kuryrports.openstack.org -spec: - group: openstack.org - scope: Namespaced - names: - plural: kuryrports - singular: kuryrport - kind: KuryrPort - shortNames: - - kp - versions: - - name: v1 - served: true - storage: true - schema: - openAPIV3Schema: - type: object - properties: - spec: - type: object - required: - - podUid - - podNodeName - properties: - podUid: - type: string - podNodeName: - type: string - podStatic: - type: boolean - status: - type: object - required: - - vifs - properties: - vifs: - type: object - x-kubernetes-preserve-unknown-fields: true - additionalPrinterColumns: - - name: PodUID - type: string - description: Pod UID - jsonPath: .spec.podUid - - name: Nodename - type: string - description: Name of the node corresponding pod lives in - jsonPath: .spec.podNodeName - - name: labels - type: string - description: Labels for the CRD - jsonPath: .metadata.labels diff --git a/kubernetes_crds/network_attachment_definition_crd.yaml b/kubernetes_crds/network_attachment_definition_crd.yaml deleted file mode 100644 index 0b0169711..000000000 --- a/kubernetes_crds/network_attachment_definition_crd.yaml +++ /dev/null @@ -1,26 +0,0 @@ -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - name: network-attachment-definitions.k8s.cni.cncf.io -spec: - group: k8s.cni.cncf.io - scope: Namespaced - names: - plural: network-attachment-definitions - singular: network-attachment-definition - kind: NetworkAttachmentDefinition - shortNames: - - net-attach-def - versions: - - name: v1 - served: true - storage: true - schema: - openAPIV3Schema: - type: object - properties: - spec: - type: object - properties: - config: - type: string diff --git a/kuryr_cni/README b/kuryr_cni/README deleted file mode 100644 index dccc35499..000000000 --- a/kuryr_cni/README +++ /dev/null @@ -1,2 +0,0 @@ -This is golang part of Kuryr, that is the CNI plugin that gets injected into -the host. \ No newline at end of file diff --git a/kuryr_cni/go.mod b/kuryr_cni/go.mod deleted file mode 100644 index 37b3eb1db..000000000 --- a/kuryr_cni/go.mod +++ /dev/null @@ -1,9 +0,0 @@ -module main - -go 1.15 - -require ( - github.com/containernetworking/cni v0.8.1 - github.com/onsi/ginkgo v1.16.1 // indirect - github.com/onsi/gomega v1.11.0 // indirect -) diff --git a/kuryr_cni/go.sum b/kuryr_cni/go.sum deleted file mode 100644 index 8097d4b9b..000000000 --- a/kuryr_cni/go.sum +++ /dev/null @@ -1,89 +0,0 @@ -github.com/containernetworking/cni v0.8.1 h1:7zpDnQ3T3s4ucOuJ/ZCLrYBxzkg0AELFfII3Epo9TmI= -github.com/containernetworking/cni v0.8.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= -github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM= -github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= -github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= -github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= -github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.16.1 h1:foqVmeWDD6yYpK+Yz3fHyNIxFYNxswxqNFjSKe+vI54= -github.com/onsi/ginkgo v1.16.1/go.mod h1:CObGmKUOKaSC0RjmoAK7tKyn4Azo5P2IWuoMnvwxz1E= -github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= -github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.11.0 h1:+CqWgvj0OZycCaqclBD1pxKHAU+tOkHmQIWvDHq2aug= -github.com/onsi/gomega v1.11.0/go.mod h1:azGKhqFUon9Vuj0YmTfLSmx0FUwqXYSTl5re8lQLTUg= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb h1:eBmm0M9fYhWpKZLjQUUKka/LtIxf46G4fxeEz5KJr9U= -golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210112080510-489259a85091 h1:DMyOG0U+gKfu8JZzg2UQe9MeaC1X+xQWlAKcRnjxjCw= -golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= diff --git a/kuryr_cni/hack/build-go.sh b/kuryr_cni/hack/build-go.sh deleted file mode 100755 index 71883e4af..000000000 --- a/kuryr_cni/hack/build-go.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/usr/bin/env bash -set -eu -cmd=kuryr-cni -eval $(go env | grep -e "GOHOSTOS" -e "GOHOSTARCH") -GOOS=${GOOS:-${GOHOSTOS}} -GOARCH=${GOACH:-${GOHOSTARCH}} -GOFLAGS=${GOFLAGS:-} -GLDFLAGS=${GLDFLAGS:-} -CGO_ENABLED=0 GOOS=${GOOS} GOARCH=${GOARCH} go build ${GOFLAGS} -ldflags "${GLDFLAGS}" -o bin/${cmd} pkg/* diff --git a/kuryr_cni/hack/update-deps.sh b/kuryr_cni/hack/update-deps.sh deleted file mode 100755 index dd0fa036e..000000000 --- a/kuryr_cni/hack/update-deps.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash -go mod vendor -go mod tidy diff --git a/kuryr_cni/pkg/main.go b/kuryr_cni/pkg/main.go deleted file mode 100644 index a866aaee1..000000000 --- a/kuryr_cni/pkg/main.go +++ /dev/null @@ -1,235 +0,0 @@ -package main - -import ( - "bytes" - "encoding/json" - "fmt" - "io/ioutil" - "log" - "net" - "net/http" - - "github.com/containernetworking/cni/pkg/skel" - "github.com/containernetworking/cni/pkg/types" - cni "github.com/containernetworking/cni/pkg/types" - "github.com/containernetworking/cni/pkg/types/current" - "github.com/containernetworking/cni/pkg/version" -) - -const ( - // FIXME(dulek): We don't really have a good way to fetch current URL:port binding here. - // I'm hardcoding it for now, but in the future we should probably put it in - // the JSON config in 10-kuryr.conflist file that we will get passed on stdin. - urlBase = "http://localhost:5036/" - addPath = "addNetwork" - delPath = "delNetwork" - - ErrVif uint = 899 - ErrParsing uint = 799 -) - -type KuryrDaemonData struct { - IfName string `json:"CNI_IFNAME"` - Netns string `json:"CNI_NETNS"` - Path string `json:"CNI_PATH"` - Command string `json:"CNI_COMMAND"` - ContainerID string `json:"CNI_CONTAINERID"` - Args string `json:"CNI_ARGS"` - KuryrConf interface{} `json:"config_kuryr"` -} - -func transformData(args *skel.CmdArgs, command string) (KuryrDaemonData, error) { - var conf interface{} - err := json.Unmarshal(args.StdinData, &conf) - if err != nil { - newErr := types.Error{ - Code: types.ErrDecodingFailure, - Msg: fmt.Sprintf("Error when reading configuration: %v", err), - Details: "", - } - return KuryrDaemonData{}, &newErr - } - - return KuryrDaemonData{ - IfName: args.IfName, - Netns: args.Netns, - Path: args.Path, - Command: command, - ContainerID: args.ContainerID, - Args: args.Args, - KuryrConf: conf, - }, nil -} - -func makeDaemonRequest(data KuryrDaemonData, expectedCode int) ([]byte, error) { - log.Printf("Calling kuryr-daemon with %s request (CNI_ARGS=%s, CNI_NETNS=%s).", data.Command, data.Args, data.Netns) - - b, err := json.Marshal(data) - if err != nil { - return []byte{}, &types.Error{ - Code: types.ErrInvalidNetworkConfig, - Msg: fmt.Sprintf("Error when preparing payload for kuryr-daemon: %v", err), - Details: "", - } - } - - url := "" - switch data.Command { - case "ADD": - url = urlBase + addPath - case "DEL": - url = urlBase + delPath - default: - return []byte{}, &types.Error{ - Code: types.ErrInvalidEnvironmentVariables, - Msg: fmt.Sprintf("Cannot handle command %s", data.Command), - Details: "", - } - } - - resp, err := http.Post(url, "application/json", bytes.NewBuffer(b)) - if err != nil { - return []byte{}, &types.Error{ - Code: types.ErrTryAgainLater, - Msg: fmt.Sprintf("Looks like %s cannot be reached. Is kuryr-daemon running?", url), - Details: fmt.Sprintf("%v", err), - } - } - defer resp.Body.Close() - - body, _ := ioutil.ReadAll(resp.Body) - if resp.StatusCode != expectedCode { - if len(body) > 1 { - var err types.Error - json.Unmarshal(body, &err) - return []byte{}, &err - } - return []byte{}, &types.Error{ - Code: uint(resp.StatusCode), - Msg: fmt.Sprintf("CNI Daemon returned error %d %s", resp.StatusCode, body), - Details: "", - } - } - return body, nil -} - -func cmdAdd(args *skel.CmdArgs) error { - data, err := transformData(args, "ADD") - if err != nil { - return err - } - - body, err := makeDaemonRequest(data, 202) - if err != nil { - return err - } - - vif := VIF{} - er := json.Unmarshal(body, &vif) - if er != nil { - return &types.Error{ - Code: ErrVif, - Msg: fmt.Sprintf("Error when reading response from kuryr-daemon: %s", string(body)), - Details: fmt.Sprintf("%v", er), - } - } - - iface := current.Interface{} - iface.Name = args.IfName - iface.Mac = vif.Address - iface.Sandbox = args.ContainerID - - var ips []*current.IPConfig - var dns types.DNS - var routes []*types.Route - for _, subnet := range vif.Network.Subnets { - addrStr := subnet.Ips[0].Address - addr := net.ParseIP(addrStr) - if addr == nil { - return &types.Error{ - Code: ErrParsing, - Msg: fmt.Sprintf("Error when parsing IP address %s received from kuryr-daemon", addrStr), - Details: "", - } - } - _, cidr, err := net.ParseCIDR(subnet.Cidr) - if err != nil { - return &types.Error{ - Code: ErrParsing, - Msg: fmt.Sprintf("Error when parsing CIDR %s received from kuryr-daemon", subnet.Cidr), - Details: fmt.Sprintf("%v", err), - } - } - - ver := "4" - if addr.To4() == nil { - ver = "6" - } - - prefixSize, _ := cidr.Mask.Size() - ifaceCIDR := fmt.Sprintf("%s/%d", addr.String(), prefixSize) - ipAddress, err := cni.ParseCIDR(ifaceCIDR) - if err != nil { - return &types.Error{ - Code: ErrParsing, - Msg: fmt.Sprintf("Error when parsing CIDR %s received from kuryr-daemon", ifaceCIDR), - Details: fmt.Sprintf("%v", err), - } - } - ifaceNum := 0 - - ips = append(ips, ¤t.IPConfig{ - Version: ver, - Interface: &ifaceNum, - Gateway: net.ParseIP(subnet.Gateway), - Address: *ipAddress, - }) - - for _, route := range subnet.Routes { - _, dst, err := net.ParseCIDR(route.Cidr) - if err != nil { - return &types.Error{ - Code: ErrParsing, - Msg: fmt.Sprintf("Error when parsing CIDR %s received from kuryr-daemon", route.Cidr), - Details: fmt.Sprintf("%v", err), - } - } - - gw := net.ParseIP(route.Gateway) - if gw == nil { - return &types.Error{ - Code: ErrParsing, - Msg: fmt.Sprintf("Error when parsing IP address %s received from kuryr-daemon", route.Gateway), - Details: "", - } - } - - routes = append(routes, &types.Route{Dst: *dst, GW: gw}) - } - - dns.Nameservers = append(dns.Nameservers, subnet.DNS...) - } - - res := ¤t.Result{ - Interfaces: []*current.Interface{&iface}, - IPs: ips, - DNS: dns, - Routes: routes, - } - - return types.PrintResult(res, "0.3.1") -} - -func cmdDel(args *skel.CmdArgs) error { - data, err := transformData(args, "DEL") - _, err = makeDaemonRequest(data, 204) - return err -} - -func cmdCheck(args *skel.CmdArgs) error { - return nil -} - -func main() { - skel.PluginMain(cmdAdd, cmdCheck, cmdDel, version.All, "CNI Plugin Kuryr-Kubernetes v1.0.0") -} diff --git a/kuryr_cni/pkg/ovo.go b/kuryr_cni/pkg/ovo.go deleted file mode 100644 index ac67833f0..000000000 --- a/kuryr_cni/pkg/ovo.go +++ /dev/null @@ -1,120 +0,0 @@ -package main - -import ( - "encoding/json" - "reflect" -) - -const ( - vod = "versioned_object.data" - ob = "objects" -) - -type VIF struct { - Network Network `json:"network"` - Address string `json:"address"` - VifName string `json:"vif_name"` -} - -type Network struct { - Subnets []Subnet `json:"subnets"` -} - -type Route struct { - Cidr string `json:"cidr"` - Gateway string `json:"gateway"` -} - -type IP struct { - Address string `json:"address"` -} - -type Subnet struct { - Routes []Route `json:"routes"` - Ips []IP `json:"ips"` - Cidr string `json:"cidr"` - Gateway string `json:"gateway"` - DNS []string `json:"dns"` -} - -func UnmarshalOVO(data []byte, r interface{}) error { - // Unmarshall into a generic map - var i map[string]interface{} - if err := json.Unmarshal(data, &i); err != nil { - return err - } - - // Skip versioned_object.data level - d := i[vod].(map[string]interface{}) - - p := reflect.ValueOf(r) // this will be a pointer - v := p.Elem() // dereferences pointer - t := v.Type() // gets type of the struct - - // Go over fields of the struct - for i := 0; i < t.NumField(); i++ { - // Initial info - field := t.Field(i) - fieldVal := v.Field(i) - key := field.Tag.Get("json") - - var obj interface{} - - // Main switch - switch fieldVal.Kind() { - case reflect.String: - // In case of string let's just write it and we're done (hence continue) - fieldVal.SetString(d[key].(string)) - continue - case reflect.Slice: - if reflect.ValueOf(d[key]).Kind() != reflect.Slice { - // It's a list with next level of "versioned_object.data" and then "objects" keys. Let's flatten this. - listObj := d[key].(map[string]interface{}) - listData := listObj[vod].(map[string]interface{}) - obj = listData[ob].([]interface{}) - break - } - // If we have a slice and d[key] is just a simple list, then struct's approach will work fine, that's - // why there's this fallthrough. - fallthrough - case reflect.Struct: - // Treat it as struct - obj = d[key] - } - - // For slices and structs marshall that level of JSON, and unmarshall them into the result. The weird - // approach with reflect.New is forced by how reflections work in golang. - jsonBytes, err := json.Marshal(obj) - if err != nil { - return err - } - new := reflect.New(fieldVal.Type()) - inter := new.Interface() - if err := json.Unmarshal(jsonBytes, &inter); err != nil { - return err - } - fieldVal.Set(new.Elem()) - } - - return nil -} - -func (v *VIF) UnmarshalJSON(data []byte) error { - return UnmarshalOVO(data, v) -} - -func (v *Network) UnmarshalJSON(data []byte) error { - return UnmarshalOVO(data, v) -} - -func (v *Subnet) UnmarshalJSON(data []byte) error { - return UnmarshalOVO(data, v) -} - -func (v *IP) UnmarshalJSON(data []byte) error { - return UnmarshalOVO(data, v) -} - -func (v *Route) UnmarshalJSON(data []byte) error { - return UnmarshalOVO(data, v) -} diff --git a/kuryr_cni/vendor/github.com/containernetworking/cni/LICENSE b/kuryr_cni/vendor/github.com/containernetworking/cni/LICENSE deleted file mode 100644 index 8f71f43fe..000000000 --- a/kuryr_cni/vendor/github.com/containernetworking/cni/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright {yyyy} {name of copyright owner} - - 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. - diff --git a/kuryr_cni/vendor/github.com/containernetworking/cni/pkg/skel/skel.go b/kuryr_cni/vendor/github.com/containernetworking/cni/pkg/skel/skel.go deleted file mode 100644 index da42db559..000000000 --- a/kuryr_cni/vendor/github.com/containernetworking/cni/pkg/skel/skel.go +++ /dev/null @@ -1,296 +0,0 @@ -// Copyright 2014-2016 CNI authors -// -// 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. - -// Package skel provides skeleton code for a CNI plugin. -// In particular, it implements argument parsing and validation. -package skel - -import ( - "bytes" - "encoding/json" - "fmt" - "io" - "io/ioutil" - "log" - "os" - "strings" - - "github.com/containernetworking/cni/pkg/types" - "github.com/containernetworking/cni/pkg/utils" - "github.com/containernetworking/cni/pkg/version" -) - -// CmdArgs captures all the arguments passed in to the plugin -// via both env vars and stdin -type CmdArgs struct { - ContainerID string - Netns string - IfName string - Args string - Path string - StdinData []byte -} - -type dispatcher struct { - Getenv func(string) string - Stdin io.Reader - Stdout io.Writer - Stderr io.Writer - - ConfVersionDecoder version.ConfigDecoder - VersionReconciler version.Reconciler -} - -type reqForCmdEntry map[string]bool - -func (t *dispatcher) getCmdArgsFromEnv() (string, *CmdArgs, *types.Error) { - var cmd, contID, netns, ifName, args, path string - - vars := []struct { - name string - val *string - reqForCmd reqForCmdEntry - }{ - { - "CNI_COMMAND", - &cmd, - reqForCmdEntry{ - "ADD": true, - "CHECK": true, - "DEL": true, - }, - }, - { - "CNI_CONTAINERID", - &contID, - reqForCmdEntry{ - "ADD": true, - "CHECK": true, - "DEL": true, - }, - }, - { - "CNI_NETNS", - &netns, - reqForCmdEntry{ - "ADD": true, - "CHECK": true, - "DEL": false, - }, - }, - { - "CNI_IFNAME", - &ifName, - reqForCmdEntry{ - "ADD": true, - "CHECK": true, - "DEL": true, - }, - }, - { - "CNI_ARGS", - &args, - reqForCmdEntry{ - "ADD": false, - "CHECK": false, - "DEL": false, - }, - }, - { - "CNI_PATH", - &path, - reqForCmdEntry{ - "ADD": true, - "CHECK": true, - "DEL": true, - }, - }, - } - - argsMissing := make([]string, 0) - for _, v := range vars { - *v.val = t.Getenv(v.name) - if *v.val == "" { - if v.reqForCmd[cmd] || v.name == "CNI_COMMAND" { - argsMissing = append(argsMissing, v.name) - } - } - } - - if len(argsMissing) > 0 { - joined := strings.Join(argsMissing, ",") - return "", nil, types.NewError(types.ErrInvalidEnvironmentVariables, fmt.Sprintf("required env variables [%s] missing", joined), "") - } - - if cmd == "VERSION" { - t.Stdin = bytes.NewReader(nil) - } - - stdinData, err := ioutil.ReadAll(t.Stdin) - if err != nil { - return "", nil, types.NewError(types.ErrIOFailure, fmt.Sprintf("error reading from stdin: %v", err), "") - } - - cmdArgs := &CmdArgs{ - ContainerID: contID, - Netns: netns, - IfName: ifName, - Args: args, - Path: path, - StdinData: stdinData, - } - return cmd, cmdArgs, nil -} - -func (t *dispatcher) checkVersionAndCall(cmdArgs *CmdArgs, pluginVersionInfo version.PluginInfo, toCall func(*CmdArgs) error) *types.Error { - configVersion, err := t.ConfVersionDecoder.Decode(cmdArgs.StdinData) - if err != nil { - return types.NewError(types.ErrDecodingFailure, err.Error(), "") - } - verErr := t.VersionReconciler.Check(configVersion, pluginVersionInfo) - if verErr != nil { - return types.NewError(types.ErrIncompatibleCNIVersion, "incompatible CNI versions", verErr.Details()) - } - - if err = toCall(cmdArgs); err != nil { - if e, ok := err.(*types.Error); ok { - // don't wrap Error in Error - return e - } - return types.NewError(types.ErrInternal, err.Error(), "") - } - - return nil -} - -func validateConfig(jsonBytes []byte) *types.Error { - var conf struct { - Name string `json:"name"` - } - if err := json.Unmarshal(jsonBytes, &conf); err != nil { - return types.NewError(types.ErrDecodingFailure, fmt.Sprintf("error unmarshall network config: %v", err), "") - } - if conf.Name == "" { - return types.NewError(types.ErrInvalidNetworkConfig, "missing network name", "") - } - if err := utils.ValidateNetworkName(conf.Name); err != nil { - return err - } - return nil -} - -func (t *dispatcher) pluginMain(cmdAdd, cmdCheck, cmdDel func(_ *CmdArgs) error, versionInfo version.PluginInfo, about string) *types.Error { - cmd, cmdArgs, err := t.getCmdArgsFromEnv() - if err != nil { - // Print the about string to stderr when no command is set - if err.Code == types.ErrInvalidEnvironmentVariables && t.Getenv("CNI_COMMAND") == "" && about != "" { - _, _ = fmt.Fprintln(t.Stderr, about) - return nil - } - return err - } - - if cmd != "VERSION" { - if err = validateConfig(cmdArgs.StdinData); err != nil { - return err - } - if err = utils.ValidateContainerID(cmdArgs.ContainerID); err != nil { - return err - } - if err = utils.ValidateInterfaceName(cmdArgs.IfName); err != nil { - return err - } - } - - switch cmd { - case "ADD": - err = t.checkVersionAndCall(cmdArgs, versionInfo, cmdAdd) - case "CHECK": - configVersion, err := t.ConfVersionDecoder.Decode(cmdArgs.StdinData) - if err != nil { - return types.NewError(types.ErrDecodingFailure, err.Error(), "") - } - if gtet, err := version.GreaterThanOrEqualTo(configVersion, "0.4.0"); err != nil { - return types.NewError(types.ErrDecodingFailure, err.Error(), "") - } else if !gtet { - return types.NewError(types.ErrIncompatibleCNIVersion, "config version does not allow CHECK", "") - } - for _, pluginVersion := range versionInfo.SupportedVersions() { - gtet, err := version.GreaterThanOrEqualTo(pluginVersion, configVersion) - if err != nil { - return types.NewError(types.ErrDecodingFailure, err.Error(), "") - } else if gtet { - if err := t.checkVersionAndCall(cmdArgs, versionInfo, cmdCheck); err != nil { - return err - } - return nil - } - } - return types.NewError(types.ErrIncompatibleCNIVersion, "plugin version does not allow CHECK", "") - case "DEL": - err = t.checkVersionAndCall(cmdArgs, versionInfo, cmdDel) - case "VERSION": - if err := versionInfo.Encode(t.Stdout); err != nil { - return types.NewError(types.ErrIOFailure, err.Error(), "") - } - default: - return types.NewError(types.ErrInvalidEnvironmentVariables, fmt.Sprintf("unknown CNI_COMMAND: %v", cmd), "") - } - - if err != nil { - return err - } - return nil -} - -// PluginMainWithError is the core "main" for a plugin. It accepts -// callback functions for add, check, and del CNI commands and returns an error. -// -// The caller must also specify what CNI spec versions the plugin supports. -// -// It is the responsibility of the caller to check for non-nil error return. -// -// For a plugin to comply with the CNI spec, it must print any error to stdout -// as JSON and then exit with nonzero status code. -// -// To let this package automatically handle errors and call os.Exit(1) for you, -// use PluginMain() instead. -func PluginMainWithError(cmdAdd, cmdCheck, cmdDel func(_ *CmdArgs) error, versionInfo version.PluginInfo, about string) *types.Error { - return (&dispatcher{ - Getenv: os.Getenv, - Stdin: os.Stdin, - Stdout: os.Stdout, - Stderr: os.Stderr, - }).pluginMain(cmdAdd, cmdCheck, cmdDel, versionInfo, about) -} - -// PluginMain is the core "main" for a plugin which includes automatic error handling. -// -// The caller must also specify what CNI spec versions the plugin supports. -// -// The caller can specify an "about" string, which is printed on stderr -// when no CNI_COMMAND is specified. The recommended output is "CNI plugin v" -// -// When an error occurs in either cmdAdd, cmdCheck, or cmdDel, PluginMain will print the error -// as JSON to stdout and call os.Exit(1). -// -// To have more control over error handling, use PluginMainWithError() instead. -func PluginMain(cmdAdd, cmdCheck, cmdDel func(_ *CmdArgs) error, versionInfo version.PluginInfo, about string) { - if e := PluginMainWithError(cmdAdd, cmdCheck, cmdDel, versionInfo, about); e != nil { - if err := e.Print(); err != nil { - log.Print("Error writing error JSON to stdout: ", err) - } - os.Exit(1) - } -} diff --git a/kuryr_cni/vendor/github.com/containernetworking/cni/pkg/types/020/types.go b/kuryr_cni/vendor/github.com/containernetworking/cni/pkg/types/020/types.go deleted file mode 100644 index 36f31678a..000000000 --- a/kuryr_cni/vendor/github.com/containernetworking/cni/pkg/types/020/types.go +++ /dev/null @@ -1,126 +0,0 @@ -// Copyright 2016 CNI authors -// -// 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. - -package types020 - -import ( - "encoding/json" - "fmt" - "io" - "net" - "os" - - "github.com/containernetworking/cni/pkg/types" -) - -const ImplementedSpecVersion string = "0.2.0" - -var SupportedVersions = []string{"", "0.1.0", ImplementedSpecVersion} - -// Compatibility types for CNI version 0.1.0 and 0.2.0 - -func NewResult(data []byte) (types.Result, error) { - result := &Result{} - if err := json.Unmarshal(data, result); err != nil { - return nil, err - } - return result, nil -} - -func GetResult(r types.Result) (*Result, error) { - // We expect version 0.1.0/0.2.0 results - result020, err := r.GetAsVersion(ImplementedSpecVersion) - if err != nil { - return nil, err - } - result, ok := result020.(*Result) - if !ok { - return nil, fmt.Errorf("failed to convert result") - } - return result, nil -} - -// Result is what gets returned from the plugin (via stdout) to the caller -type Result struct { - CNIVersion string `json:"cniVersion,omitempty"` - IP4 *IPConfig `json:"ip4,omitempty"` - IP6 *IPConfig `json:"ip6,omitempty"` - DNS types.DNS `json:"dns,omitempty"` -} - -func (r *Result) Version() string { - return ImplementedSpecVersion -} - -func (r *Result) GetAsVersion(version string) (types.Result, error) { - for _, supportedVersion := range SupportedVersions { - if version == supportedVersion { - r.CNIVersion = version - return r, nil - } - } - return nil, fmt.Errorf("cannot convert version %q to %s", SupportedVersions, version) -} - -func (r *Result) Print() error { - return r.PrintTo(os.Stdout) -} - -func (r *Result) PrintTo(writer io.Writer) error { - data, err := json.MarshalIndent(r, "", " ") - if err != nil { - return err - } - _, err = writer.Write(data) - return err -} - -// IPConfig contains values necessary to configure an interface -type IPConfig struct { - IP net.IPNet - Gateway net.IP - Routes []types.Route -} - -// net.IPNet is not JSON (un)marshallable so this duality is needed -// for our custom IPNet type - -// JSON (un)marshallable types -type ipConfig struct { - IP types.IPNet `json:"ip"` - Gateway net.IP `json:"gateway,omitempty"` - Routes []types.Route `json:"routes,omitempty"` -} - -func (c *IPConfig) MarshalJSON() ([]byte, error) { - ipc := ipConfig{ - IP: types.IPNet(c.IP), - Gateway: c.Gateway, - Routes: c.Routes, - } - - return json.Marshal(ipc) -} - -func (c *IPConfig) UnmarshalJSON(data []byte) error { - ipc := ipConfig{} - if err := json.Unmarshal(data, &ipc); err != nil { - return err - } - - c.IP = net.IPNet(ipc.IP) - c.Gateway = ipc.Gateway - c.Routes = ipc.Routes - return nil -} diff --git a/kuryr_cni/vendor/github.com/containernetworking/cni/pkg/types/args.go b/kuryr_cni/vendor/github.com/containernetworking/cni/pkg/types/args.go deleted file mode 100644 index 4eac64899..000000000 --- a/kuryr_cni/vendor/github.com/containernetworking/cni/pkg/types/args.go +++ /dev/null @@ -1,112 +0,0 @@ -// Copyright 2015 CNI authors -// -// 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. - -package types - -import ( - "encoding" - "fmt" - "reflect" - "strings" -) - -// UnmarshallableBool typedef for builtin bool -// because builtin type's methods can't be declared -type UnmarshallableBool bool - -// UnmarshalText implements the encoding.TextUnmarshaler interface. -// Returns boolean true if the string is "1" or "[Tt]rue" -// Returns boolean false if the string is "0" or "[Ff]alse" -func (b *UnmarshallableBool) UnmarshalText(data []byte) error { - s := strings.ToLower(string(data)) - switch s { - case "1", "true": - *b = true - case "0", "false": - *b = false - default: - return fmt.Errorf("boolean unmarshal error: invalid input %s", s) - } - return nil -} - -// UnmarshallableString typedef for builtin string -type UnmarshallableString string - -// UnmarshalText implements the encoding.TextUnmarshaler interface. -// Returns the string -func (s *UnmarshallableString) UnmarshalText(data []byte) error { - *s = UnmarshallableString(data) - return nil -} - -// CommonArgs contains the IgnoreUnknown argument -// and must be embedded by all Arg structs -type CommonArgs struct { - IgnoreUnknown UnmarshallableBool `json:"ignoreunknown,omitempty"` -} - -// GetKeyField is a helper function to receive Values -// Values that represent a pointer to a struct -func GetKeyField(keyString string, v reflect.Value) reflect.Value { - return v.Elem().FieldByName(keyString) -} - -// UnmarshalableArgsError is used to indicate error unmarshalling args -// from the args-string in the form "K=V;K2=V2;..." -type UnmarshalableArgsError struct { - error -} - -// LoadArgs parses args from a string in the form "K=V;K2=V2;..." -func LoadArgs(args string, container interface{}) error { - if args == "" { - return nil - } - - containerValue := reflect.ValueOf(container) - - pairs := strings.Split(args, ";") - unknownArgs := []string{} - for _, pair := range pairs { - kv := strings.Split(pair, "=") - if len(kv) != 2 { - return fmt.Errorf("ARGS: invalid pair %q", pair) - } - keyString := kv[0] - valueString := kv[1] - keyField := GetKeyField(keyString, containerValue) - if !keyField.IsValid() { - unknownArgs = append(unknownArgs, pair) - continue - } - keyFieldIface := keyField.Addr().Interface() - u, ok := keyFieldIface.(encoding.TextUnmarshaler) - if !ok { - return UnmarshalableArgsError{fmt.Errorf( - "ARGS: cannot unmarshal into field '%s' - type '%s' does not implement encoding.TextUnmarshaler", - keyString, reflect.TypeOf(keyFieldIface))} - } - err := u.UnmarshalText([]byte(valueString)) - if err != nil { - return fmt.Errorf("ARGS: error parsing value of pair %q: %v)", pair, err) - } - } - - isIgnoreUnknown := GetKeyField("IgnoreUnknown", containerValue).Bool() - if len(unknownArgs) > 0 && !isIgnoreUnknown { - return fmt.Errorf("ARGS: unknown args %q", unknownArgs) - } - return nil -} diff --git a/kuryr_cni/vendor/github.com/containernetworking/cni/pkg/types/current/types.go b/kuryr_cni/vendor/github.com/containernetworking/cni/pkg/types/current/types.go deleted file mode 100644 index 754cc6e72..000000000 --- a/kuryr_cni/vendor/github.com/containernetworking/cni/pkg/types/current/types.go +++ /dev/null @@ -1,276 +0,0 @@ -// Copyright 2016 CNI authors -// -// 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. - -package current - -import ( - "encoding/json" - "fmt" - "io" - "net" - "os" - - "github.com/containernetworking/cni/pkg/types" - "github.com/containernetworking/cni/pkg/types/020" -) - -const ImplementedSpecVersion string = "0.4.0" - -var SupportedVersions = []string{"0.3.0", "0.3.1", ImplementedSpecVersion} - -func NewResult(data []byte) (types.Result, error) { - result := &Result{} - if err := json.Unmarshal(data, result); err != nil { - return nil, err - } - return result, nil -} - -func GetResult(r types.Result) (*Result, error) { - resultCurrent, err := r.GetAsVersion(ImplementedSpecVersion) - if err != nil { - return nil, err - } - result, ok := resultCurrent.(*Result) - if !ok { - return nil, fmt.Errorf("failed to convert result") - } - return result, nil -} - -var resultConverters = []struct { - versions []string - convert func(types.Result) (*Result, error) -}{ - {types020.SupportedVersions, convertFrom020}, - {SupportedVersions, convertFrom030}, -} - -func convertFrom020(result types.Result) (*Result, error) { - oldResult, err := types020.GetResult(result) - if err != nil { - return nil, err - } - - newResult := &Result{ - CNIVersion: ImplementedSpecVersion, - DNS: oldResult.DNS, - Routes: []*types.Route{}, - } - - if oldResult.IP4 != nil { - newResult.IPs = append(newResult.IPs, &IPConfig{ - Version: "4", - Address: oldResult.IP4.IP, - Gateway: oldResult.IP4.Gateway, - }) - for _, route := range oldResult.IP4.Routes { - newResult.Routes = append(newResult.Routes, &types.Route{ - Dst: route.Dst, - GW: route.GW, - }) - } - } - - if oldResult.IP6 != nil { - newResult.IPs = append(newResult.IPs, &IPConfig{ - Version: "6", - Address: oldResult.IP6.IP, - Gateway: oldResult.IP6.Gateway, - }) - for _, route := range oldResult.IP6.Routes { - newResult.Routes = append(newResult.Routes, &types.Route{ - Dst: route.Dst, - GW: route.GW, - }) - } - } - - return newResult, nil -} - -func convertFrom030(result types.Result) (*Result, error) { - newResult, ok := result.(*Result) - if !ok { - return nil, fmt.Errorf("failed to convert result") - } - newResult.CNIVersion = ImplementedSpecVersion - return newResult, nil -} - -func NewResultFromResult(result types.Result) (*Result, error) { - version := result.Version() - for _, converter := range resultConverters { - for _, supportedVersion := range converter.versions { - if version == supportedVersion { - return converter.convert(result) - } - } - } - return nil, fmt.Errorf("unsupported CNI result22 version %q", version) -} - -// Result is what gets returned from the plugin (via stdout) to the caller -type Result struct { - CNIVersion string `json:"cniVersion,omitempty"` - Interfaces []*Interface `json:"interfaces,omitempty"` - IPs []*IPConfig `json:"ips,omitempty"` - Routes []*types.Route `json:"routes,omitempty"` - DNS types.DNS `json:"dns,omitempty"` -} - -// Convert to the older 0.2.0 CNI spec Result type -func (r *Result) convertTo020() (*types020.Result, error) { - oldResult := &types020.Result{ - CNIVersion: types020.ImplementedSpecVersion, - DNS: r.DNS, - } - - for _, ip := range r.IPs { - // Only convert the first IP address of each version as 0.2.0 - // and earlier cannot handle multiple IP addresses - if ip.Version == "4" && oldResult.IP4 == nil { - oldResult.IP4 = &types020.IPConfig{ - IP: ip.Address, - Gateway: ip.Gateway, - } - } else if ip.Version == "6" && oldResult.IP6 == nil { - oldResult.IP6 = &types020.IPConfig{ - IP: ip.Address, - Gateway: ip.Gateway, - } - } - - if oldResult.IP4 != nil && oldResult.IP6 != nil { - break - } - } - - for _, route := range r.Routes { - is4 := route.Dst.IP.To4() != nil - if is4 && oldResult.IP4 != nil { - oldResult.IP4.Routes = append(oldResult.IP4.Routes, types.Route{ - Dst: route.Dst, - GW: route.GW, - }) - } else if !is4 && oldResult.IP6 != nil { - oldResult.IP6.Routes = append(oldResult.IP6.Routes, types.Route{ - Dst: route.Dst, - GW: route.GW, - }) - } - } - - if oldResult.IP4 == nil && oldResult.IP6 == nil { - return nil, fmt.Errorf("cannot convert: no valid IP addresses") - } - - return oldResult, nil -} - -func (r *Result) Version() string { - return ImplementedSpecVersion -} - -func (r *Result) GetAsVersion(version string) (types.Result, error) { - switch version { - case "0.3.0", "0.3.1", ImplementedSpecVersion: - r.CNIVersion = version - return r, nil - case types020.SupportedVersions[0], types020.SupportedVersions[1], types020.SupportedVersions[2]: - return r.convertTo020() - } - return nil, fmt.Errorf("cannot convert version 0.3.x to %q", version) -} - -func (r *Result) Print() error { - return r.PrintTo(os.Stdout) -} - -func (r *Result) PrintTo(writer io.Writer) error { - data, err := json.MarshalIndent(r, "", " ") - if err != nil { - return err - } - _, err = writer.Write(data) - return err -} - -// Convert this old version result to the current CNI version result -func (r *Result) Convert() (*Result, error) { - return r, nil -} - -// Interface contains values about the created interfaces -type Interface struct { - Name string `json:"name"` - Mac string `json:"mac,omitempty"` - Sandbox string `json:"sandbox,omitempty"` -} - -func (i *Interface) String() string { - return fmt.Sprintf("%+v", *i) -} - -// Int returns a pointer to the int value passed in. Used to -// set the IPConfig.Interface field. -func Int(v int) *int { - return &v -} - -// IPConfig contains values necessary to configure an IP address on an interface -type IPConfig struct { - // IP version, either "4" or "6" - Version string - // Index into Result structs Interfaces list - Interface *int - Address net.IPNet - Gateway net.IP -} - -func (i *IPConfig) String() string { - return fmt.Sprintf("%+v", *i) -} - -// JSON (un)marshallable types -type ipConfig struct { - Version string `json:"version"` - Interface *int `json:"interface,omitempty"` - Address types.IPNet `json:"address"` - Gateway net.IP `json:"gateway,omitempty"` -} - -func (c *IPConfig) MarshalJSON() ([]byte, error) { - ipc := ipConfig{ - Version: c.Version, - Interface: c.Interface, - Address: types.IPNet(c.Address), - Gateway: c.Gateway, - } - - return json.Marshal(ipc) -} - -func (c *IPConfig) UnmarshalJSON(data []byte) error { - ipc := ipConfig{} - if err := json.Unmarshal(data, &ipc); err != nil { - return err - } - - c.Version = ipc.Version - c.Interface = ipc.Interface - c.Address = net.IPNet(ipc.Address) - c.Gateway = ipc.Gateway - return nil -} diff --git a/kuryr_cni/vendor/github.com/containernetworking/cni/pkg/types/types.go b/kuryr_cni/vendor/github.com/containernetworking/cni/pkg/types/types.go deleted file mode 100644 index 3fa757a5d..000000000 --- a/kuryr_cni/vendor/github.com/containernetworking/cni/pkg/types/types.go +++ /dev/null @@ -1,207 +0,0 @@ -// Copyright 2015 CNI authors -// -// 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. - -package types - -import ( - "encoding/json" - "fmt" - "io" - "net" - "os" -) - -// like net.IPNet but adds JSON marshalling and unmarshalling -type IPNet net.IPNet - -// ParseCIDR takes a string like "10.2.3.1/24" and -// return IPNet with "10.2.3.1" and /24 mask -func ParseCIDR(s string) (*net.IPNet, error) { - ip, ipn, err := net.ParseCIDR(s) - if err != nil { - return nil, err - } - - ipn.IP = ip - return ipn, nil -} - -func (n IPNet) MarshalJSON() ([]byte, error) { - return json.Marshal((*net.IPNet)(&n).String()) -} - -func (n *IPNet) UnmarshalJSON(data []byte) error { - var s string - if err := json.Unmarshal(data, &s); err != nil { - return err - } - - tmp, err := ParseCIDR(s) - if err != nil { - return err - } - - *n = IPNet(*tmp) - return nil -} - -// NetConf describes a network. -type NetConf struct { - CNIVersion string `json:"cniVersion,omitempty"` - - Name string `json:"name,omitempty"` - Type string `json:"type,omitempty"` - Capabilities map[string]bool `json:"capabilities,omitempty"` - IPAM IPAM `json:"ipam,omitempty"` - DNS DNS `json:"dns"` - - RawPrevResult map[string]interface{} `json:"prevResult,omitempty"` - PrevResult Result `json:"-"` -} - -type IPAM struct { - Type string `json:"type,omitempty"` -} - -// NetConfList describes an ordered list of networks. -type NetConfList struct { - CNIVersion string `json:"cniVersion,omitempty"` - - Name string `json:"name,omitempty"` - DisableCheck bool `json:"disableCheck,omitempty"` - Plugins []*NetConf `json:"plugins,omitempty"` -} - -type ResultFactoryFunc func([]byte) (Result, error) - -// Result is an interface that provides the result of plugin execution -type Result interface { - // The highest CNI specification result version the result supports - // without having to convert - Version() string - - // Returns the result converted into the requested CNI specification - // result version, or an error if conversion failed - GetAsVersion(version string) (Result, error) - - // Prints the result in JSON format to stdout - Print() error - - // Prints the result in JSON format to provided writer - PrintTo(writer io.Writer) error -} - -func PrintResult(result Result, version string) error { - newResult, err := result.GetAsVersion(version) - if err != nil { - return err - } - return newResult.Print() -} - -// DNS contains values interesting for DNS resolvers -type DNS struct { - Nameservers []string `json:"nameservers,omitempty"` - Domain string `json:"domain,omitempty"` - Search []string `json:"search,omitempty"` - Options []string `json:"options,omitempty"` -} - -type Route struct { - Dst net.IPNet - GW net.IP -} - -func (r *Route) String() string { - return fmt.Sprintf("%+v", *r) -} - -// Well known error codes -// see https://github.com/containernetworking/cni/blob/master/SPEC.md#well-known-error-codes -const ( - ErrUnknown uint = iota // 0 - ErrIncompatibleCNIVersion // 1 - ErrUnsupportedField // 2 - ErrUnknownContainer // 3 - ErrInvalidEnvironmentVariables // 4 - ErrIOFailure // 5 - ErrDecodingFailure // 6 - ErrInvalidNetworkConfig // 7 - ErrTryAgainLater uint = 11 - ErrInternal uint = 999 -) - -type Error struct { - Code uint `json:"code"` - Msg string `json:"msg"` - Details string `json:"details,omitempty"` -} - -func NewError(code uint, msg, details string) *Error { - return &Error{ - Code: code, - Msg: msg, - Details: details, - } -} - -func (e *Error) Error() string { - details := "" - if e.Details != "" { - details = fmt.Sprintf("; %v", e.Details) - } - return fmt.Sprintf("%v%v", e.Msg, details) -} - -func (e *Error) Print() error { - return prettyPrint(e) -} - -// net.IPNet is not JSON (un)marshallable so this duality is needed -// for our custom IPNet type - -// JSON (un)marshallable types -type route struct { - Dst IPNet `json:"dst"` - GW net.IP `json:"gw,omitempty"` -} - -func (r *Route) UnmarshalJSON(data []byte) error { - rt := route{} - if err := json.Unmarshal(data, &rt); err != nil { - return err - } - - r.Dst = net.IPNet(rt.Dst) - r.GW = rt.GW - return nil -} - -func (r Route) MarshalJSON() ([]byte, error) { - rt := route{ - Dst: IPNet(r.Dst), - GW: r.GW, - } - - return json.Marshal(rt) -} - -func prettyPrint(obj interface{}) error { - data, err := json.MarshalIndent(obj, "", " ") - if err != nil { - return err - } - _, err = os.Stdout.Write(data) - return err -} diff --git a/kuryr_cni/vendor/github.com/containernetworking/cni/pkg/utils/utils.go b/kuryr_cni/vendor/github.com/containernetworking/cni/pkg/utils/utils.go deleted file mode 100644 index b8ec38874..000000000 --- a/kuryr_cni/vendor/github.com/containernetworking/cni/pkg/utils/utils.go +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright 2019 CNI authors -// -// 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. - -package utils - -import ( - "bytes" - "fmt" - "regexp" - "unicode" - - "github.com/containernetworking/cni/pkg/types" -) - -const ( - // cniValidNameChars is the regexp used to validate valid characters in - // containerID and networkName - cniValidNameChars = `[a-zA-Z0-9][a-zA-Z0-9_.\-]` - - // maxInterfaceNameLength is the length max of a valid interface name - maxInterfaceNameLength = 15 -) - -var cniReg = regexp.MustCompile(`^` + cniValidNameChars + `*$`) - -// ValidateContainerID will validate that the supplied containerID is not empty does not contain invalid characters -func ValidateContainerID(containerID string) *types.Error { - - if containerID == "" { - return types.NewError(types.ErrUnknownContainer, "missing containerID", "") - } - if !cniReg.MatchString(containerID) { - return types.NewError(types.ErrInvalidEnvironmentVariables, "invalid characters in containerID", containerID) - } - return nil -} - -// ValidateNetworkName will validate that the supplied networkName does not contain invalid characters -func ValidateNetworkName(networkName string) *types.Error { - - if networkName == "" { - return types.NewError(types.ErrInvalidNetworkConfig, "missing network name:", "") - } - if !cniReg.MatchString(networkName) { - return types.NewError(types.ErrInvalidNetworkConfig, "invalid characters found in network name", networkName) - } - return nil -} - -// ValidateInterfaceName will validate the interface name based on the three rules below -// 1. The name must not be empty -// 2. The name must be less than 16 characters -// 3. The name must not be "." or ".." -// 3. The name must not contain / or : or any whitespace characters -// ref to https://github.com/torvalds/linux/blob/master/net/core/dev.c#L1024 -func ValidateInterfaceName(ifName string) *types.Error { - if len(ifName) == 0 { - return types.NewError(types.ErrInvalidEnvironmentVariables, "interface name is empty", "") - } - if len(ifName) > maxInterfaceNameLength { - return types.NewError(types.ErrInvalidEnvironmentVariables, "interface name is too long", fmt.Sprintf("interface name should be less than %d characters", maxInterfaceNameLength+1)) - } - if ifName == "." || ifName == ".." { - return types.NewError(types.ErrInvalidEnvironmentVariables, "interface name is . or ..", "") - } - for _, r := range bytes.Runes([]byte(ifName)) { - if r == '/' || r == ':' || unicode.IsSpace(r) { - return types.NewError(types.ErrInvalidEnvironmentVariables, "interface name contains / or : or whitespace characters", "") - } - } - - return nil -} diff --git a/kuryr_cni/vendor/github.com/containernetworking/cni/pkg/version/conf.go b/kuryr_cni/vendor/github.com/containernetworking/cni/pkg/version/conf.go deleted file mode 100644 index 3cca58bbe..000000000 --- a/kuryr_cni/vendor/github.com/containernetworking/cni/pkg/version/conf.go +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2016 CNI authors -// -// 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. - -package version - -import ( - "encoding/json" - "fmt" -) - -// ConfigDecoder can decode the CNI version available in network config data -type ConfigDecoder struct{} - -func (*ConfigDecoder) Decode(jsonBytes []byte) (string, error) { - var conf struct { - CNIVersion string `json:"cniVersion"` - } - err := json.Unmarshal(jsonBytes, &conf) - if err != nil { - return "", fmt.Errorf("decoding version from network config: %s", err) - } - if conf.CNIVersion == "" { - return "0.1.0", nil - } - return conf.CNIVersion, nil -} diff --git a/kuryr_cni/vendor/github.com/containernetworking/cni/pkg/version/plugin.go b/kuryr_cni/vendor/github.com/containernetworking/cni/pkg/version/plugin.go deleted file mode 100644 index 1df427243..000000000 --- a/kuryr_cni/vendor/github.com/containernetworking/cni/pkg/version/plugin.go +++ /dev/null @@ -1,144 +0,0 @@ -// Copyright 2016 CNI authors -// -// 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. - -package version - -import ( - "encoding/json" - "fmt" - "io" - "strconv" - "strings" -) - -// PluginInfo reports information about CNI versioning -type PluginInfo interface { - // SupportedVersions returns one or more CNI spec versions that the plugin - // supports. If input is provided in one of these versions, then the plugin - // promises to use the same CNI version in its response - SupportedVersions() []string - - // Encode writes this CNI version information as JSON to the given Writer - Encode(io.Writer) error -} - -type pluginInfo struct { - CNIVersion_ string `json:"cniVersion"` - SupportedVersions_ []string `json:"supportedVersions,omitempty"` -} - -// pluginInfo implements the PluginInfo interface -var _ PluginInfo = &pluginInfo{} - -func (p *pluginInfo) Encode(w io.Writer) error { - return json.NewEncoder(w).Encode(p) -} - -func (p *pluginInfo) SupportedVersions() []string { - return p.SupportedVersions_ -} - -// PluginSupports returns a new PluginInfo that will report the given versions -// as supported -func PluginSupports(supportedVersions ...string) PluginInfo { - if len(supportedVersions) < 1 { - panic("programmer error: you must support at least one version") - } - return &pluginInfo{ - CNIVersion_: Current(), - SupportedVersions_: supportedVersions, - } -} - -// PluginDecoder can decode the response returned by a plugin's VERSION command -type PluginDecoder struct{} - -func (*PluginDecoder) Decode(jsonBytes []byte) (PluginInfo, error) { - var info pluginInfo - err := json.Unmarshal(jsonBytes, &info) - if err != nil { - return nil, fmt.Errorf("decoding version info: %s", err) - } - if info.CNIVersion_ == "" { - return nil, fmt.Errorf("decoding version info: missing field cniVersion") - } - if len(info.SupportedVersions_) == 0 { - if info.CNIVersion_ == "0.2.0" { - return PluginSupports("0.1.0", "0.2.0"), nil - } - return nil, fmt.Errorf("decoding version info: missing field supportedVersions") - } - return &info, nil -} - -// ParseVersion parses a version string like "3.0.1" or "0.4.5" into major, -// minor, and micro numbers or returns an error -func ParseVersion(version string) (int, int, int, error) { - var major, minor, micro int - if version == "" { - return -1, -1, -1, fmt.Errorf("invalid version %q: the version is empty", version) - } - - parts := strings.Split(version, ".") - if len(parts) >= 4 { - return -1, -1, -1, fmt.Errorf("invalid version %q: too many parts", version) - } - - major, err := strconv.Atoi(parts[0]) - if err != nil { - return -1, -1, -1, fmt.Errorf("failed to convert major version part %q: %v", parts[0], err) - } - - if len(parts) >= 2 { - minor, err = strconv.Atoi(parts[1]) - if err != nil { - return -1, -1, -1, fmt.Errorf("failed to convert minor version part %q: %v", parts[1], err) - } - } - - if len(parts) >= 3 { - micro, err = strconv.Atoi(parts[2]) - if err != nil { - return -1, -1, -1, fmt.Errorf("failed to convert micro version part %q: %v", parts[2], err) - } - } - - return major, minor, micro, nil -} - -// GreaterThanOrEqualTo takes two string versions, parses them into major/minor/micro -// numbers, and compares them to determine whether the first version is greater -// than or equal to the second -func GreaterThanOrEqualTo(version, otherVersion string) (bool, error) { - firstMajor, firstMinor, firstMicro, err := ParseVersion(version) - if err != nil { - return false, err - } - - secondMajor, secondMinor, secondMicro, err := ParseVersion(otherVersion) - if err != nil { - return false, err - } - - if firstMajor > secondMajor { - return true, nil - } else if firstMajor == secondMajor { - if firstMinor > secondMinor { - return true, nil - } else if firstMinor == secondMinor && firstMicro >= secondMicro { - return true, nil - } - } - return false, nil -} diff --git a/kuryr_cni/vendor/github.com/containernetworking/cni/pkg/version/reconcile.go b/kuryr_cni/vendor/github.com/containernetworking/cni/pkg/version/reconcile.go deleted file mode 100644 index 25c3810b2..000000000 --- a/kuryr_cni/vendor/github.com/containernetworking/cni/pkg/version/reconcile.go +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2016 CNI authors -// -// 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. - -package version - -import "fmt" - -type ErrorIncompatible struct { - Config string - Supported []string -} - -func (e *ErrorIncompatible) Details() string { - return fmt.Sprintf("config is %q, plugin supports %q", e.Config, e.Supported) -} - -func (e *ErrorIncompatible) Error() string { - return fmt.Sprintf("incompatible CNI versions: %s", e.Details()) -} - -type Reconciler struct{} - -func (r *Reconciler) Check(configVersion string, pluginInfo PluginInfo) *ErrorIncompatible { - return r.CheckRaw(configVersion, pluginInfo.SupportedVersions()) -} - -func (*Reconciler) CheckRaw(configVersion string, supportedVersions []string) *ErrorIncompatible { - for _, supportedVersion := range supportedVersions { - if configVersion == supportedVersion { - return nil - } - } - - return &ErrorIncompatible{ - Config: configVersion, - Supported: supportedVersions, - } -} diff --git a/kuryr_cni/vendor/github.com/containernetworking/cni/pkg/version/version.go b/kuryr_cni/vendor/github.com/containernetworking/cni/pkg/version/version.go deleted file mode 100644 index 8f3508e61..000000000 --- a/kuryr_cni/vendor/github.com/containernetworking/cni/pkg/version/version.go +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright 2016 CNI authors -// -// 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. - -package version - -import ( - "encoding/json" - "fmt" - - "github.com/containernetworking/cni/pkg/types" - "github.com/containernetworking/cni/pkg/types/020" - "github.com/containernetworking/cni/pkg/types/current" -) - -// Current reports the version of the CNI spec implemented by this library -func Current() string { - return "0.4.0" -} - -// Legacy PluginInfo describes a plugin that is backwards compatible with the -// CNI spec version 0.1.0. In particular, a runtime compiled against the 0.1.0 -// library ought to work correctly with a plugin that reports support for -// Legacy versions. -// -// Any future CNI spec versions which meet this definition should be added to -// this list. -var Legacy = PluginSupports("0.1.0", "0.2.0") -var All = PluginSupports("0.1.0", "0.2.0", "0.3.0", "0.3.1", "0.4.0") - -var resultFactories = []struct { - supportedVersions []string - newResult types.ResultFactoryFunc -}{ - {current.SupportedVersions, current.NewResult}, - {types020.SupportedVersions, types020.NewResult}, -} - -// Finds a Result object matching the requested version (if any) and asks -// that object to parse the plugin result, returning an error if parsing failed. -func NewResult(version string, resultBytes []byte) (types.Result, error) { - reconciler := &Reconciler{} - for _, resultFactory := range resultFactories { - err := reconciler.CheckRaw(version, resultFactory.supportedVersions) - if err == nil { - // Result supports this version - return resultFactory.newResult(resultBytes) - } - } - - return nil, fmt.Errorf("unsupported CNI result version %q", version) -} - -// ParsePrevResult parses a prevResult in a NetConf structure and sets -// the NetConf's PrevResult member to the parsed Result object. -func ParsePrevResult(conf *types.NetConf) error { - if conf.RawPrevResult == nil { - return nil - } - - resultBytes, err := json.Marshal(conf.RawPrevResult) - if err != nil { - return fmt.Errorf("could not serialize prevResult: %v", err) - } - - conf.RawPrevResult = nil - conf.PrevResult, err = NewResult(conf.CNIVersion, resultBytes) - if err != nil { - return fmt.Errorf("could not parse prevResult: %v", err) - } - - return nil -} diff --git a/kuryr_cni/vendor/modules.txt b/kuryr_cni/vendor/modules.txt deleted file mode 100644 index f1c720678..000000000 --- a/kuryr_cni/vendor/modules.txt +++ /dev/null @@ -1,12 +0,0 @@ -# github.com/containernetworking/cni v0.8.1 -## explicit -github.com/containernetworking/cni/pkg/skel -github.com/containernetworking/cni/pkg/types -github.com/containernetworking/cni/pkg/types/020 -github.com/containernetworking/cni/pkg/types/current -github.com/containernetworking/cni/pkg/utils -github.com/containernetworking/cni/pkg/version -# github.com/onsi/ginkgo v1.16.1 -## explicit -# github.com/onsi/gomega v1.11.0 -## explicit diff --git a/kuryr_kubernetes/__init__.py b/kuryr_kubernetes/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/kuryr_kubernetes/clients.py b/kuryr_kubernetes/clients.py deleted file mode 100644 index 2a3635db0..000000000 --- a/kuryr_kubernetes/clients.py +++ /dev/null @@ -1,116 +0,0 @@ -# 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 ipaddress -import os - -from keystoneauth1 import session as k_session -from kuryr.lib import utils -from openstack import connection -from openstack import exceptions as os_exc - -from kuryr_kubernetes import config -from kuryr_kubernetes import k8s_client - -_clients = {} -_NEUTRON_CLIENT = 'neutron-client' -_KUBERNETES_CLIENT = 'kubernetes-client' -_OPENSTACKSDK = 'openstacksdk' - - -def get_network_client(): - return _clients[_OPENSTACKSDK].network - - -def get_loadbalancer_client(): - return _clients[_OPENSTACKSDK].load_balancer - - -def get_kubernetes_client() -> k8s_client.K8sClient: - return _clients[_KUBERNETES_CLIENT] - - -def get_compute_client(): - return _clients[_OPENSTACKSDK].compute - - -def setup_clients(): - setup_kubernetes_client() - setup_openstacksdk() - - -def setup_kubernetes_client(): - if config.CONF.kubernetes.api_root: - api_root = config.CONF.kubernetes.api_root - else: - # NOTE(dulek): This is for containerized deployments, i.e. running in - # K8s Pods. - host = os.environ['KUBERNETES_SERVICE_HOST'] - port = os.environ['KUBERNETES_SERVICE_PORT_HTTPS'] - try: - addr = ipaddress.ip_address(host) - if addr.version == 6: - host = '[%s]' % host - except ValueError: - # It's not an IP addres but a hostname, it's fine, move along. - pass - api_root = "https://%s:%s" % (host, port) - _clients[_KUBERNETES_CLIENT] = k8s_client.K8sClient(api_root) - - -def get_neutron_error_type(ex): - try: - response = ex.response.json() - except (ValueError, AttributeError): - return None - - if response: - try: - return response['NeutronError']['type'] - except KeyError: - pass - return None - - -def handle_neutron_errors(method, *args, **kwargs): - """Handle errors on openstacksdk router methods""" - result = method(*args, **kwargs) - if 'NeutronError' in result: - error = result['NeutronError'] - if error['type'] in ('RouterNotFound', - 'RouterInterfaceNotFoundForSubnet', - 'SubnetNotFound'): - raise os_exc.NotFoundException(message=error['message']) - else: - raise os_exc.SDKException(error['type'] + ": " + error['message']) - - return result - - -def setup_openstacksdk(): - auth_plugin = utils.get_auth_plugin('neutron') - session = utils.get_keystone_session('neutron', auth_plugin) - - # NOTE(mdulko): To get rid of warnings about connection pool being full - # we need to "tweak" the keystoneauth's adapters increasing - # the maximum pool size. - for scheme in list(session.session.adapters): - session.session.mount(scheme, k_session.TCPKeepAliveAdapter( - pool_maxsize=1000)) - - conn = connection.Connection( - session=session, - region_name=getattr(config.CONF.neutron, 'region_name', None)) - _clients[_OPENSTACKSDK] = conn diff --git a/kuryr_kubernetes/cmd/__init__.py b/kuryr_kubernetes/cmd/__init__.py deleted file mode 100644 index 172274c13..000000000 --- a/kuryr_kubernetes/cmd/__init__.py +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright 2017 NEC Corporation. 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 logging as sys_logging - -from oslo_reports import guru_meditation_report as gmr - -from kuryr_kubernetes import version - -# During the call to gmr.TextGuruMeditation.setup_autorun(), Guru Meditation -# Report tries to start logging. Set a handler here to accommodate this. -logger = sys_logging.getLogger(None) -if not logger.handlers: - logger.addHandler(sys_logging.StreamHandler()) - -_version_string = version.version_info.release_string() -gmr.TextGuruMeditation.setup_autorun(version=_version_string) diff --git a/kuryr_kubernetes/cmd/cni.py b/kuryr_kubernetes/cmd/cni.py deleted file mode 100644 index f19f30f6f..000000000 --- a/kuryr_kubernetes/cmd/cni.py +++ /dev/null @@ -1,22 +0,0 @@ -# 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. - -from kuryr_kubernetes.cni import main - - -run = main.run - -if __name__ == '__main__': - run() diff --git a/kuryr_kubernetes/cmd/daemon.py b/kuryr_kubernetes/cmd/daemon.py deleted file mode 100644 index 0f572ad6d..000000000 --- a/kuryr_kubernetes/cmd/daemon.py +++ /dev/null @@ -1,22 +0,0 @@ -# Copyright 2017 NEC Technologies India Pvt. Ltd. -# 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. - -from kuryr_kubernetes.cni.daemon import service - - -start = service.start - -if __name__ == '__main__': - start() diff --git a/kuryr_kubernetes/cmd/eventlet/__init__.py b/kuryr_kubernetes/cmd/eventlet/__init__.py deleted file mode 100644 index db423fc99..000000000 --- a/kuryr_kubernetes/cmd/eventlet/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -# 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 eventlet - -eventlet.monkey_patch() diff --git a/kuryr_kubernetes/cmd/eventlet/controller.py b/kuryr_kubernetes/cmd/eventlet/controller.py deleted file mode 100644 index ef560d4ea..000000000 --- a/kuryr_kubernetes/cmd/eventlet/controller.py +++ /dev/null @@ -1,22 +0,0 @@ -# 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. - -from kuryr_kubernetes.controller import service - - -start = service.start - -if __name__ == '__main__': - start() diff --git a/kuryr_kubernetes/cmd/sanity/__init__.py b/kuryr_kubernetes/cmd/sanity/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/kuryr_kubernetes/cmd/sanity/checks.py b/kuryr_kubernetes/cmd/sanity/checks.py deleted file mode 100644 index 9c3c9b009..000000000 --- a/kuryr_kubernetes/cmd/sanity/checks.py +++ /dev/null @@ -1,85 +0,0 @@ -# Copyright (c) 2021 OpenStack Foundation. -# 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. - - -from oslo_config import cfg -from oslo_log import log as logging - -from kuryr_kubernetes import config - -CONF = config.CONF -LOG = logging.getLogger(__name__) - - -def _logger(): - if cfg.CONF.sanity_check_error: - return LOG.error - else: - return LOG.warning - - -def ports_pool_min_max(): - try: - if not cfg.CONF.vif_pool.ports_pool_max: - return True - pool_max = cfg.CONF.vif_pool.ports_pool_max - pool_min = cfg.CONF.vif_pool.ports_pool_min - if pool_max < pool_min: - _logger()(f'The current configuration of ports_pool_min ' - f'"{pool_min}" and ports_pool_max "{pool_max}" ' - f'may cause infinite loop of creating ' - f'and deleting ports.') - return False - except (OSError, RuntimeError, IndexError, ValueError) as e: - LOG.debug("Exception while checking ports_pool_max. " - "Exception: %s", e) - return False - return True - - -def ports_pool_min_batch(): - try: - pool_min = cfg.CONF.vif_pool.ports_pool_min - pool_batch = cfg.CONF.vif_pool.ports_pool_batch - if pool_min > pool_batch: - _logger()(f'The current configuration of ports_pool_min ' - f'"{pool_min}" and ports_pool_batch "{pool_batch}" ' - f'may cause kuryr to send multiple unnecessary ' - f'bulk ports creation requests. ') - return False - except (OSError, RuntimeError, IndexError, ValueError) as e: - LOG.debug("Exception while checking ports_pool_batch. " - "Exception: %s", e) - return False - return True - - -def ports_pool_max_batch(): - try: - if not cfg.CONF.vif_pool.ports_pool_max: - return True - pool_max = cfg.CONF.vif_pool.ports_pool_max - pool_batch = cfg.CONF.vif_pool.ports_pool_batch - if pool_max < pool_batch: - _logger()(f'The current configuration of ports_pool_max ' - f'"{pool_max}" and ports_pool_batch "{pool_batch}" ' - f'may cause kuryr to create the ' - f'ports and then delete them immediately.') - return False - except (OSError, RuntimeError, IndexError, ValueError) as e: - LOG.debug("Exception while checking ports_pool_batch. " - "Exception: %s", e) - return False - return True diff --git a/kuryr_kubernetes/cmd/sanity_checks.py b/kuryr_kubernetes/cmd/sanity_checks.py deleted file mode 100644 index 237b9b02d..000000000 --- a/kuryr_kubernetes/cmd/sanity_checks.py +++ /dev/null @@ -1,105 +0,0 @@ -# Copyright (c) 2021 OpenStack Foundation. -# 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. - -""" -CLI interface for kuryr sanity commands. -""" - -import sys - -from oslo_config import cfg -from oslo_log import log as logging - -from kuryr_kubernetes.cmd.sanity import checks -from kuryr_kubernetes import config -from kuryr_kubernetes.controller.drivers import vif_pool # noqa - -LOG = logging.getLogger(__name__) - - -class BoolOptCallback(cfg.BoolOpt): - def __init__(self, name, callback, **kwargs): - if 'default' not in kwargs: - kwargs['default'] = False - self.callback = callback - super(BoolOptCallback, self).__init__(name, **kwargs) - - -def check_ports_pool_min_max(): - result = checks.ports_pool_min_max() - if not result: - LOG.warning("The ports_pool_max is enabled, " - "the ports_pool_min should be smaller than " - "ports_pool_max. Either disable ports_pool_max " - "setting it to 0 or increase it's value.") - return result - - -def check_ports_pool_min_batch(): - result = checks.ports_pool_min_batch() - if not result: - LOG.warning("The ports_pool_min should be lower than " - "ports_pool_batch. Please decrease it's value.") - return result - - -def check_ports_pool_max_batch(): - result = checks.ports_pool_max_batch() - if not result: - LOG.warning("The ports_pool_max is enabled, " - "the ports_pool_max should be higher than " - "ports_pool_batch. Either disable ports_pool_max " - "setting it to 0 or decrease it's value.") - return result - - -# Define CLI opts to test specific features, with a callback for the test -OPTS = [ - BoolOptCallback('vif_pool_min_max', check_ports_pool_min_max, - default=False, - help='Check configuration sanity of ports_pool_min and ' - 'ports_pool_max.'), - BoolOptCallback('vif_pool_min_batch', check_ports_pool_min_batch, - default=False, - help='Check configuration sanity of ports_pool_min and ' - 'ports_pool_batch.'), - BoolOptCallback('vif_pool_max_batch', check_ports_pool_max_batch, - default=False, - help='Check configuration sanity of ports_pool_max and ' - 'ports_pool_batch.'), -] - -CLI_OPTS = [ - cfg.BoolOpt('sanity_check_error', default=False, - help='If this flag is configured, the sanity command fails ' - 'if any of the sanity tests fails.'), -] - - -def all_tests_passed(): - results = [opt.callback() for opt in OPTS if cfg.CONF.get(opt.name)] - return all(results) - - -def main(): - cfg.CONF.register_cli_opts(OPTS) - cfg.CONF.register_cli_opts(CLI_OPTS) - config.init(sys.argv[1:], default_config_files=['/etc/kuryr/kuryr.conf']) - config.setup_logging() - return 0 if all_tests_passed() else 1 - - -if __name__ == '__main__': - main() diff --git a/kuryr_kubernetes/cmd/status.py b/kuryr_kubernetes/cmd/status.py deleted file mode 100644 index 6b2f0636b..000000000 --- a/kuryr_kubernetes/cmd/status.py +++ /dev/null @@ -1,204 +0,0 @@ -# Copyright 2018 Red Hat -# -# 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. - -""" -CLI interface for kuryr status commands. -""" - -import sys -import textwrap -import traceback - -import prettytable - -import os_vif -from os_vif.objects import base -from oslo_config import cfg -from oslo_serialization import jsonutils - -from kuryr_kubernetes import clients -from kuryr_kubernetes import config -from kuryr_kubernetes import constants -from kuryr_kubernetes import objects -from kuryr_kubernetes import version - -CONF = config.CONF - -UPGRADE_CHECK_SUCCESS = 0 -UPGRADE_CHECK_WARNING = 1 -UPGRADE_CHECK_FAILURE = 2 - -UPGRADE_CHECK_MSG_MAP = { - UPGRADE_CHECK_SUCCESS: 'Success', - UPGRADE_CHECK_WARNING: 'Warning', - UPGRADE_CHECK_FAILURE: 'Failure', -} - - -class UpgradeCheckResult(object): - """Class used for 'kuryr-k8s-status upgrade check' results. - - The 'code' attribute is an UpgradeCheckCode enum. - The 'details' attribute is a message generally only used for - checks that result in a warning or failure code. The details should provide - information on what issue was discovered along with any remediation. - """ - - def __init__(self, code, details=None): - super(UpgradeCheckResult, self).__init__() - self.code = code - self.details = details - - def get_details(self): - if self.details is not None: - # wrap the text on the details to 60 characters - return '\n'.join(textwrap.wrap(self.details, 60, - subsequent_indent=' ' * 9)) - - -class UpgradeCommands(object): - def __init__(self): - self.check_methods = { - 'Pod annotations': self._check_annotations, # Stein - } - clients.setup_kubernetes_client() - self.k8s = clients.get_kubernetes_client() - - def _get_annotation(self, pod): - annotations = pod['metadata']['annotations'] - if constants.K8S_ANNOTATION_VIF not in annotations: - # NOTE(dulek): We ignore pods without annotation, those - # probably are hostNetworking. - return None - k_ann = annotations[constants.K8S_ANNOTATION_VIF] - k_ann = jsonutils.loads(k_ann) - obj = base.VersionedObject.obj_from_primitive(k_ann) - return obj - - def _check_annotations(self): - old_count = 0 - malformed_count = 0 - pods = self.k8s.get('/api/v1/pods')['items'] - for pod in pods: - try: - obj = self._get_annotation(pod) - if not obj: - # NOTE(dulek): We ignore pods without annotation, those - # probably are hostNetworking. - continue - except Exception: - # TODO(dulek): We might want to print this exception. - malformed_count += 1 - continue - - if obj.obj_name() != objects.vif.PodState.obj_name(): - old_count += 1 - - if malformed_count == 0 and old_count == 0: - return UpgradeCheckResult(0, 'All annotations are updated.') - elif malformed_count > 0 and old_count == 0: - msg = ('You have %d malformed Kuryr pod annotations in your ' - 'deployment. This is not blocking the upgrade, but ' - 'consider investigating it.' % malformed_count) - return UpgradeCheckResult(1, msg) - elif old_count > 0: - msg = ('You have %d Kuryr pod annotations in old format. You need ' - 'to run `kuryr-k8s-status upgrade update-annotations` ' - 'before proceeding with the upgrade.' % old_count) - return UpgradeCheckResult(2, msg) - - def upgrade_check(self): - check_results = [] - - t = prettytable.PrettyTable(['Upgrade Check Results'], - hrules=prettytable.ALL) - t.align = 'l' - - for name, method in self.check_methods.items(): - result = method() - check_results.append(result) - cell = ( - 'Check: %(name)s\n' - 'Result: %(result)s\n' - 'Details: %(details)s' % - { - 'name': name, - 'result': UPGRADE_CHECK_MSG_MAP[result.code], - 'details': result.get_details(), - } - ) - t.add_row([cell]) - print(t) - - return max(res.code for res in check_results) - - def update_annotations(self): - pass - - def downgrade_annotations(self): - pass - - -def print_version(): - print(version.version_info.version_string()) - - -def add_parsers(subparsers): - upgrade_cmds = UpgradeCommands() - - upgrade = subparsers.add_parser( - 'upgrade', help='Actions related to upgrades between releases.') - sub = upgrade.add_subparsers() - - check = sub.add_parser('check', help='Check if upgrading is possible.') - check.set_defaults(action_fn=upgrade_cmds.upgrade_check) - - ann_update = sub.add_parser( - 'update-annotations', - help='Update annotations in K8s API to newest version.') - ann_update.set_defaults(action_fn=upgrade_cmds.update_annotations) - - ann_downgrade = sub.add_parser( - 'downgrade-annotations', - help='Downgrade annotations in K8s API to previous version (useful ' - 'when reverting a failed upgrade).') - ann_downgrade.set_defaults(action_fn=upgrade_cmds.downgrade_annotations) - - version_action = subparsers.add_parser('version') - version_action.set_defaults(action_fn=print_version) - - -def main(): - opt = cfg.SubCommandOpt( - 'category', title='command', - description='kuryr-k8s-status command or category to execute', - handler=add_parsers) - - conf = cfg.ConfigOpts() - conf.register_cli_opt(opt) - conf(sys.argv[1:]) - - os_vif.initialize() - objects.register_locally_defined_vifs() - - try: - return conf.category.action_fn() - except Exception: - print('Error:\n%s' % traceback.format_exc()) - # This is 255 so it's not confused with the upgrade check exit codes. - return 255 - - -if __name__ == '__main__': - main() diff --git a/kuryr_kubernetes/cni/__init__.py b/kuryr_kubernetes/cni/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/kuryr_kubernetes/cni/api.py b/kuryr_kubernetes/cni/api.py deleted file mode 100644 index b5d696522..000000000 --- a/kuryr_kubernetes/cni/api.py +++ /dev/null @@ -1,169 +0,0 @@ -# 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 -from http import client as httplib -import traceback - -from kuryr.lib._i18n import _ -from os_vif.objects import base -from oslo_log import log as logging -from oslo_serialization import jsonutils -import requests - -from kuryr_kubernetes import config -from kuryr_kubernetes import constants as k_const -from kuryr_kubernetes import exceptions as k_exc - -LOG = logging.getLogger(__name__) - - -class CNIRunner(object, metaclass=abc.ABCMeta): - # TODO(ivc): extend SUPPORTED_VERSIONS and format output based on - # requested params.CNI_VERSION and/or params.config.cniVersion - VERSION = '0.3.1' - SUPPORTED_VERSIONS = ['0.3.1'] - - @abc.abstractmethod - def _add(self, params): - raise NotImplementedError() - - @abc.abstractmethod - def _delete(self, params): - raise NotImplementedError() - - 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}) - - @abc.abstractmethod - def prepare_env(self, env, stdin): - raise NotImplementedError() - - @abc.abstractmethod - def get_container_id(self, params): - raise NotImplementedError() - - def run(self, env, fin, fout): - try: - # Prepare params according to calling Object - params = self.prepare_env(env, fin) - if env.get('CNI_COMMAND') == 'ADD': - vif = self._add(params) - self._write_dict(fout, vif) - elif env.get('CNI_COMMAND') == 'DEL': - self._delete(params) - elif env.get('CNI_COMMAND') == 'VERSION': - self._write_version(fout) - else: - raise k_exc.CNIError(_("unknown CNI_COMMAND: %s") - % env['CNI_COMMAND']) - return 0 - except Exception as ex: - # LOG.exception - self._write_exception(fout, str(ex)) - return 1 - - def _vif_data(self, vif, params): - result = {} - nameservers = [] - - cni_ip_list = result.setdefault("ips", []) - cni_routes_list = result.setdefault("routes", []) - result["interfaces"] = [ - { - "name": params["CNI_IFNAME"], - "mac": vif.address, - "sandbox": self.get_container_id(params)}] - for subnet in vif.network.subnets.objects: - cni_ip = {} - nameservers.extend(subnet.dns) - - ip = subnet.ips.objects[0].address - - cni_ip['version'] = str(ip.version) - cni_ip['address'] = "%s/%s" % (ip, subnet.cidr.prefixlen) - cni_ip['interface'] = len(result["interfaces"]) - 1 - - if hasattr(subnet, 'gateway'): - cni_ip['gateway'] = str(subnet.gateway) - - if subnet.routes.objects: - routes = [ - {'dst': str(route.cidr), 'gw': str(route.gateway)} - for route in subnet.routes.objects] - cni_routes_list.extend(routes) - cni_ip_list.append(cni_ip) - - if nameservers: - result['dns'] = {'nameservers': nameservers} - return result - - -class CNIDaemonizedRunner(CNIRunner): - - def _add(self, params): - resp = self._make_request('addNetwork', params, httplib.ACCEPTED) - vif = base.VersionedObject.obj_from_primitive(resp.json()) - return self._vif_data(vif, params) - - def _delete(self, params): - self._make_request('delNetwork', params, httplib.NO_CONTENT) - - def prepare_env(self, env, stdin): - cni_envs = {} - cni_envs.update( - {k: v for k, v in env.items() if k.startswith('CNI_')}) - cni_envs['config_kuryr'] = dict(stdin) - return cni_envs - - def get_container_id(self, params): - return params["CNI_CONTAINERID"] - - def _make_request(self, path, cni_envs, expected_status=None): - method = 'POST' - - address = config.CONF.cni_daemon.bind_address - url = 'http://%s/%s' % (address, path) - try: - LOG.debug('Making request to CNI Daemon. %(method)s %(path)s\n' - '%(body)s', - {'method': method, 'path': url, 'body': cni_envs}) - resp = requests.post(url, json=cni_envs, - headers={'Connection': 'close'}) - except requests.ConnectionError: - LOG.exception('Looks like %s cannot be reached. Is kuryr-daemon ' - 'running?', address) - raise - LOG.debug('CNI Daemon returned "%(status)d %(reason)s".', - {'status': resp.status_code, 'reason': resp.reason}) - if expected_status and resp.status_code != expected_status: - LOG.error('CNI daemon returned error "%(status)d %(reason)s".', - {'status': resp.status_code, 'reason': resp.reason}) - raise k_exc.CNIError('Got invalid status code from CNI daemon.') - return resp diff --git a/kuryr_kubernetes/cni/binding/__init__.py b/kuryr_kubernetes/cni/binding/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/kuryr_kubernetes/cni/binding/base.py b/kuryr_kubernetes/cni/binding/base.py deleted file mode 100644 index b46b5e55f..000000000 --- a/kuryr_kubernetes/cni/binding/base.py +++ /dev/null @@ -1,166 +0,0 @@ -# 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 errno - -import os_vif -from os_vif.objects import vif as osv_objects -from oslo_log import log as logging -import pyroute2 -from pyroute2 import netns as pyroute_netns -from stevedore import driver as stv_driver - -from kuryr_kubernetes.cni import utils as cni_utils -from kuryr_kubernetes import utils - -_BINDING_NAMESPACE = 'kuryr_kubernetes.cni.binding' -LOG = logging.getLogger(__name__) - - -class BaseBindingDriver(object, metaclass=abc.ABCMeta): - """Interface to attach ports to pods.""" - - def _remove_ifaces(self, ipdb, ifnames, netns='host'): - """Check if any of `ifnames` exists and remove it. - - :param ipdb: ipdb of the network namespace to check - :param ifnames: iterable of interface names to remove - :param netns: network namespace name (used for logging) - """ - for ifname in ifnames: - if ifname in ipdb.interfaces: - LOG.warning('Found hanging interface %(ifname)s inside ' - '%(netns)s netns. Most likely it is a leftover ' - 'from a kuryr-daemon restart. Trying to delete ' - 'it.', {'ifname': ifname, 'netns': netns}) - with ipdb.interfaces[ifname] as iface: - iface.remove() - - @abc.abstractmethod - def connect(self, vif, ifname, netns, container_id): - raise NotImplementedError() - - @abc.abstractmethod - def disconnect(self, vif, ifname, netns, container_id): - raise NotImplementedError() - - -def _get_binding_driver(vif): - mgr = stv_driver.DriverManager(namespace=_BINDING_NAMESPACE, - name=type(vif).__name__, - invoke_on_load=True) - return mgr.driver - - -def get_ipdb(netns=None): - if netns: - netns = utils.convert_netns(netns) - ipdb = pyroute2.IPDB(nl=pyroute2.NetNS(netns)) - else: - ipdb = pyroute2.IPDB() - return ipdb - - -def _enable_ipv6(netns): - # Docker disables IPv6 for --net=none containers - # TODO(apuimedo) remove when it is no longer the case - try: - netns = utils.convert_netns(netns) - path = utils.convert_netns('/proc/self/ns/net') - self_ns_fd = open(path) - pyroute_netns.setns(netns) - path = utils.convert_netns('/proc/sys/net/ipv6/conf/all/disable_ipv6') - with open(path, 'w') as disable_ipv6: - disable_ipv6.write('0') - except Exception: - raise - finally: - pyroute_netns.setns(self_ns_fd) - - -def _configure_l3(vif, ifname, netns, is_default_gateway): - with get_ipdb(netns) as ipdb: - with ipdb.interfaces[ifname] as iface: - for subnet in vif.network.subnets.objects: - if subnet.cidr.version == 6: - _enable_ipv6(netns) - for fip in subnet.ips.objects: - iface.add_ip('%s/%s' % (fip.address, - subnet.cidr.prefixlen)) - - routes = ipdb.routes - for subnet in vif.network.subnets.objects: - for route in subnet.routes.objects: - routes.add(gateway=str(route.gateway), - dst=str(route.cidr)).commit() - if is_default_gateway and hasattr(subnet, 'gateway'): - try: - routes.add(gateway=str(subnet.gateway), - dst='default').commit() - except pyroute2.NetlinkError as ex: - if ex.code != errno.EEXIST: - raise - LOG.debug("Default route already exists in pod for vif=%s." - " Did not overwrite with requested gateway=%s", - vif, subnet.gateway) - - -def _need_configure_l3(vif): - if isinstance(vif, osv_objects.VIFVHostUser): - return False - if not hasattr(vif, 'physnet'): - # NOTE(danil): non-sriov vif. Figure out if it is nested-dpdk - if vif.obj_attr_is_set('port_profile') and hasattr(vif.port_profile, - 'l3_setup'): - return vif.port_profile.l3_setup - # NOTE(danil): by default kuryr-kubernetes has to setup l3 - return True - return True - - -@cni_utils.log_ipdb -def connect(vif, instance_info, ifname, netns=None, report_health=None, - is_default_gateway=True, container_id=None): - driver = _get_binding_driver(vif) - if report_health: - report_health(driver.is_alive()) - os_vif.plug(vif, instance_info) - driver.connect(vif, ifname, netns, container_id) - if _need_configure_l3(vif): - _configure_l3(vif, ifname, netns, is_default_gateway) - - -@cni_utils.log_ipdb -def disconnect(vif, instance_info, ifname, netns=None, report_health=None, - container_id=None, **kwargs): - driver = _get_binding_driver(vif) - if report_health: - report_health(driver.is_alive()) - driver.disconnect(vif, ifname, netns, container_id) - os_vif.unplug(vif, instance_info) - - -@cni_utils.log_ipdb -def cleanup(ifname, netns): - try: - with get_ipdb(netns) as c_ipdb: - if ifname in c_ipdb.interfaces: - with c_ipdb.interfaces[ifname] as iface: - iface.remove() - except Exception: - # Just ignore cleanup errors, there's not much we can do anyway. - LOG.warning('Error occured when attempting to clean up netns %s. ' - 'Ignoring.', netns) diff --git a/kuryr_kubernetes/cni/binding/bridge.py b/kuryr_kubernetes/cni/binding/bridge.py deleted file mode 100644 index 06a36f238..000000000 --- a/kuryr_kubernetes/cni/binding/bridge.py +++ /dev/null @@ -1,115 +0,0 @@ -# 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 os -from oslo_config import cfg -from oslo_log import log - -from kuryr_kubernetes.cni.binding import base as b_base -from kuryr_kubernetes.handlers import health -from kuryr_kubernetes import linux_net_utils as net_utils - -LOG = log.getLogger(__name__) -CONF = cfg.CONF - - -class BaseBridgeDriver(health.HealthHandler, b_base.BaseBindingDriver): - - def __init__(self): - super(BaseBridgeDriver, self).__init__() - - def connect(self, vif, ifname, netns, container_id): - host_ifname = vif.vif_name - - # NOTE(dulek): Check if we already run connect for this iface and if - # there's a leftover host-side vif. If so we need to - # remove it, its peer should get deleted automatically by - # the kernel. - with b_base.get_ipdb() as h_ipdb: - self._remove_ifaces(h_ipdb, (host_ifname,)) - - interface_mtu = vif.network.mtu - mtu_cfg = CONF.neutron_defaults.network_device_mtu - if mtu_cfg and mtu_cfg < interface_mtu: - interface_mtu = CONF.neutron_defaults.network_device_mtu - - with b_base.get_ipdb(netns) as c_ipdb: - with c_ipdb.create(ifname=ifname, peer=host_ifname, - kind='veth') as c_iface: - c_iface.mtu = interface_mtu - c_iface.address = str(vif.address) - c_iface.up() - - if netns: - with c_ipdb.interfaces[host_ifname] as h_iface: - h_iface.net_ns_pid = os.getpid() - - with b_base.get_ipdb() as h_ipdb: - with h_ipdb.interfaces[host_ifname] as h_iface: - h_iface.mtu = interface_mtu - h_iface.up() - - def disconnect(self, vif, ifname, netns, container_id): - pass - - -class BridgeDriver(BaseBridgeDriver): - def __init__(self): - super(BridgeDriver, self).__init__() - - def connect(self, vif, ifname, netns, container_id): - super(BridgeDriver, self).connect(vif, ifname, netns, container_id) - host_ifname = vif.vif_name - bridge_name = vif.bridge_name - - with b_base.get_ipdb() as h_ipdb: - with h_ipdb.interfaces[bridge_name] as h_br: - h_br.add_port(host_ifname) - - def disconnect(self, vif, ifname, netns, container_id): - # NOTE(ivc): veth pair is destroyed automatically along with the - # container namespace - pass - - -class VIFOpenVSwitchDriver(BaseBridgeDriver): - - def __init__(self): - super(VIFOpenVSwitchDriver, self).__init__() - - def connect(self, vif, ifname, netns, container_id): - super(VIFOpenVSwitchDriver, self).connect(vif, ifname, netns, - container_id) - # FIXME(irenab) use pod_id (neutron port device_id) - instance_id = 'kuryr' - net_utils.create_ovs_vif_port(vif.bridge_name, vif.vif_name, - vif.port_profile.interface_id, - vif.address, instance_id) - - def disconnect(self, vif, ifname, netns, container_id): - super(VIFOpenVSwitchDriver, self).disconnect(vif, ifname, netns, - container_id) - net_utils.delete_ovs_vif_port(vif.bridge_name, vif.vif_name) - - def is_alive(self): - bridge_name = CONF.neutron_defaults.ovs_bridge - try: - with b_base.get_ipdb() as h_ipdb: - h_ipdb.interfaces[bridge_name] - return True - except Exception: - LOG.error("The configured ovs_bridge=%s integration interface " - "does not exists. Reporting that driver is not healthy.", - bridge_name) - return False diff --git a/kuryr_kubernetes/cni/binding/dpdk.py b/kuryr_kubernetes/cni/binding/dpdk.py deleted file mode 100644 index a95b0459d..000000000 --- a/kuryr_kubernetes/cni/binding/dpdk.py +++ /dev/null @@ -1,196 +0,0 @@ -# Copyright (C) 2020 Intel Corporation -# 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 os - -from os_vif import objects -from oslo_config import cfg -from oslo_log import log as logging -from oslo_serialization import jsonutils - -from kuryr_kubernetes import clients -from kuryr_kubernetes.cni.binding import base as b_base -from kuryr_kubernetes import constants -from kuryr_kubernetes.handlers import health - -from kuryr.lib._i18n import _ - - -LOG = logging.getLogger(__name__) -CONF = cfg.CONF - -NET_DEV_PATH = "/sys/class/net/{}/device" -VIRTIO_DEVS_PATH = "/sys/bus/virtio/devices" -PCI_PATH = "/sys/bus/pci/devices" -PCI_DRVS_PATH = "/sys/bus/pci/drivers" - - -# TODO(garyloug) These should probably eventually move to config.py -# TODO(garyloug) Would be nice if dpdk_driver is set as CNI arg -nested_dpdk_opts = [ - cfg.StrOpt('dpdk_driver', - help=_('The DPDK driver that the device will be bound to after ' - 'it is unbound from the kernel driver'), - default='uio_pci_generic'), - cfg.StrOpt('pci_mount_point', - help=_('Absolute path to directory containing pci address of ' - 'devices to be used by DPDK application'), - default='/var/pci_address'), -] - -CONF.register_opts(nested_dpdk_opts, "nested_dpdk") - - -class DpdkDriver(health.HealthHandler, b_base.BaseBindingDriver): - - def __init__(self): - super(DpdkDriver, self).__init__() - - def connect(self, vif, ifname, netns, container_id): - name = self._get_iface_name_by_mac(vif.address) - driver, pci_addr = self._get_device_info(name) - - vif.dev_driver = driver - vif.pci_address = pci_addr - dpdk_driver = CONF.nested_dpdk.dpdk_driver - self._change_driver_binding(pci_addr, dpdk_driver) - self._create_pci_file(pci_addr, container_id, ifname) - self._set_vif(vif) - - def disconnect(self, vif, ifname, netns, container_id): - self._remove_pci_file(container_id, ifname) - - def _get_iface_name_by_mac(self, mac_address): - with b_base.get_ipdb() as h_ipdb: - for name, data in h_ipdb.interfaces.items(): - if data['address'] == mac_address: - return data['ifname'] - - def _get_device_info(self, ifname): - """Get driver and PCI addr by using sysfs""" - - # TODO(garyloug): check the type (virtio) - dev = os.path.basename(os.readlink(NET_DEV_PATH.format(ifname))) - pci_link = os.readlink(os.path.join(VIRTIO_DEVS_PATH, dev)) - pci_addr = os.path.basename(os.path.dirname(pci_link)) - pci_driver_link = os.readlink(os.path.join(PCI_PATH, pci_addr, - 'driver')) - pci_driver = os.path.basename(pci_driver_link) - - return pci_driver, pci_addr - - def _change_driver_binding(self, pci, driver): - old_driver_path = os.path.join(PCI_PATH, pci, 'driver') - old_driver_link = os.readlink(old_driver_path) - old_driver = os.path.basename(old_driver_link) - - unbind_path = os.path.join(PCI_DRVS_PATH, old_driver, 'unbind') - bind_path = os.path.join(PCI_DRVS_PATH, driver, 'bind') - - with open(unbind_path, 'w') as unbind_fd: - unbind_fd.write(pci) - - override = os.path.join(PCI_PATH, pci, 'driver_override') - # NOTE(danil): to change driver for device it is necessary to - # write the name of this driver into override_fd. Before that - # Null should be written there. This process is described properly - # in dpdk-devbind.py script by DPDK - with open(override, 'w') as override_fd: - override_fd.write("\00") - - with open(override, 'w') as override_fd: - override_fd.write(driver) - - with open(bind_path, 'w') as bind_fd: - bind_fd.write(pci) - - LOG.info("Device %s was binded on driver %s. Old driver is %s", pci, - driver, old_driver) - - def _create_pci_file(self, pci_addr, container_id, ifname): - # NOTE(danil): writing used pci addresses is necessary to know what - # device to use by dpdk applications inside containers - try: - os.makedirs(CONF.nested_dpdk.pci_mount_point, exists_ok=True) - file_path = os.path.join(CONF.nested_dpdk.pci_mount_point, - container_id + '-' + ifname) - with open(file_path, 'w') as fd: - fd.write(pci_addr) - except OSError as err: - LOG.exception('Cannot create file %s. Error message: (%d) %s', - file_path, err.errno, err.strerror) - - def _remove_pci_file(self, container_id, ifname): - file_path = os.path.join(CONF.nested_dpdk.pci_mount_point, - container_id + '-' + ifname) - try: - os.remove(file_path) - except OSError as err: - LOG.warning('Cannot remove file %s. Error message: (%d) %s', - file_path, err.errno, err.strerror) - - def _set_vif(self, vif): - # TODO(ivc): extract annotation interactions - vifs, labels, resource_version, kp_link = self._get_pod_details( - vif.port_profile.selflink) - for ifname, data in vifs.items(): - if vif.id == data['vif'].id: - vifs[ifname] = data - break - self._set_pod_details(vifs, vif.port_profile.selflink, labels, - resource_version, kp_link) - - def _get_pod_details(self, selflink): - k8s = clients.get_kubernetes_client() - pod = k8s.get(selflink) - kp = k8s.get(f'{constants.K8S_API_CRD_NAMESPACES}/' - f'{pod["metadata"]["namespace"]}/kuryrports/' - f'{pod["metadata"]["name"]}') - - try: - vifs = {k: {'default': v['default'], - 'vif': objects.base.VersionedObject - .obj_from_primitive(v['vif'])} - for k, v in kp['status']['vifs'].items()} - except (KeyError, AttributeError): - LOG.exception(f"No vifs found on KuryrPort: {kp}") - raise - LOG.info(f"Got VIFs from Kuryrport: {vifs}") - - resource_version = pod['metadata']['resourceVersion'] - labels = pod['metadata'].get('labels') - return vifs, labels, resource_version, kp['metadata']['selflink'] - - def _set_pod_details(self, vifs, selflink, labels, resource_version, - kp_link): - k8s = clients.get_kubernetes_client() - if vifs: - vif_dict = {k: {'default': v['default'], - 'vif': v['vif'].obj_to_primitive()} - for k, v in vifs.items()} - - LOG.info("Setting VIFs in KuryrPort %r", vif_dict) - k8s.patch_crd('status', kp_link, {'vifs': vif_dict}) - - if not labels: - LOG.info("Removing Label annotation: %r", labels) - labels_annotation = None - else: - labels_annotation = jsonutils.dumps(labels, sort_keys=True) - LOG.info("Setting Labels annotation: %r", labels_annotation) - - k8s.annotate(selflink, - {constants.K8S_ANNOTATION_LABEL: labels_annotation}, - resource_version=resource_version) diff --git a/kuryr_kubernetes/cni/binding/nested.py b/kuryr_kubernetes/cni/binding/nested.py deleted file mode 100644 index 9389b62e9..000000000 --- a/kuryr_kubernetes/cni/binding/nested.py +++ /dev/null @@ -1,239 +0,0 @@ -# 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 errno -import os - -from oslo_log import log as logging -import psutil -import pyroute2 -from pyroute2 import netlink as pyroute_netlink - -from kuryr_kubernetes.cni.binding import base as b_base -from kuryr_kubernetes import config -from kuryr_kubernetes import exceptions -from kuryr_kubernetes.handlers import health -from kuryr_kubernetes import utils - -VLAN_KIND = 'vlan' -MACVLAN_KIND = 'macvlan' -MACVLAN_MODE_BRIDGE = 'bridge' -KUBELET_PORT = 10250 - -LOG = logging.getLogger(__name__) - - -class NestedDriver(health.HealthHandler, b_base.BaseBindingDriver, - metaclass=abc.ABCMeta): - - def __init__(self): - super(NestedDriver, self).__init__() - - @abc.abstractmethod - def _get_iface_create_args(self, vif): - raise NotImplementedError() - - def _detect_iface_name(self, h_ipdb): - # Let's try config first - if config.CONF.binding.link_iface in h_ipdb.interfaces: - LOG.debug(f'Using configured interface ' - f'{config.CONF.binding.link_iface} as bridge interface.') - return config.CONF.binding.link_iface - - # Then let's try choosing the one where kubelet listens to - conns = [x for x in psutil.net_connections() - if x.status == psutil.CONN_LISTEN - and x.laddr.port == KUBELET_PORT] - if len(conns) == 1: - lookup_addr = conns[0].laddr.ip - for name, iface in h_ipdb.interfaces.items(): - if type(name) is int: # Skip ones duplicated by id - continue - - for addr in iface['ipaddr']: - if addr[0] == lookup_addr: - LOG.debug(f'Using kubelet bind interface {name} as ' - f'bridge interface.') - return name - - # Alright, just try the first non-loopback interface - for name, iface in h_ipdb.interfaces.items(): - if type(name) is int: # Skip ones duplicated by id - continue - - if iface['flags'] & pyroute_netlink.rtnl.ifinfmsg.IFF_LOOPBACK: - continue # Skip loopback - - LOG.debug(f'Using interface {name} as bridge interface.') - return name - - raise exceptions.CNIBindingFailure('Cannot find bridge interface for ' - 'nested driver to use. Please set ' - '[binding]link_iface option.') - - def connect(self, vif, ifname, netns, container_id): - # NOTE(vikasc): Ideally 'ifname' should be used here but instead a - # temporary name is being used while creating the device for - # container in host network namespace. This is because cni expects - # only 'eth0' as interface name and if host already has an - # interface named 'eth0', device creation will fail with 'already - # exists' error. - temp_name = vif.vif_name - - # First let's take a peek into the pod namespace and try to remove any - # leftover interface in case we got restarted before CNI returned to - # kubelet. - with b_base.get_ipdb(netns) as c_ipdb: - self._remove_ifaces(c_ipdb, (temp_name, ifname), netns) - - # We might also have leftover interface in the host netns, let's try to - # remove it too. This is outside of the main host's IPDB context - # manager to make sure removal is commited before starting next - # transaction. - with b_base.get_ipdb() as h_ipdb: - self._remove_ifaces(h_ipdb, (temp_name,)) - - with b_base.get_ipdb() as h_ipdb: - # TODO(vikasc): evaluate whether we should have stevedore - # driver for getting the link device. - vm_iface_name = self._detect_iface_name(h_ipdb) - mtu = h_ipdb.interfaces[vm_iface_name].mtu - if mtu < vif.network.mtu: - # NOTE(dulek): This might happen if Neutron and DHCP agent - # have different MTU settings. See - # https://bugs.launchpad.net/kuryr-kubernetes/+bug/1863212 - raise exceptions.CNIBindingFailure( - f'MTU of interface {vm_iface_name} ({mtu}) is smaller ' - f'than MTU of pod network {vif.network.id} ' - f'({vif.network.mtu}). Please make sure pod network ' - f'has the same or smaller MTU as node (VM) network.') - - args = self._get_iface_create_args(vif) - with h_ipdb.create(ifname=temp_name, - link=h_ipdb.interfaces[vm_iface_name], - **args) as iface: - iface.net_ns_fd = utils.convert_netns(netns) - - with b_base.get_ipdb(netns) as c_ipdb: - with c_ipdb.interfaces[temp_name] as iface: - iface.ifname = ifname - iface.mtu = vif.network.mtu - iface.address = str(vif.address) - iface.up() - - def disconnect(self, vif, ifname, netns, container_id): - # NOTE(dulek): Interfaces should get deleted with the netns, but it may - # happen that kubelet or crio will call new CNI ADD before - # the old netns is deleted. This might result in VLAN ID - # conflict. In oder to protect from that let's remove the - # netns ifaces here anyway. - with b_base.get_ipdb(netns) as c_ipdb: - self._remove_ifaces(c_ipdb, (vif.vif_name, ifname), netns) - - -class VlanDriver(NestedDriver): - - def __init__(self): - super(VlanDriver, self).__init__() - - def connect(self, vif, ifname, netns, container_id): - try: - super().connect(vif, ifname, netns, container_id) - except pyroute2.NetlinkError as e: - if e.code == errno.EEXIST: - args = self._get_iface_create_args(vif) - LOG.warning( - f'Creation of pod interface failed due to VLAN ID ' - f'(vlan_info={args}) conflict. Probably the CRI had not ' - f'cleaned up the network namespace of deleted pods. ' - f'Attempting to find and delete offending interface and ' - f'retry.') - self._cleanup_conflicting_vlan(netns, args['vlan_id']) - super().connect(vif, ifname, netns, container_id) - return - raise - - def _get_iface_create_args(self, vif): - return {'kind': VLAN_KIND, 'vlan_id': vif.vlan_id} - - def _cleanup_conflicting_vlan(self, netns, vlan_id): - if vlan_id is None: - # Better to not attempt that, might remove way to much. - return - - netns_paths = [] - handled_netns = set() - with b_base.get_ipdb() as h_ipdb: - vm_iface_name = self._detect_iface_name(h_ipdb) - vm_iface_index = h_ipdb.interfaces[vm_iface_name].index - - if netns.startswith('/proc'): - # Paths have /proc//ns/net pattern, we need to iterate - # over /proc. - netns_dir = utils.convert_netns('/proc') - for pid in os.listdir(netns_dir): - if not pid.isdigit(): - # Ignore all the non-pid stuff in /proc - continue - netns_paths.append(os.path.join(netns_dir, pid, 'ns/net')) - else: - # cri-o manages netns, they're in /var/run/netns/* or similar. - netns_dir = os.path.dirname(netns) - netns_paths = os.listdir(netns_dir) - netns_paths = [os.path.join(netns_dir, netns_path) - for netns_path in netns_paths] - - for netns_path in netns_paths: - netns_path = os.fsdecode(netns_path) - try: - # NOTE(dulek): inode can be used to clearly distinguish the - # netns' as `man namespaces` says: - # - # Since Linux 3.8, they appear as symbolic links. If two - # processes are in the same namespace, then the device IDs and - # inode numbers of their /proc/[pid]/ns/xxx symbolic links will - # be the same; an application can check this using the - # stat.st_dev and stat.st_ino fields returned by stat(2). - netns_stat = os.stat(netns_path) - netns_id = netns_stat.st_dev, netns_stat.st_ino - except OSError: - continue - if netns_id in handled_netns: - continue - handled_netns.add(netns_id) - - try: - with b_base.get_ipdb(netns_path) as c_ipdb: - for ifname, iface in c_ipdb.interfaces.items(): - if (iface.vlan_id == vlan_id - and iface.link == vm_iface_index): - LOG.warning( - f'Found offending interface {ifname} with ' - f'VLAN ID {vlan_id} in netns {netns_path}. ' - f'Trying to remove it.') - with c_ipdb.interfaces[ifname] as found_iface: - found_iface.remove() - break - except OSError: - continue - - -class MacvlanDriver(NestedDriver): - - def __init__(self): - super(MacvlanDriver, self).__init__() - - def _get_iface_create_args(self, vif): - return {'kind': MACVLAN_KIND, 'macvlan_mode': MACVLAN_MODE_BRIDGE} diff --git a/kuryr_kubernetes/cni/binding/vhostuser.py b/kuryr_kubernetes/cni/binding/vhostuser.py deleted file mode 100644 index ea1a28b10..000000000 --- a/kuryr_kubernetes/cni/binding/vhostuser.py +++ /dev/null @@ -1,131 +0,0 @@ -# Copyright (c) 2020 Samsung Electronics Co., Ltd. -# 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 os.path -import stat - -from os_vif.objects import fields as osv_fields -from oslo_config import cfg -from oslo_log import log -from oslo_serialization import jsonutils -from vif_plug_ovs import constants -from vif_plug_ovs import ovs - -from kuryr_kubernetes.cni.binding import base -from kuryr_kubernetes import config -from kuryr_kubernetes import exceptions as k_exc -from kuryr_kubernetes.handlers import health - -LOG = log.getLogger(__name__) - - -def _get_vhostuser_port_name(vif): - return ovs.OvsPlugin.gen_port_name(constants.OVS_VHOSTUSER_PREFIX, vif.id) - - -def _get_vhu_sock(config_file_path): - with open(config_file_path, 'r') as f: - conf = jsonutils.load(f) - return conf['vhostname'] - - -def _check_sock_file(vhostuser_socket): - mode = os.stat(vhostuser_socket).st_mode - return stat.S_ISSOCK(mode) - - -class VIFVHostUserDriver(health.HealthHandler, base.BaseBindingDriver): - - def __init__(self): - super(VIFVHostUserDriver, self).__init__() - self.mount_path = config.CONF.vhostuser.mount_point - self.ovs_vu_path = config.CONF.vhostuser.ovs_vhu_path - if not self.mount_path: - raise cfg.RequiredOptError('mount_point', 'vhostuser') - - def _write_config(self, container_id, ifname, port_name, vif): - """Write vhostuser configuration file - - This function writes configuration file, this file will be used by - application inside container and for cleanup (def disconnect) - procedure. - """ - vhost_conf = {} - vhost_conf["vhostname"] = port_name - vhost_conf["vhostmac"] = vif.address - vhost_conf["mode"] = vif.mode - with open(self._config_file_path(container_id, ifname), "w") as f: - jsonutils.dump(vhost_conf, f) - - def _config_file_path(self, container_id, ifname): - return os.path.join(self.mount_path, f'{container_id}-{ifname}') - - def connect(self, vif, ifname, netns, container_id): - port_name = _get_vhostuser_port_name(vif) - self._write_config(container_id, ifname, port_name, vif) - # no need to copy in case of SERVER mode - if vif.mode == osv_fields.VIFVHostUserMode.SERVER: - return - - src_vhu_sock = os.path.join(self.ovs_vu_path, port_name) - - if _check_sock_file(src_vhu_sock): - dst_vhu_sock = os.path.join(vif.path, port_name) - LOG.debug("Moving %s to %s while processing VIF %s", src_vhu_sock, - dst_vhu_sock, vif.id) - os.rename(src_vhu_sock, dst_vhu_sock) - else: - error_msg = ("Socket %s required for VIF %s doesn't exist" % - (src_vhu_sock, vif.id)) - LOG.error(error_msg) - raise k_exc.CNIError(error_msg) - - def disconnect(self, vif, ifname, netns, container_id): - # This function removes configuration file and appropriate - # socket file. Unfortunatelly Open vSwitch daemon can't remove - # moved socket, so we have to do it - config_file_path = self._config_file_path(container_id, ifname) - - if not os.path.exists(config_file_path): - LOG.warning("Configuration file: %s for VIF %s doesn't exist!", - config_file_path, vif.id) - return - vhu_sock_path = os.path.join(self.mount_path, - _get_vhu_sock(config_file_path)) - LOG.debug("remove: %s, %s", config_file_path, vhu_sock_path) - try: - os.remove(vhu_sock_path) - except Exception: - LOG.exception("Failed to delete socket %s when processing VIF %s.", - vhu_sock_path, vif.id) - os.remove(config_file_path) - - def is_alive(self): - healthy = False - try: - healthy = (os.path.exists(self.ovs_vu_path) - and os.path.exists(self.mount_path)) - except Exception: - LOG.exception('Error when determining health status of vhostuser ' - 'CNI driver.') - - if not healthy: - LOG.error('Directory %s or %s does not exist or Kuryr has no ' - 'permissions to access it. Marking vhostuser binding ' - 'driver as unhealthy.', self.ovs_vu_path, - self.mount_path) - - return healthy diff --git a/kuryr_kubernetes/cni/daemon/__init__.py b/kuryr_kubernetes/cni/daemon/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/kuryr_kubernetes/cni/daemon/service.py b/kuryr_kubernetes/cni/daemon/service.py deleted file mode 100644 index ac459db25..000000000 --- a/kuryr_kubernetes/cni/daemon/service.py +++ /dev/null @@ -1,375 +0,0 @@ -# Copyright 2017 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 ctypes import c_bool -import errno -from http import client as httplib -import multiprocessing -import os -import queue -import sys -import threading -import time -import urllib3 - -import cotyledon -import flask -import pyroute2 -from pyroute2.ipdb import transactional -from werkzeug import serving - -import os_vif -from oslo_config import cfg -from oslo_log import log as logging -from oslo_serialization import jsonutils - -from kuryr_kubernetes import clients -from kuryr_kubernetes.cni.daemon import watcher_service -from kuryr_kubernetes.cni import health -from kuryr_kubernetes.cni.plugins import k8s_cni_registry -from kuryr_kubernetes.cni import prometheus_exporter -from kuryr_kubernetes.cni import utils as cni_utils -from kuryr_kubernetes import config -from kuryr_kubernetes import exceptions -from kuryr_kubernetes import objects - -LOG = logging.getLogger(__name__) -CONF = cfg.CONF -ErrContainerUnknown = 3 -ErrInvalidEnvironmentVariables = 4 -ErrTryAgainLater = 11 -ErrInternal = 999 - - -class DaemonServer(object): - def __init__(self, plugin, healthy, metrics): - self.ctx = None - self.plugin = plugin - self.healthy = healthy - self.metrics = metrics - self.failure_count = multiprocessing.Value('i', 0) - self.application = flask.Flask('kuryr-daemon') - self.application.add_url_rule( - '/addNetwork', methods=['POST'], view_func=self.add) - self.application.add_url_rule( - '/delNetwork', methods=['POST'], view_func=self.delete) - self.headers = {'ContentType': 'application/json', - 'Connection': 'close'} - self._server = None - - def _prepare_request(self): - params = cni_utils.CNIParameters(flask.request.get_json()) - LOG.debug('Received %s request. CNI Params: %s', - params.CNI_COMMAND, params) - return params - - def _error(self, error_code, message, details=""): - template = { - "code": error_code, - "msg": message, - "details": details - } - data = jsonutils.dumps(template) - return data - - def _update_metrics(self, command, error, duration): - """Add a new metric value to the shared metrics dict""" - labels = {'command': command, 'error': error} - self.metrics.put({'labels': labels, 'duration': duration}) - - @cni_utils.measure_time('ADD') - def add(self): - try: - params = self._prepare_request() - except Exception: - self._check_failure() - LOG.exception('Exception when reading CNI params.') - error = self._error(ErrInvalidEnvironmentVariables, - "Required CNI params missing.") - return error, httplib.BAD_REQUEST, self.headers - - try: - vif = self.plugin.add(params) - data = jsonutils.dumps(vif.obj_to_primitive()) - except (exceptions.CNIPodGone, exceptions.CNIPodUidMismatch) as e: - LOG.warning('Pod deleted while processing ADD request') - error = self._error(ErrContainerUnknown, str(e)) - return error, httplib.GONE, self.headers - except exceptions.CNITimeout as e: - LOG.exception('Timeout on ADD request') - error = self._error(ErrTryAgainLater, f"{e}. Try Again Later.") - return error, httplib.GATEWAY_TIMEOUT, self.headers - except pyroute2.NetlinkError as e: - if e.code == errno.EEXIST: - self._check_failure() - LOG.warning( - f'Creation of pod interface failed due to VLAN ID ' - f'conflict. Probably the CRI had not cleaned up the ' - f'network namespace of deleted pods. Attempting to retry.') - error = self._error(ErrTryAgainLater, - "Creation of pod interface failed due to " - "VLAN ID conflict. Try Again Later") - return error, httplib.GATEWAY_TIMEOUT, self.headers - raise - except Exception: - if not self.healthy.value: - error = self._error(ErrInternal, - "Maximum CNI ADD Failures Reached.", - "Error when processing addNetwork request." - " CNI Params: {}".format(params)) - else: - self._check_failure() - error = self._error(ErrInternal, - "Error processing request", - "Failure processing addNetwork request. " - "CNI Params: {}".format(params)) - LOG.exception('Error when processing addNetwork request. CNI ' - 'Params: %s', params) - return error, httplib.INTERNAL_SERVER_ERROR, self.headers - - return data, httplib.ACCEPTED, self.headers - - @cni_utils.measure_time('DEL') - def delete(self): - try: - params = self._prepare_request() - except Exception: - LOG.exception('Exception when reading CNI params.') - error = self._error(ErrInvalidEnvironmentVariables, - "Required CNI params missing.") - return error, httplib.BAD_REQUEST, self.headers - - try: - self.plugin.delete(params) - except (exceptions.CNIKuryrPortTimeout, exceptions.CNIPodUidMismatch): - # NOTE(dulek): It's better to ignore these errors - most of the - # time it will happen when pod is long gone and CRI - # overzealously tries to delete it from the network. - # We cannot really do anything without VIF annotation, - # so let's just tell CRI to move along. - LOG.warning('Error when processing delNetwork request. ' - 'Ignoring this error, pod is most likely gone') - return '', httplib.NO_CONTENT, self.headers - except Exception: - if not self.healthy.value: - error = self._error(ErrInternal, - "Maximum CNI DEL Failures Reached.", - "Error processing delNetwork request. " - "CNI Params: {}".format(params)) - else: - self._check_failure() - error = self._error(ErrInternal, - "Error processing request", - "Failure processing delNetwork request. " - "CNI Params: {}".format(params)) - LOG.exception('Error when processing delNetwork request. CNI ' - 'Params: %s.', params) - return error, httplib.INTERNAL_SERVER_ERROR, self.headers - return '', httplib.NO_CONTENT, self.headers - - def run(self): - server_pair = CONF.cni_daemon.bind_address - LOG.info('Starting server on %s.', server_pair) - try: - address, port = server_pair.split(':') - port = int(port) - except ValueError: - LOG.exception('Cannot start server on %s.', server_pair) - raise - - if CONF.cni_daemon.worker_num <= 1: - msg = ('[cni_daemon]worker_num needs to be set to a value higher ' - 'than 1') - LOG.critical(msg) - raise exceptions.InvalidKuryrConfiguration(msg) - - try: - self._server = serving.make_server( - address, port, self.application, threaded=False, - processes=CONF.cni_daemon.worker_num) - self._server.serve_forever() - except Exception: - LOG.exception('Failed to start kuryr-daemon.') - raise - - def stop(self): - LOG.info("Waiting for DaemonServer worker processes to exit...") - self._server._block_on_close = True - self._server.shutdown() - self._server.server_close() - LOG.info("All DaemonServer workers finished gracefully.") - - def _check_failure(self): - with self.failure_count.get_lock(): - if self.failure_count.value < CONF.cni_daemon.cni_failures_count: - self.failure_count.value += 1 - else: - with self.healthy.get_lock(): - LOG.debug("Reporting maximum CNI ADD/DEL failures " - "reached.") - self.healthy.value = False - - -class CNIDaemonServerService(cotyledon.Service): - name = "server" - - def __init__(self, worker_id, registry, healthy, metrics): - super(CNIDaemonServerService, self).__init__(worker_id) - self.registry = registry - self.healthy = healthy - self.plugin = k8s_cni_registry.K8sCNIRegistryPlugin(registry, - self.healthy) - self.metrics = metrics - self.server = DaemonServer(self.plugin, self.healthy, self.metrics) - - def run(self): - # NOTE(dulek): We might do a *lot* of pyroute2 operations, let's - # make the pyroute2 timeout configurable to make sure - # kernel will have chance to catch up. - transactional.SYNC_TIMEOUT = CONF.cni_daemon.pyroute2_timeout - - # Run HTTP server - self.server.run() - - def terminate(self): - self.server.stop() - - -class CNIDaemonHealthServerService(cotyledon.Service): - name = "health" - - def __init__(self, worker_id, healthy): - super(CNIDaemonHealthServerService, self).__init__(worker_id) - self.health_server = health.CNIHealthServer(healthy) - - def run(self): - self.health_server.run() - - -class CNIDaemonExporterService(cotyledon.Service): - name = "Prometheus Exporter" - - def __init__(self, worker_id, metrics): - super(CNIDaemonExporterService, self).__init__(worker_id) - self.prometheus_exporter = prometheus_exporter.CNIPrometheusExporter() - self.is_running = True - self.metrics = metrics - self.exporter_thread = threading.Thread( - target=self._start_metric_updater) - self.exporter_thread.start() - - def _start_metric_updater(self): - while self.is_running: - try: - metric = self.metrics.get(timeout=1) - except queue.Empty: - continue - labels = metric['labels'] - duration = metric['duration'] - self.prometheus_exporter.update_metric(labels, duration) - - def terminate(self): - self.is_running = False - if self.exporter_thread: - self.exporter_thread.join() - - def run(self): - self.prometheus_exporter.run() - - -class CNIDaemonServiceManager(cotyledon.ServiceManager): - def __init__(self): - # NOTE(mdulko): Default shutdown timeout is 60 seconds and K8s won't - # wait more by default anyway. - super(CNIDaemonServiceManager, self).__init__() - self._server_service = None - # TODO(dulek): Use cotyledon.oslo_config_glue to support conf reload. - - # TODO(vikasc): Should be done using dynamically loadable OVO types - # plugin. - objects.register_locally_defined_vifs() - - os_vif.initialize() - clients.setup_kubernetes_client() - - self.manager = multiprocessing.Manager() - registry = self.manager.dict() # For Watcher->Server communication. - healthy = multiprocessing.Value(c_bool, True) - metrics = self.manager.Queue() - self.add(watcher_service.KuryrPortWatcherService, workers=1, - args=(registry, healthy,)) - self.add(watcher_service.PodWatcherService, workers=1, - args=(registry, healthy,)) - self._server_service = self.add(CNIDaemonServerService, workers=1, - args=(registry, healthy, metrics,)) - self.add(CNIDaemonHealthServerService, workers=1, args=(healthy,)) - self.add(CNIDaemonExporterService, workers=1, args=(metrics,)) - - def shutdown_hook(service_id, worker_id, exit_code): - LOG.critical(f'Child Service {service_id} had exited with code ' - f'{exit_code}, stopping kuryr-daemon') - self.shutdown() - - self.register_hooks(on_terminate=self.terminate, - on_dead_worker=shutdown_hook) - - def run(self): - # FIXME(darshna): Remove pyroute2 IPDB deprecation warning, remove - # once we stop using pyroute2.IPDB. - logging.getLogger('pyroute2').setLevel(logging.ERROR) - logging.getLogger('pr2modules.ipdb.main').setLevel(logging.ERROR) - - reaper_thread = threading.Thread(target=self._zombie_reaper, - daemon=True) - self._terminate_called = threading.Event() - reaper_thread.start() - super(CNIDaemonServiceManager, self).run() - - def _zombie_reaper(self): - while True: - try: - res = os.waitpid(-1, os.WNOHANG) - # don't sleep or stop if a zombie process was found - # as there could be more - if res != (0, 0): - continue - except ChildProcessError: - # There are no child processes yet (or they have been killed) - pass - except os.error: - LOG.exception("Got OS error while reaping zombie processes") - if self._terminate_called.isSet(): - break - time.sleep(1) - - def terminate(self): - self._terminate_called.set() - if self._server_service: - LOG.info("Gracefully stopping DaemonServer service..") - self.reconfigure(self._server_service, 0) - for worker in self._running_services[self._server_service]: - worker.terminate() - for worker in self._running_services[self._server_service]: - worker.join() - LOG.info("Stopping registry manager...") - self.manager.shutdown() - LOG.info("Continuing with shutdown") - - -def start(): - urllib3.disable_warnings() - config.init(sys.argv[1:]) - config.setup_logging() - - CNIDaemonServiceManager().run() diff --git a/kuryr_kubernetes/cni/daemon/watcher_service.py b/kuryr_kubernetes/cni/daemon/watcher_service.py deleted file mode 100644 index cb6987a36..000000000 --- a/kuryr_kubernetes/cni/daemon/watcher_service.py +++ /dev/null @@ -1,94 +0,0 @@ -# Copyright 2022 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. - -import threading -import time -import urllib.parse - -import cotyledon -from oslo_config import cfg -from oslo_log import log as logging - -from kuryr_kubernetes.cni import handlers -from kuryr_kubernetes import constants as k_const -from kuryr_kubernetes import utils -from kuryr_kubernetes import watcher as k_watcher - - -HEALTH_CHECKER_DELAY = 5 -LOG = logging.getLogger(__name__) -CONF = cfg.CONF - - -class BaseCNIDaemonWatcherService(cotyledon.Service): - name = "watcher" - - def __init__(self, worker_id, handler, path, registry, healthy): - super().__init__(worker_id) - self.pipeline = None - self.watcher = None - self.health_thread = None - self.handler = handler - self.registry = registry - self.healthy = healthy - self.path = path - self.is_running = False - - def run(self): - self.pipeline = handlers.CNIPipeline() - self.pipeline.register(self.handler) - self.watcher = k_watcher.Watcher(self.pipeline) - self.watcher.add(self.path) - - self.is_running = True - - self.health_thread = threading.Thread( - target=self._start_watcher_health_checker) - self.health_thread.start() - - self.watcher.start() - - def _start_watcher_health_checker(self): - while self.is_running: - if not self.watcher.is_alive(): - LOG.warning(f"Reporting watcher {self.__class__.__name__} is " - f"not healthy because it's not running anymore.") - with self.healthy.get_lock(): - self.healthy.value = False - time.sleep(HEALTH_CHECKER_DELAY) - - def terminate(self): - self.is_running = False - if self.health_thread: - self.health_thread.join() - if self.watcher: - self.watcher.stop() - - -class KuryrPortWatcherService(BaseCNIDaemonWatcherService): - def __init__(self, worker_id, registry, healthy): - query_label = urllib.parse.quote_plus(f'{k_const.KURYRPORT_LABEL}=' - f'{utils.get_nodename()}') - path = f'{k_const.K8S_API_CRD_KURYRPORTS}?labelSelector={query_label}' - handler = handlers.CNIKuryrPortHandler(registry) - super().__init__(worker_id, handler, path, registry, healthy) - - -class PodWatcherService(BaseCNIDaemonWatcherService): - def __init__(self, worker_id, registry, healthy): - query_label = urllib.parse.quote_plus(f'spec.nodeName=' - f'{utils.get_nodename()}') - path = f'{k_const.K8S_API_PODS}?fieldSelector={query_label}' - handler = handlers.CNIPodHandler(registry) - super().__init__(worker_id, handler, path, registry, healthy) diff --git a/kuryr_kubernetes/cni/handlers.py b/kuryr_kubernetes/cni/handlers.py deleted file mode 100644 index 5e90f0dd6..000000000 --- a/kuryr_kubernetes/cni/handlers.py +++ /dev/null @@ -1,116 +0,0 @@ -# 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. - -from os_vif import objects as obj_vif -from oslo_concurrency import lockutils -from oslo_log import log as logging - -from kuryr_kubernetes import constants as k_const -from kuryr_kubernetes.handlers import dispatch as k_dis -from kuryr_kubernetes.handlers import k8s_base -from kuryr_kubernetes import utils - - -LOG = logging.getLogger(__name__) - - -class CNIKuryrPortHandler(k8s_base.ResourceEventHandler): - OBJECT_KIND = k_const.K8S_OBJ_KURYRPORT - - def __init__(self, registry): - super().__init__() - self.registry = registry - - def on_vif(self, kuryrport, vifs): - kp_name = utils.get_res_unique_name(kuryrport) - with lockutils.lock(kp_name, external=True): - if (kp_name not in self.registry or - self.registry[kp_name] == k_const.CNI_DELETED_POD_SENTINEL - or self.registry[kp_name]['kp']['metadata']['uid'] != - kuryrport['metadata']['uid']): - self.registry[kp_name] = {'kp': kuryrport, - 'vifs': vifs, - 'containerid': None, - 'vif_unplugged': False, - 'del_received': False} - else: - old_vifs = self.registry[kp_name]['vifs'] - for iface in vifs: - if old_vifs[iface].active != vifs[iface].active: - kp_dict = self.registry[kp_name] - kp_dict['vifs'] = vifs - self.registry[kp_name] = kp_dict - - def on_deleted(self, kuryrport, *args, **kwargs): - kp_name = utils.get_res_unique_name(kuryrport) - try: - if (kp_name in self.registry and self.registry[kp_name] - != k_const.CNI_DELETED_POD_SENTINEL): - # NOTE(ndesh): We need to lock here to avoid race condition - # with the deletion code for CNI DEL so that - # we delete the registry entry exactly once - with lockutils.lock(kp_name, external=True): - if self.registry[kp_name]['vif_unplugged']: - del self.registry[kp_name] - else: - kp_dict = self.registry[kp_name] - kp_dict['del_received'] = True - self.registry[kp_name] = kp_dict - except KeyError: - # This means someone else removed it. It's odd but safe to ignore. - LOG.debug('KuryrPort %s entry already removed from registry while ' - 'handling DELETED event. Ignoring.', kp_name) - pass - - def on_present(self, kuryrport, *args, **kwargs): - LOG.debug('MODIFIED event for KuryrPort %s', - utils.get_res_unique_name(kuryrport)) - vifs = self._get_vifs(kuryrport) - if vifs: - self.on_vif(kuryrport, vifs) - - def _get_vifs(self, kuryrport): - vifs_dict = { - k: obj_vif.base.VersionedObject.obj_from_primitive(v['vif']) - for k, v in kuryrport['status']['vifs'].items()} - LOG.debug("Got vifs: %r", vifs_dict) - - return vifs_dict - - -class CNIPodHandler(k8s_base.ResourceEventHandler): - OBJECT_KIND = k_const.K8S_OBJ_POD - - def __init__(self, registry): - super().__init__() - self.registry = registry - - def on_finalize(self, pod, *args, **kwargs): - # TODO(dulek): Verify if this is the handler for such case. - kp_name = utils.get_res_unique_name(pod) - with lockutils.lock(kp_name, external=True): - # If there was no KP and Pod got deleted, we need inform the - # thread waiting for it about that. We'll insert sentinel value. - if kp_name not in self.registry: - self.registry[kp_name] = k_const.CNI_DELETED_POD_SENTINEL - - -class CNIPipeline(k_dis.EventPipeline): - - def _wrap_dispatcher(self, dispatcher): - return dispatcher - - def _wrap_consumer(self, consumer): - return consumer diff --git a/kuryr_kubernetes/cni/health.py b/kuryr_kubernetes/cni/health.py deleted file mode 100644 index 0a206c7d5..000000000 --- a/kuryr_kubernetes/cni/health.py +++ /dev/null @@ -1,140 +0,0 @@ -# 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 http import client as httplib -import os - -from oslo_config import cfg -from oslo_log import log as logging -from pyroute2 import IPDB - -from kuryr.lib._i18n import _ -from kuryr_kubernetes.cni import utils -from kuryr_kubernetes import health as base_server - -LOG = logging.getLogger(__name__) -CONF = cfg.CONF - -cni_health_server_opts = [ - cfg.IntOpt('port', - help=_('Port for CNI Health HTTP Server.'), - default=8090), - cfg.IntOpt('max_memory_usage', - help=_('Maximum memory usage (MiB) for CNI Health Server ' - 'process. If this value is exceeded kuryr-daemon ' - 'will be marked as unhealthy.'), - default=-1), - cfg.StrOpt( - 'cg_path', - help=_('sysfs path to the CNI cgroup. This is used for resource ' - 'tracking and as such should point to the cgroup hierarchy ' - 'leaf. It only applies when non containerized'), - default='/sys/fs/cgroup/memory/system.slice/kuryr-cni.service') -] - -CONF.register_opts(cni_health_server_opts, "cni_health_server") - -TOP_CGROUP_MEMORY_PATH = '/sys/fs/cgroup/memory' -MEMSW_FILENAME = 'memory.memsw.usage_in_bytes' -BYTES_AMOUNT = 1048576 -CAP_NET_ADMIN = 12 # Taken from linux/capabilities.h -EFFECTIVE_CAPS = 'CapEff:\t' - - -def _has_cap(capability, entry, proc_status_path='/proc/self/status'): - """Returns true iff the process has the specified capability. - - :param capability: the bit number for the capability to check as seen - in linux/capabilities.h. - :param entry: Whether to check CapInh, CapEff or CapBnd. - :param proc_status_path: Which process status should be checked. If none - is passed, it will check the current process. - :return: Whether the specified process has the capability bit set - """ - with open(proc_status_path, 'r') as pstat: - for line in pstat: - if line.startswith(entry): - caps = int(line[len(entry):], 16) - return (caps & (1 << capability)) != 0 - - -def _get_cni_cgroup_path(): - """Returns the path to the CNI process cgroup memory directory.""" - if utils.running_under_container_runtime(): - # We are running inside a container. This means the root cgroup - # is the one we need to track as it will be the CNI parent proc - cg_memsw_path = TOP_CGROUP_MEMORY_PATH - else: - cg_memsw_path = CONF.cni_health_server.cg_path - - return cg_memsw_path - - -def _get_memsw_usage(cgroup_mem_path): - """Returns the group's resident memory plus swap usage.""" - with open(os.path.join(cgroup_mem_path, MEMSW_FILENAME)) as memsw: - memsw_in_bytes = int(memsw.read()) - return memsw_in_bytes / BYTES_AMOUNT - - -class CNIHealthServer(base_server.BaseHealthServer): - """Server used by readiness and liveness probe to manage CNI health checks. - - Verifies presence of NET_ADMIN capabilities, IPDB in working order, - connectivity to Kubernetes API, quantity of CNI add failure, health of - CNI components and existence of memory leaks. - """ - - def __init__(self, components_healthy): - - super().__init__('daemon-health', CONF.cni_health_server.port) - self._components_healthy = components_healthy - - def readiness_status(self): - k8s_conn = self.verify_k8s_connection() - - if not _has_cap(CAP_NET_ADMIN, EFFECTIVE_CAPS): - error_message = 'NET_ADMIN capabilities not present.' - LOG.error(error_message) - return error_message, httplib.INTERNAL_SERVER_ERROR, {} - if not k8s_conn: - error_message = 'K8s API healtz endpoint failed.' - LOG.error(error_message) - return error_message, httplib.INTERNAL_SERVER_ERROR, {} - - return 'ok', httplib.OK, {} - - def liveness_status(self): - no_limit = -1 - try: - with IPDB(): - pass - except Exception: - error_message = 'IPDB not in working order.' - LOG.error(error_message) - return error_message, httplib.INTERNAL_SERVER_ERROR, {} - - if CONF.cni_health_server.max_memory_usage != no_limit: - mem_usage = _get_memsw_usage(_get_cni_cgroup_path()) - - if mem_usage > CONF.cni_health_server.max_memory_usage: - err_message = 'CNI daemon exceeded maximum memory usage.' - LOG.error(err_message) - return err_message, httplib.INTERNAL_SERVER_ERROR, {} - - with self._components_healthy.get_lock(): - if not self._components_healthy.value: - err_message = 'Kuryr CNI components not healthy.' - LOG.error(err_message) - return err_message, httplib.INTERNAL_SERVER_ERROR, {} - - return 'ok', httplib.OK, {} diff --git a/kuryr_kubernetes/cni/main.py b/kuryr_kubernetes/cni/main.py deleted file mode 100644 index 0957cd384..000000000 --- a/kuryr_kubernetes/cni/main.py +++ /dev/null @@ -1,69 +0,0 @@ -# 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 os -import signal -import sys - -import os_vif -from oslo_config import cfg -from oslo_log import log as logging -from oslo_serialization import jsonutils - -from kuryr_kubernetes.cni import api as cni_api -from kuryr_kubernetes.cni import utils -from kuryr_kubernetes import config -from kuryr_kubernetes import constants as k_const -from kuryr_kubernetes import objects as k_objects - -CONF = cfg.CONF -LOG = logging.getLogger(__name__) -_CNI_TIMEOUT = 180 - - -def run(): - d = jsonutils.load(sys.stdin.buffer) - cni_conf = utils.CNIConfig(d) - args = (['--config-file', cni_conf.kuryr_conf] if 'kuryr_conf' in d - else []) - - try: - if cni_conf.debug: - args.append('-d') - except AttributeError: - pass - config.init(args) - config.setup_logging() - - # Initialize o.vo registry. - k_objects.register_locally_defined_vifs() - os_vif.initialize() - - runner = cni_api.CNIDaemonizedRunner() - - def _timeout(signum, frame): - runner._write_dict(sys.stdout, { - 'msg': 'timeout', - 'code': k_const.CNI_TIMEOUT_CODE, - }) - LOG.debug('timed out') - sys.exit(1) - - signal.signal(signal.SIGALRM, _timeout) - signal.alarm(_CNI_TIMEOUT) - status = runner.run(os.environ, cni_conf, sys.stdout) - LOG.debug("Exiting with status %s", status) - if status: - sys.exit(status) diff --git a/kuryr_kubernetes/cni/plugins/__init__.py b/kuryr_kubernetes/cni/plugins/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/kuryr_kubernetes/cni/plugins/base.py b/kuryr_kubernetes/cni/plugins/base.py deleted file mode 100644 index 5d9ccb51e..000000000 --- a/kuryr_kubernetes/cni/plugins/base.py +++ /dev/null @@ -1,28 +0,0 @@ -# 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 - - -class CNIPlugin(object, metaclass=abc.ABCMeta): - - @abc.abstractmethod - def add(self, params): - raise NotImplementedError() - - @abc.abstractmethod - def delete(self, params): - raise NotImplementedError() diff --git a/kuryr_kubernetes/cni/plugins/k8s_cni_registry.py b/kuryr_kubernetes/cni/plugins/k8s_cni_registry.py deleted file mode 100644 index c04791ab7..000000000 --- a/kuryr_kubernetes/cni/plugins/k8s_cni_registry.py +++ /dev/null @@ -1,246 +0,0 @@ -# Copyright 2017 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. - -import retrying - -from os_vif import objects as obj_vif -from oslo_concurrency import lockutils -from oslo_config import cfg -from oslo_log import log as logging - -from kuryr_kubernetes import clients -from kuryr_kubernetes.cni.binding import base as b_base -from kuryr_kubernetes.cni.plugins import base as base_cni -from kuryr_kubernetes.cni import utils -from kuryr_kubernetes import constants as k_const -from kuryr_kubernetes import exceptions -from kuryr_kubernetes import utils as k_utils - -LOG = logging.getLogger(__name__) -CONF = cfg.CONF -RETRY_DELAY = 1000 # 1 second in milliseconds - - -class K8sCNIRegistryPlugin(base_cni.CNIPlugin): - def __init__(self, registry, healthy): - self.healthy = healthy - self.registry = registry - self.k8s = clients.get_kubernetes_client() - - def _get_obj_name(self, params): - return f'{params.args.K8S_POD_NAMESPACE}/{params.args.K8S_POD_NAME}' - - def _get_pod(self, params): - namespace = params.args.K8S_POD_NAMESPACE - name = params.args.K8S_POD_NAME - - try: - return self.k8s.get( - f'{k_const.K8S_API_NAMESPACES}/{namespace}/pods/{name}') - except exceptions.K8sResourceNotFound: - return None - except exceptions.K8sClientException: - uniq_name = self._get_obj_name(params) - LOG.exception('Error when getting Pod %s', uniq_name) - raise - - def add(self, params): - kp_name = self._get_obj_name(params) - timeout = CONF.cni_daemon.vif_annotation_timeout - - # In order to fight race conditions when pods get recreated with the - # same name (think StatefulSet), we're trying to get pod UID either - # from the request or the API in order to use it as the ID to compare. - if 'K8S_POD_UID' not in params.args: - # CRI doesn't pass K8S_POD_UID, get it from the API. - pod = self._get_pod(params) - if not pod: - raise exceptions.CNIPodGone(kp_name) - params.args.K8S_POD_UID = pod['metadata']['uid'] - - vifs = self._do_work(params, b_base.connect, timeout) - - # NOTE(dulek): Saving containerid to be able to distinguish old DEL - # requests that we should ignore. We need a lock to - # prevent race conditions and replace whole object in the - # dict for multiprocessing.Manager to notice that. - with lockutils.lock(kp_name, external=True): - d = self.registry[kp_name] - d['containerid'] = params.CNI_CONTAINERID - self.registry[kp_name] = d - LOG.debug('Saved containerid = %s for CRD %s', - params.CNI_CONTAINERID, kp_name) - - # Wait for timeout sec, 1 sec between tries, retry when even one - # vif is not active. - @retrying.retry(stop_max_delay=timeout * 1000, wait_fixed=RETRY_DELAY, - retry_on_result=utils.any_vif_inactive) - def wait_for_active(kp_name): - return self.registry[kp_name]['vifs'] - - data = {'metadata': {'name': params.args.K8S_POD_NAME, - 'namespace': params.args.K8S_POD_NAMESPACE}} - pod = k_utils.get_referenced_object(data, 'Pod') - - try: - self.k8s.add_event(pod, 'CNIWaitingForActiveVIFs', - f'Waiting for Neutron ports of {kp_name} to ' - f'become ACTIVE after binding.', - component='kuryr-daemon') - vifs = wait_for_active(kp_name) - except retrying.RetryError: - self.k8s.add_event(pod, 'CNITimedOutWaitingForActiveVIFs', - f'Timed out waiting for Neutron ports of ' - f'{kp_name} to become ACTIVE after binding.', - 'Warning', 'kuryr-daemon') - raise exceptions.CNINeutronPortActivationTimeout( - kp_name, self.registry[kp_name]['vifs']) - - return vifs[k_const.DEFAULT_IFNAME] - - def delete(self, params): - kp_name = self._get_obj_name(params) - try: - with lockutils.lock(kp_name, external=True): - kp = self.registry[kp_name] - if kp == k_const.CNI_DELETED_POD_SENTINEL: - LOG.warning( - 'Received DEL request for deleted Pod %s without a' - 'KuryrPort. Ignoring.', kp_name) - del self.registry[kp_name] - return - reg_ci = self.registry[kp_name]['containerid'] - LOG.debug('Read containerid = %s for KuryrPort %s', reg_ci, - kp_name) - if reg_ci and reg_ci != params.CNI_CONTAINERID: - # NOTE(dulek): This is a DEL request for some older (probably - # failed) ADD call. We should ignore it or we'll - # unplug a running pod. - LOG.warning('Received DEL request for unknown ADD call for ' - 'Kuryrport %s (CNI_CONTAINERID=%s). Ignoring.', - kp_name, params.CNI_CONTAINERID) - return - except KeyError: - pass - - # Passing arbitrary 5 seconds as timeout, as it does not make any sense - # to wait on CNI DEL. If kuryrport got deleted from API - VIF info is - # gone. If kuryrport got the vif info removed - it is now gone too. - # The number's not 0, because we need to anticipate for restarts and - # delay before registry is populated by watcher. - try: - self._do_work(params, b_base.disconnect, 5) - except (exceptions.CNIKuryrPortTimeout, exceptions.CNIPodUidMismatch): - # So the VIF info seems to be lost at this point, we don't even - # know what binding driver was used to plug it. Let's at least - # try to remove the interface we created from the netns to prevent - # possible VLAN ID conflicts. - b_base.cleanup(params.CNI_IFNAME, params.CNI_NETNS) - raise - - # NOTE(ndesh): We need to lock here to avoid race condition - # with the deletion code in the watcher to ensure that - # we delete the registry entry exactly once - try: - with lockutils.lock(kp_name, external=True): - if self.registry[kp_name]['del_received']: - del self.registry[kp_name] - else: - kp_dict = self.registry[kp_name] - kp_dict['vif_unplugged'] = True - self.registry[kp_name] = kp_dict - except KeyError: - # This means the kuryrport was removed before vif was unplugged. - # This shouldn't happen, but we can't do anything about it now - LOG.debug('KuryrPort %s not found registry while handling DEL ' - 'request. Ignoring.', kp_name) - pass - - def report_drivers_health(self, driver_healthy): - if not driver_healthy: - with self.healthy.get_lock(): - LOG.debug("Reporting CNI driver not healthy.") - self.healthy.value = driver_healthy - - def _get_vifs_from_registry(self, params, timeout): - kp_name = self._get_obj_name(params) - - # In case of KeyError retry for `timeout` s, wait 1 s between tries. - @retrying.retry(stop_max_delay=timeout * 1000, wait_fixed=RETRY_DELAY, - retry_on_exception=lambda e: isinstance( - e, (KeyError, exceptions.CNIPodUidMismatch))) - def find(): - d = self.registry[kp_name] - if d == k_const.CNI_DELETED_POD_SENTINEL: - # Pod got deleted meanwhile - raise exceptions.CNIPodGone(kp_name) - - static = d['kp']['spec'].get('podStatic', None) - uid = d['kp']['spec']['podUid'] - # FIXME(dulek): This is weirdly structured for upgrades support. - # If podStatic is not set (KuryrPort created by old - # Kuryr version), then on uid mismatch we're fetching - # pod from API and check if it's static here. Pods - # are quite ephemeral, so will gradually get replaced - # after the upgrade and in a while all should have - # the field set and the performance penalty should - # be resolved. Remove in the future. - if 'K8S_POD_UID' in params.args and uid != params.args.K8S_POD_UID: - if static is None: - pod = self._get_pod(params) - static = k_utils.is_pod_static(pod) - - # Static pods have mirror pod UID in API, so it's always - # mismatched. We don't raise in that case. See [1] for more. - # [1] https://github.com/k8snetworkplumbingwg/multus-cni/ - # issues/773 - if not static: - raise exceptions.CNIPodUidMismatch( - kp_name, params.args.K8S_POD_UID, uid) - return d - - try: - d = find() - return d['kp'], d['vifs'] - except KeyError: - data = {'metadata': {'name': params.args.K8S_POD_NAME, - 'namespace': params.args.K8S_POD_NAMESPACE}} - pod = k_utils.get_referenced_object(data, 'Pod') - self.k8s.add_event(pod, 'CNITimeoutKuryrPortRegistry', - f'Timed out waiting for Neutron ports to be ' - f'created for {kp_name}. Check ' - f'kuryr-controller logs.', 'Warning', - 'kuryr-daemon') - raise exceptions.CNIKuryrPortTimeout(kp_name) - - def _do_work(self, params, fn, timeout): - kp, vifs = self._get_vifs_from_registry(params, timeout) - - for ifname, vif in vifs.items(): - is_default_gateway = (ifname == k_const.DEFAULT_IFNAME) - if is_default_gateway: - # NOTE(ygupta): if this is the default interface, we should - # use the ifname supplied in the CNI ADD request - ifname = params.CNI_IFNAME - - fn(vif, self._get_inst(kp), ifname, params.CNI_NETNS, - report_health=self.report_drivers_health, - is_default_gateway=is_default_gateway, - container_id=params.CNI_CONTAINERID) - return vifs - - def _get_inst(self, kp): - return (obj_vif.instance_info - .InstanceInfo(uuid=kp['spec']['podUid'], - name=kp['metadata']['name'])) diff --git a/kuryr_kubernetes/cni/prometheus_exporter.py b/kuryr_kubernetes/cni/prometheus_exporter.py deleted file mode 100644 index d04b39621..000000000 --- a/kuryr_kubernetes/cni/prometheus_exporter.py +++ /dev/null @@ -1,71 +0,0 @@ -# Copyright 2020 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. - -import flask -import prometheus_client -from prometheus_client.exposition import generate_latest - -from oslo_config import cfg -from oslo_log import log as logging - - -LOG = logging.getLogger(__name__) -CONF = cfg.CONF -_INF = float("inf") - - -class CNIPrometheusExporter(object): - """Provides metrics to Prometheus""" - - def __init__(self): - self.application = flask.Flask('prometheus-exporter') - self.ctx = None - self.application.add_url_rule( - '/metrics', methods=['GET'], view_func=self.metrics) - self.headers = {'Connection': 'close'} - self._create_metric() - - def update_metric(self, labels, duration): - """Observes the request duration value and count it in buckets""" - self.cni_requests_duration.labels(**labels).observe(duration) - - def metrics(self): - """Provides the registered metrics""" - collected_metric = generate_latest(self.registry) - return flask.Response(collected_metric, mimetype='text/plain') - - def run(self): - # Disable obtrusive werkzeug logs. - logging.getLogger('werkzeug').setLevel(logging.WARNING) - - address = '::' - try: - LOG.info('Starting CNI Prometheus exporter') - self.application.run( - address, CONF.prometheus_exporter.cni_exporter_port) - except Exception: - LOG.exception('Failed to start Prometheus exporter') - raise - - def _create_metric(self): - """Creates a registry and records a new Histogram metric.""" - self.registry = prometheus_client.CollectorRegistry() - metric_name = 'kuryr_cni_request_duration_seconds' - metric_description = 'The duration of CNI requests' - buckets = (10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, - 130, 140, 150, 160, 170, 180, _INF) - self.cni_requests_duration = prometheus_client.Histogram( - metric_name, metric_description, - labelnames={'command', 'error'}, buckets=buckets, - registry=self.registry) diff --git a/kuryr_kubernetes/cni/utils.py b/kuryr_kubernetes/cni/utils.py deleted file mode 100644 index 2e2f796cb..000000000 --- a/kuryr_kubernetes/cni/utils.py +++ /dev/null @@ -1,111 +0,0 @@ -# Copyright (c) 2017 NEC Technologies India Pvt Ltd. -# 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 functools -import time - -from http import client as httplib -from oslo_log import log as logging - - -PROC_ONE_CGROUP_PATH = '/proc/1/cgroup' -CONTAINER_RUNTIME_CGROUP_IDS = ( - 'docker', # This is set by docker/moby - 'libpod', # This is set by podman -) - -LOG = logging.getLogger(__name__) -SUCCESSFUL_REQUEST_CODE = (httplib.NO_CONTENT, httplib.ACCEPTED) - - -def running_under_container_runtime(proc_one_cg_path=PROC_ONE_CGROUP_PATH): - """Returns True iff the CNI process is under a known container runtime.""" - with open(proc_one_cg_path, 'r') as cgroup_info: - proc_one_cg_info = cgroup_info.read() - return any(runtime in proc_one_cg_info for runtime in - CONTAINER_RUNTIME_CGROUP_IDS) - - -def any_vif_inactive(vifs): - """Return True if there is at least one VIF that's not ACTIVE.""" - return any(not vif.active for vif in vifs.values()) - - -class CNIConfig(dict): - def __init__(self, cfg): - super(CNIConfig, self).__init__(cfg) - - for k, v in self.items(): - 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) - - def __contains__(self, key): - return hasattr(self, key) - - -class CNIParameters(object): - def __init__(self, env, cfg=None): - for k, v in env.items(): - if k.startswith('CNI_'): - setattr(self, k, v) - if cfg is None: - self.config = CNIConfig(env['config_kuryr']) - else: - self.config = cfg - self.args = CNIArgs(self.CNI_ARGS) - - def __repr__(self): - return repr({key: value for key, value in self.__dict__.items() if - key.startswith('CNI_')}) - - -def log_ipdb(func): - @functools.wraps(func) - def with_logging(*args, **kwargs): - try: - return func(*args, **kwargs) - except RuntimeError as e: - try: - LOG.error('Error when manipulating network interfaces') - LOG.error(e.debug['traceback']) - LOG.debug('Full debugging info: %s', e.debug) - except AttributeError: - pass - raise - return with_logging - - -def measure_time(command): - """Measures CNI ADD/DEL resquest duration""" - def decorator(method): - def wrapper(obj, *args, **kwargs): - start_time = time.time() - result = method(obj, *args, **kwargs) - cni_request_error = ( - result[1] not in SUCCESSFUL_REQUEST_CODE) - obj._update_metrics( - command, cni_request_error, time.time() - start_time) - return result - wrapper.__name__ = method.__name__ - return wrapper - return decorator diff --git a/kuryr_kubernetes/config.py b/kuryr_kubernetes/config.py deleted file mode 100644 index 3ca014fa9..000000000 --- a/kuryr_kubernetes/config.py +++ /dev/null @@ -1,352 +0,0 @@ -# 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 os -import sys - -from kuryr.lib._i18n import _ -from kuryr.lib import config as lib_config -from oslo_config import cfg -from oslo_log import log as logging - -from kuryr_kubernetes import version - -LOG = logging.getLogger(__name__) - -kuryr_k8s_opts = [ - cfg.StrOpt('pybasedir', - help=_('Directory where Kuryr-kubernetes python module is ' - 'installed.'), - default=os.path.abspath( - os.path.join(os.path.dirname(__file__), - '../../'))), -] - -daemon_opts = [ - cfg.StrOpt('bind_address', - help=_('Bind address for CNI daemon HTTP server. It is ' - 'recommened to allow only local connections.'), - default='127.0.0.1:5036'), - cfg.IntOpt('worker_num', - help=_('Maximum number of processes that will be spawned to ' - 'process requests from CNI driver.'), - default=30), - cfg.IntOpt('vif_annotation_timeout', - help=_('Time (in seconds) the CNI daemon will wait for VIF ' - 'annotation to appear in pod metadata before failing ' - 'the CNI request.'), - default=60), - cfg.IntOpt('pyroute2_timeout', - help=_('Kuryr uses pyroute2 library to manipulate networking ' - 'interfaces. When processing a high number of Kuryr ' - 'requests in parallel, it may take kernel more time to ' - 'process all networking stack changes. This option ' - 'allows to tune internal pyroute2 timeout.'), - default=10), - cfg.BoolOpt('docker_mode', - help=_('Set to True when you are running kuryr-daemon inside ' - 'a Docker container on Kubernetes host. E.g. as ' - 'DaemonSet on Kubernetes cluster Kuryr is supposed to ' - 'provide networking for. This mainly means that ' - 'kuryr-daemon will look for network namespaces in ' - '$netns_proc_dir instead of /proc.'), - default=False), - cfg.StrOpt('netns_proc_dir', - help=_("When docker_mode is set to True, this config option " - "should be set to where host's /proc directory is " - "mounted. Please note that mounting it is necessary to " - "allow Kuryr-Kubernetes to move host interfaces between " - "host network namespaces, which is essential for Kuryr " - "to work."), - default=None), - cfg.IntOpt('cni_failures_count', - help=_('Maximum number of consecutive failures of kuryr-daemon ' - 'when processing requests. If this number is exceeded, ' - 'kuryr-daemon will be marked as unhealthy.'), - default=3), -] - -k8s_opts = [ - cfg.StrOpt('api_root', - help=_("The root URL of the Kubernetes API"), - default=os.environ.get('K8S_API', 'https://localhost:6443')), - cfg.StrOpt('ssl_client_crt_file', - help=_("Absolute path to client cert to " - "connect to HTTPS K8S_API")), - cfg.StrOpt('ssl_client_key_file', - help=_("Absolute path client key file to " - "connect to HTTPS K8S_API")), - cfg.StrOpt('ssl_ca_crt_file', - help=_("Absolute path to ca cert file to " - "connect to HTTPS K8S_API"), - default='/var/run/secrets/kubernetes.io/serviceaccount/ca.crt'), - cfg.BoolOpt('ssl_verify_server_crt', - help=_("HTTPS K8S_API server identity verification"), - default=False), - cfg.StrOpt('token_file', - help=_("The token to talk to the k8s API"), - default='/var/run/secrets/kubernetes.io/serviceaccount/token'), - cfg.StrOpt('pod_project_driver', - help=_("The driver to determine OpenStack project for pod " - "ports (default or annotation)"), - default='default'), - cfg.StrOpt('service_project_driver', - help=_("The driver to determine OpenStack project for " - "services (default or annotation)"), - default='default'), - cfg.StrOpt('namespace_project_driver', - help=_("The driver to determine OpenStack project for " - "namespaces (default or annotation)"), - default='default'), - cfg.StrOpt('network_policy_project_driver', - help=_("The driver to determine OpenStack project for network " - "policies (default or annotation)"), - default='default'), - cfg.StrOpt('pod_subnets_driver', - help=_("The driver to determine Neutron " - "subnets for pod ports"), - default='default'), - cfg.StrOpt('service_subnets_driver', - help=_("The driver to determine Neutron " - "subnets for services"), - default='default'), - cfg.StrOpt('pod_security_groups_driver', - help=_("The driver to determine Neutron " - "security groups for pods"), - default='default'), - cfg.StrOpt('service_security_groups_driver', - help=_("The driver to determine Neutron " - "security groups for services"), - default='default'), - cfg.StrOpt('pod_vif_driver', - help=_("The driver that provides VIFs for Kubernetes Pods."), - default='neutron-vif'), - cfg.StrOpt('endpoints_lbaas_driver', - help=_("The driver that provides LoadBalancers for " - "Kubernetes Endpoints"), - default='lbaasv2'), - cfg.StrOpt('endpoints_driver_octavia_provider', - help=_("The Octavia load balancer provider that will be used " - "to support Kubernetes Endpoints"), - default='default'), - cfg.StrOpt('vif_pool_driver', - help=_("The driver that manages VIFs pools for " - "Kubernetes Pods"), - default='noop'), - cfg.StrOpt('nodes_subnets_driver', - help=_("The driver that manages listing K8s nodes subnet_ids."), - default='config'), - cfg.BoolOpt('port_debug', - help=_('Enable port debug to force kuryr port names to be ' - 'set to their corresponding pod names.'), - default=False), - cfg.StrOpt('service_public_ip_driver', - help=_("The driver that provides external IP for LB at " - "Kubernetes"), - default='neutron_floating_ip'), - cfg.BoolOpt('enable_manager', - help=_("Enable Manager to manage the pools."), - default=False), - cfg.IntOpt('watch_retry_timeout', - help=_('Time (in seconds) the watcher retries watching for.'), - default=60), - cfg.IntOpt('watch_connection_timeout', - help=_('TCP connection timeout (in seconds) for the watcher ' - 'connections to K8s API.'), - default=30), - cfg.IntOpt('watch_read_timeout', - help=_('TCP read timeout (in seconds) for the watcher ' - 'connections to K8s API. This affects reaction to time ' - 'when there are no events being streamed from K8s API. ' - 'When too low, Kuryr will reconnect more often. When ' - 'too high, Kuryr will take longer to reconnect when K8s ' - 'API stream was being silently broken.'), - default=60), - cfg.IntOpt('watch_reconcile_period', - help=_('Period (in seconds) between iterations of fetching ' - 'full list of watched K8s API resources and putting ' - 'them into the enabled handlers. Setting 0 disables the ' - 'periodic reconciling. The reconciliation is done to ' - 'prevent Kuryr from missing events due to K8s API or ' - 'etcd issues.'), - default=120), - cfg.ListOpt('enabled_handlers', - help=_("The comma-separated handlers that should be " - "registered for watching in the pipeline."), - default=['vif', 'endpoints', 'service', 'kuryrloadbalancer', - 'kuryrport']), - cfg.BoolOpt('controller_ha', - help=_('Enable kuryr-controller active/passive HA. Only ' - 'supported in containerized deployments on Kubernetes ' - 'or OpenShift.'), - default=False), - cfg.PortOpt('controller_ha_elector_port', - help=_('Port on which leader-elector pod is listening to.'), - default=16401), - cfg.StrOpt('network_policy_driver', - help=_("Driver for network policies"), - default='default'), - cfg.ListOpt('multi_vif_drivers', - help=_("The drivers that provide additional VIFs for " - "Kubernetes Pods."), - default='noop'), - cfg.StrOpt('additional_ifname_prefix', - help=_("The prefix to use for additional vifs created by " - " multi_vif drivers"), - default='eth'), - cfg.BoolOpt('use_events', - help=_('Use Kubernetes Events objects to indicate status of ' - 'Kuryr created OpenStack objects like networking for ' - 'pods (Neutron ports) or services (Octavia ' - 'loadbalancers). It might have impact on performance ' - 'on Kubernetes cluster, since all objects (so the ' - 'Event objects too) are stored on etcd.'), - default=True), -] - -neutron_defaults = [ - cfg.StrOpt('project', - help=_("Default OpenStack project ID for " - "Kubernetes resources")), - cfg.StrOpt('pod_subnet', - help=_("Default Neutron subnet ID for Kubernetes pods")), - cfg.ListOpt('pod_security_groups', - help=_("Default Neutron security groups' IDs " - "for Kubernetes pods")), - cfg.StrOpt('ovs_bridge', - help=_("Default OpenVSwitch integration bridge"), - sample_default="br-int"), - cfg.StrOpt('service_subnet', - help=_("Default Neutron subnet ID for Kubernetes services")), - cfg.StrOpt('external_svc_net', - help=_("Default external network ID for Kubernetes services")), - cfg.StrOpt('external_svc_subnet', - help=_("Optional external subnet ID for Kubernetes services"), - default=None), - cfg.IntOpt('network_device_mtu', - help='Default MTU setting for network interface.', - default=0,), - cfg.IntOpt('lbaas_activation_timeout', - help=_("Time (in seconds) that kuryr controller waits for " - "neutron LBaaS to be activated"), - default=300), - cfg.DictOpt('subnet_mapping', - help=_("A mapping of default subnets for certain driverType " - "in a form of :"), - default={}), - cfg.ListOpt('resource_tags', - help=_("List of tags that will be applied to all OpenStack " - "(Neutron and Octavia) resources created by Kuryr. " - "This can be used to identify and garbage-collect " - "them when Kubernetes cluster Kuryr was serving is no " - "longer needed."), - default=[]), -] - -octavia_defaults = [ - cfg.StrOpt('member_mode', - help=_("Define the communication mode between load balanacer " - "and its members"), - default='L3'), - cfg.BoolOpt('enforce_sg_rules', - help=_("Enable the enforcement of SG rules at the LB SG " - "in case the LB does not maintain the source IP " - "of the caller resource"), - default=True), - cfg.StrOpt('lb_algorithm', - help=_("The load-balancer algorithm that distributed traffic " - "to the pool members. The options are: ROUND_ROBIN, " - "LEAST_CONNECTIONS, SOURCE_IP and SOURCE_IP_PORT."), - default='ROUND_ROBIN'), - cfg.IntOpt('timeout_client_data', - help=_("Frontend client inactivity timeout in milliseconds. " - "When set to 0, the default timeout value set by " - "Octavia is used."), - default=0), - cfg.IntOpt('timeout_member_data', - help=_("Backend member inactivity timeout in milliseconds. " - "When set to 0, the default timeout value set by " - "Octavia is used."), - default=0), -] - -cache_defaults = [ - cfg.BoolOpt('enabled', - help=_("Enable caching."), - default=True), - cfg.StrOpt('backend', - help=_("Select backend cache option."), - default="dogpile.cache.memory"), -] - -nested_vif_driver_opts = [ - cfg.ListOpt('worker_nodes_subnets', - help=_("Neutron subnet IDs for k8s worker node VMs."), - default=[]), - cfg.IntOpt('rev_update_attempts', - help=_("How many time to try to re-update the neutron resource " - "when revision has been changed by other thread"), - default=3), -] - -vhostuser = [ - cfg.StrOpt('mount_point', - help=_("Path where vhost-user port will be created " - "also it should be mount point for pod"), - default='/var/cni/vhostuser'), - cfg.StrOpt('ovs_vhu_path', - help=_("Path where OVS keeps socket files for vhost-user " - "ports"), - default='/var/run/openvswitch/') -] - -prometheus_exporter_opts = [ - cfg.IntOpt('controller_exporter_port', - help=_('port for the Controller Prometheus exporter.'), - default=9654), - cfg.IntOpt('cni_exporter_port', - help=_('port for the CNI Prometheus exporter.'), - default=9655) -] - -CONF = cfg.CONF -CONF.register_opts(kuryr_k8s_opts) -CONF.register_opts(daemon_opts, group='cni_daemon') -CONF.register_opts(k8s_opts, group='kubernetes') -CONF.register_opts(neutron_defaults, group='neutron_defaults') -CONF.register_opts(octavia_defaults, group='octavia_defaults') -CONF.register_opts(cache_defaults, group='cache_defaults') -CONF.register_opts(nested_vif_driver_opts, group='pod_vif_nested') -CONF.register_opts(vhostuser, group='vhostuser') -CONF.register_opts(prometheus_exporter_opts, "prometheus_exporter") - -CONF.register_opts(lib_config.core_opts) -CONF.register_opts(lib_config.binding_opts, 'binding') -lib_config.register_neutron_opts(CONF) - -logging.register_options(CONF) - - -def init(args, **kwargs): - version_k8s = version.version_info.version_string() - CONF(args=args, project='kuryr-k8s', version=version_k8s, **kwargs) - if os.environ.get('CNI_COMMAND') == 'VERSION': - CONF.set_default('use_stderr', True) - - -def setup_logging(): - - logging.setup(CONF, 'kuryr-kubernetes') - logging.set_defaults(default_log_levels=logging.get_default_log_levels()) - version_k8s = version.version_info.version_string() - LOG.info("Logging enabled!") - LOG.info("%(prog)s version %(version)s", - {'prog': sys.argv[0], 'version': version_k8s}) diff --git a/kuryr_kubernetes/constants.py b/kuryr_kubernetes/constants.py deleted file mode 100644 index 927967244..000000000 --- a/kuryr_kubernetes/constants.py +++ /dev/null @@ -1,113 +0,0 @@ -# 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. - -KURYR_FQDN = 'kuryr.openstack.org' - -K8S_API_BASE = '/api/v1' -K8S_API_PODS = K8S_API_BASE + '/pods' -K8S_API_NAMESPACES = K8S_API_BASE + '/namespaces' -K8S_API_CRD_VERSION = 'openstack.org/v1' -K8S_API_CRD = '/apis/' + K8S_API_CRD_VERSION -K8S_API_CRD_NAMESPACES = K8S_API_CRD + '/namespaces' -K8S_API_CRD_KURYRNETWORKS = K8S_API_CRD + '/kuryrnetworks' -K8S_API_CRD_KURYRNETWORKPOLICIES = K8S_API_CRD + '/kuryrnetworkpolicies' -K8S_API_CRD_KURYRLOADBALANCERS = K8S_API_CRD + '/kuryrloadbalancers' -K8S_API_CRD_KURYRPORTS = K8S_API_CRD + '/kuryrports' -K8S_API_POLICIES = '/apis/networking.k8s.io/v1/networkpolicies' -K8S_API_NETWORKING = '/apis/networking.k8s.io/v1' -K8S_API_NETWORKING_NAMESPACES = K8S_API_NETWORKING + '/namespaces' - -K8S_API_NPWG_CRD = '/apis/k8s.cni.cncf.io/v1' - -K8S_OBJ_NAMESPACE = 'Namespace' -K8S_OBJ_POD = 'Pod' -K8S_OBJ_SERVICE = 'Service' -K8S_OBJ_ENDPOINTS = 'Endpoints' -K8S_OBJ_POLICY = 'NetworkPolicy' -K8S_OBJ_KURYRNETWORK = 'KuryrNetwork' -K8S_OBJ_KURYRNETWORKPOLICY = 'KuryrNetworkPolicy' -K8S_OBJ_KURYRLOADBALANCER = 'KuryrLoadBalancer' -K8S_OBJ_KURYRPORT = 'KuryrPort' - -OPENSHIFT_OBJ_MACHINE = 'Machine' -OPENSHIFT_API_CRD_MACHINES = '/apis/machine.openshift.io/v1beta1/machines' - -K8S_POD_STATUS_PENDING = 'Pending' -K8S_POD_STATUS_SUCCEEDED = 'Succeeded' -K8S_POD_STATUS_FAILED = 'Failed' - -K8S_NODE_ADDRESS_INTERNAL = 'InternalIP' - -K8S_ANNOTATION_PREFIX = 'openstack.org/kuryr' -K8S_ANNOTATION_VIF = K8S_ANNOTATION_PREFIX + '-vif' -K8S_ANNOTATION_LABEL = K8S_ANNOTATION_PREFIX + '-pod-label' -K8S_ANNOTATION_IP = K8S_ANNOTATION_PREFIX + '-pod-ip' -K8S_ANNOTATION_NAMESPACE_LABEL = K8S_ANNOTATION_PREFIX + '-namespace-label' -K8S_ANNOTATION_LBAAS_SPEC = K8S_ANNOTATION_PREFIX + '-lbaas-spec' -K8S_ANNOTATION_LBAAS_STATE = K8S_ANNOTATION_PREFIX + '-lbaas-state' -K8S_ANNOTATION_NET_CRD = K8S_ANNOTATION_PREFIX + '-net-crd' -K8S_ANNOTATION_NETPOLICY_CRD = K8S_ANNOTATION_PREFIX + '-netpolicy-crd' -K8S_ANNOTATION_POLICY = K8S_ANNOTATION_PREFIX + '-counter' -K8s_ANNOTATION_PROJECT = K8S_ANNOTATION_PREFIX + '-project' - -K8S_ANNOTATION_CLIENT_TIMEOUT = K8S_ANNOTATION_PREFIX + '-timeout-client-data' -K8S_ANNOTATION_MEMBER_TIMEOUT = K8S_ANNOTATION_PREFIX + '-timeout-member-data' - -K8S_ANNOTATION_NPWG_PREFIX = 'k8s.v1.cni.cncf.io' -K8S_ANNOTATION_NPWG_NETWORK = K8S_ANNOTATION_NPWG_PREFIX + '/networks' -K8S_ANNOTATION_NPWG_CRD_SUBNET_ID = 'subnetId' -K8S_ANNOTATION_NPWG_CRD_DRIVER_TYPE = 'driverType' - -K8S_ANNOTATION_HEADLESS_SERVICE = 'service.kubernetes.io/headless' -K8S_ANNOTATION_CONFIG_SOURCE = 'kubernetes.io/config.source' - -POD_FINALIZER = KURYR_FQDN + '/pod-finalizer' -KURYRNETWORK_FINALIZER = 'kuryrnetwork.finalizers.kuryr.openstack.org' -KURYRLB_FINALIZER = 'kuryr.openstack.org/kuryrloadbalancer-finalizers' -SERVICE_FINALIZER = 'kuryr.openstack.org/service-finalizer' -NETWORKPOLICY_FINALIZER = 'kuryr.openstack.org/networkpolicy-finalizer' - -KURYRPORT_FINALIZER = KURYR_FQDN + '/kuryrport-finalizer' -KURYRPORT_LABEL = KURYR_FQDN + '/nodeName' - -K8S_OS_VIF_NOOP_PLUGIN = "noop" - -CNI_EXCEPTION_CODE = 100 -CNI_TIMEOUT_CODE = 200 -CNI_DELETED_POD_SENTINEL = None - -KURYR_PORT_NAME = 'kuryr-pool-port' - -OCTAVIA_L2_MEMBER_MODE = "L2" -OCTAVIA_L3_MEMBER_MODE = "L3" -NEUTRON_LBAAS_HAPROXY_PROVIDER = 'haproxy' -IPv4 = 'IPv4' -IPv6 = 'IPv6' -IP_VERSION_4 = 4 -IP_VERSION_6 = 6 - -VIF_POOL_POPULATE = '/populatePool' -VIF_POOL_FREE = '/freePool' -VIF_POOL_LIST = '/listPools' -VIF_POOL_SHOW = '/showPool' - -DEFAULT_IFNAME = 'eth0' - -K8S_OPERATOR_IN = 'in' -K8S_OPERATOR_NOT_IN = 'notin' -K8S_OPERATOR_DOES_NOT_EXIST = 'doesnotexist' -K8S_OPERATOR_EXISTS = 'exists' - -LEFTOVER_RM_POOL_SIZE = 5 diff --git a/kuryr_kubernetes/controller/__init__.py b/kuryr_kubernetes/controller/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/kuryr_kubernetes/controller/drivers/__init__.py b/kuryr_kubernetes/controller/drivers/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/kuryr_kubernetes/controller/drivers/annotation_project.py b/kuryr_kubernetes/controller/drivers/annotation_project.py deleted file mode 100644 index 04acc359d..000000000 --- a/kuryr_kubernetes/controller/drivers/annotation_project.py +++ /dev/null @@ -1,69 +0,0 @@ -# Copyright (c) 2022 Troila -# 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. - -from oslo_config import cfg -from oslo_log import log as logging - -from kuryr_kubernetes import config -from kuryr_kubernetes import constants -from kuryr_kubernetes.controller.drivers import base -from kuryr_kubernetes.controller.drivers import utils as driver_utils - -LOG = logging.getLogger(__name__) - - -class AnnotationProjectBaseDriver( - base.PodProjectDriver, base.ServiceProjectDriver, - base.NamespaceProjectDriver, base.NetworkPolicyProjectDriver): - """Provides project ID based on resource's annotation.""" - - project_annotation = constants.K8s_ANNOTATION_PROJECT - - def _get_namespace_project(self, namespace): - ns_md = namespace['metadata'] - project = ns_md.get('annotations', {}).get(self.project_annotation) - if not project: - LOG.debug("Namespace %s has no project annotation, try to get " - "project id from the configuration option.", - namespace['metadata']['name']) - project = config.CONF.neutron_defaults.project - if not project: - raise cfg.RequiredOptError('project', - cfg.OptGroup('neutron_defaults')) - return project - - def get_project(self, resource): - res_ns = resource['metadata']['namespace'] - namespace_path = f"{constants.K8S_API_NAMESPACES}/{res_ns}" - namespace = driver_utils.get_k8s_resource(namespace_path) - return self._get_namespace_project(namespace) - - -class AnnotationPodProjectDriver(AnnotationProjectBaseDriver): - pass - - -class AnnotationServiceProjectDriver(AnnotationProjectBaseDriver): - pass - - -class AnnotationNamespaceProjectDriver(AnnotationProjectBaseDriver): - - def get_project(self, namespace): - return self._get_namespace_project(namespace) - - -class AnnotationNetworkPolicyProjectDriver(AnnotationProjectBaseDriver): - pass diff --git a/kuryr_kubernetes/controller/drivers/base.py b/kuryr_kubernetes/controller/drivers/base.py deleted file mode 100644 index 16d11660a..000000000 --- a/kuryr_kubernetes/controller/drivers/base.py +++ /dev/null @@ -1,781 +0,0 @@ -# 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 - -from kuryr.lib._i18n import _ -from stevedore import driver as stv_driver - -from kuryr_kubernetes import config - -_DRIVER_NAMESPACE_BASE = 'kuryr_kubernetes.controller.drivers' -_DRIVER_MANAGERS = {} -_MULTI_VIF_DRIVERS = [] - - -class DriverBase(object): - """Base class for controller drivers. - - Subclasses must define an *ALIAS* attribute that is used to find a driver - implementation by `get_instance` class method which utilises - `stevedore.driver.DriverManager` with the namespace set to - 'kuryr_kubernetes.controller.drivers.*ALIAS*' and the name of - the driver determined from the '[kubernetes]/*ALIAS*_driver' configuration - parameter. - - Usage example: - - class SomeDriverInterface(DriverBase, metaclass=abc.ABCMeta): - ALIAS = 'driver_alias' - - @abc.abstractmethod - def some_method(self): - pass - - driver = SomeDriverInterface.get_instance() - driver.some_method() - """ - - @classmethod - def get_instance(cls, specific_driver=None, scope='default'): - """Get an implementing driver instance. - - :param specific_driver: Loads a specific driver instead of using conf. - Uses separate manager entry so that loading of - default/other drivers is not affected. - :param scope: Loads the driver in the given scope (if independent - instances of a driver are required) - """ - - alias = cls.ALIAS - - if specific_driver: - driver_key = '{}:{}:{}'.format(alias, specific_driver, scope) - else: - driver_key = '{}:_from_cfg:{}'.format(alias, scope) - - try: - manager = _DRIVER_MANAGERS[driver_key] - except KeyError: - driver_name = (specific_driver or - config.CONF.kubernetes[alias + '_driver']) - - manager = stv_driver.DriverManager( - namespace="%s.%s" % (_DRIVER_NAMESPACE_BASE, alias), - name=driver_name, - invoke_on_load=True) - _DRIVER_MANAGERS[driver_key] = manager - - driver = manager.driver - if not isinstance(driver, cls): - raise TypeError(_("Invalid %(alias)r driver type: %(driver)s, " - "must be a subclass of %(type)s") % { - 'alias': alias, - 'driver': driver.__class__.__name__, - 'type': cls}) - return driver - - def __str__(self): - return self.__class__.__name__ - - -class PodProjectDriver(DriverBase, metaclass=abc.ABCMeta): - """Provides an OpenStack project ID for Kubernetes Pod ports.""" - - ALIAS = 'pod_project' - - @abc.abstractmethod - def get_project(self, pod): - """Get an OpenStack project ID for Kubernetes Pod ports. - - :param pod: dict containing Kubernetes Pod object - :return: project ID - """ - - raise NotImplementedError() - - -class ServiceProjectDriver(DriverBase, metaclass=abc.ABCMeta): - """Provides an OpenStack project ID for Kubernetes Services.""" - - ALIAS = 'service_project' - - @abc.abstractmethod - def get_project(self, service): - """Get an OpenStack project ID for Kubernetes Service. - - :param service: dict containing Kubernetes Service object - :return: project ID - """ - - raise NotImplementedError() - - -class NamespaceProjectDriver(DriverBase, metaclass=abc.ABCMeta): - """Provides an OpenStack project ID for Kubernetes Namespace.""" - - ALIAS = 'namespace_project' - - @abc.abstractmethod - def get_project(self, namespace): - """Get an OpenStack project ID for Kubernetes Namespace. - - :param service: dict containing Kubernetes Namespace object - :return: project ID - """ - - raise NotImplementedError() - - -class PodSubnetsDriver(DriverBase, metaclass=abc.ABCMeta): - """Provides subnets for Kubernetes Pods.""" - - ALIAS = 'pod_subnets' - - @abc.abstractmethod - def get_subnets(self, pod, project_id): - """Get subnets for Pod. - - :param pod: dict containing Kubernetes Pod object - :param project_id: OpenStack project ID - :return: dict containing the mapping 'subnet_id' -> 'network' for all - the subnets we want to create ports on, where 'network' is an - `os_vif.network.Network` object containing a single - `os_vif.subnet.Subnet` object corresponding to the 'subnet_id' - """ - raise NotImplementedError() - - def create_namespace_network(self, namespace, project_id): - """Create network resources for a namespace. - - :param namespace: string with the namespace name - :param project_id: OpenStack project ID - :return: dict with the keys and values for the CRD spec, such as - routerId or subnetId - """ - raise NotImplementedError() - - def delete_namespace_subnet(self, kuryr_net_crd): - """Delete network resources associated to a namespace. - - :param kuryr_net_crd: kuryrnetwork CRD obj dict that contains Neutron's - network resources associated to a namespace - """ - raise NotImplementedError() - - def rollback_network_resources(self, crd_spec, namespace): - """Rollback created network resources for a namespace. - - :param crd_spec: dict with the keys and values for the CRD spec, such - as routerId or subnetId - :param namespace: name of the Kubernetes namespace object - """ - raise NotImplementedError() - - def cleanup_namespace_networks(self, namespace): - """Clean up network leftover on the namespace. - - Due to Kuryr controller restarts it may happen that some network - resources are leftover. This method ensures they are deleted upon - retries. - - :param namespace: name of the Kubernetes namespace object - """ - raise NotImplementedError() - - -class ServiceSubnetsDriver(DriverBase, metaclass=abc.ABCMeta): - """Provides subnets for Kubernetes Services.""" - - ALIAS = 'service_subnets' - - @abc.abstractmethod - def get_subnets(self, service, project_id): - """Get subnets for Service. - - :param service: dict containing Kubernetes Pod object - :param project_id: OpenStack project ID - :return: dict containing the mapping 'subnet_id' -> 'network' for all - the subnets we want to create ports on, where 'network' is an - `os_vif.network.Network` object containing a single - `os_vif.subnet.Subnet` object corresponding to the 'subnet_id' - """ - raise NotImplementedError() - - -class PodSecurityGroupsDriver(DriverBase, metaclass=abc.ABCMeta): - """Provides security groups for Kubernetes Pods.""" - - ALIAS = 'pod_security_groups' - - @abc.abstractmethod - def get_security_groups(self, pod, project_id): - """Get a list of security groups' IDs for Pod. - - :param pod: dict containing Kubernetes Pod object - :param project_id: OpenStack project ID - :return: list containing security groups' IDs - """ - raise NotImplementedError() - - def create_sg_rules(self, pod): - """Create security group rules for a pod. - - :param pod: dict containing Kubernetes Pod object - :return: a list containing podSelectors of CRDs - that had security group rules created - """ - raise NotImplementedError() - - def delete_sg_rules(self, pod): - """Delete security group rules for a pod - - :param pod: dict containing Kubernetes Pod object - :return: a list containing podSelectors of CRDs - that had security group rules deleted - """ - raise NotImplementedError() - - def update_sg_rules(self, pod): - """Update security group rules for a pod - - :param pod: dict containing Kubernetes Pod object - :return: a list containing podSelectors of CRDs - that had security group rules updated - """ - raise NotImplementedError() - - def delete_namespace_sg_rules(self, namespace): - """Delete security group rule associated to a namespace. - - :param namespace: dict containing K8S Namespace object - """ - raise NotImplementedError() - - def create_namespace_sg_rules(self, namespace): - """Create security group rule associated to a namespace. - - :param namespace: dict containing K8S Namespace object - """ - raise NotImplementedError() - - def update_namespace_sg_rules(self, namespace): - """Update security group rule associated to a namespace. - - :param namespace: dict containing K8S Namespace object - """ - raise NotImplementedError() - - -class ServiceSecurityGroupsDriver(DriverBase, metaclass=abc.ABCMeta): - """Provides security groups for Kubernetes Services.""" - - ALIAS = 'service_security_groups' - - @abc.abstractmethod - def get_security_groups(self, service, project_id): - """Get a list of security groups' IDs for Service. - - :param service: dict containing Kubernetes Service object - :param project_id: OpenStack project ID - :return: list containing security groups' IDs - """ - raise NotImplementedError() - - -class PodVIFDriver(DriverBase, metaclass=abc.ABCMeta): - """Manages Neutron ports to provide VIFs for Kubernetes Pods.""" - - ALIAS = 'pod_vif' - - @abc.abstractmethod - def request_vif(self, pod, project_id, subnets, security_groups): - """Links Neutron port to pod and returns it as VIF object. - - Implementing drivers must ensure the Neutron port satisfying the - requested parameters is present and is valid for specified `pod`. It - is up to the implementing drivers to either create new ports on each - request or reuse available ports when possible. - - Implementing drivers may return a VIF object with its `active` field - set to 'False' to indicate that Neutron port requires additional - actions to enable network connectivity after VIF is plugged (e.g. - setting up OpenFlow and/or iptables rules by OpenVSwitch agent). In - that case the Controller will call driver's `activate_vif` method - and the CNI plugin will block until it receives activation - confirmation from the Controller. - - :param pod: dict containing Kubernetes Pod object - :param project_id: OpenStack project ID - :param subnets: dict containing subnet mapping as returned by - `PodSubnetsDriver.get_subnets`. If multiple entries - are present in that mapping, it is guaranteed that - all entries have the same value of `Network.id`. - :param security_groups: list containing security groups' IDs as - returned by - `PodSecurityGroupsDriver.get_security_groups` - :return: VIF object - """ - raise NotImplementedError() - - def request_vifs(self, pod, project_id, subnets, security_groups, - num_ports, semaphore): - """Creates Neutron ports for pods and returns them as VIF objects list. - - It follows the same pattern as request_vif but creating the specified - amount of ports and vif objects at num_ports parameter. - - The port creation request is generic as it not going to be used by the - pod -- at least not all of them. Additionally, in order to save Neutron - calls, the ports creation is handled in a bulk request. - - :param pod: dict containing Kubernetes Pod object - :param project_id: OpenStack project ID - :param subnets: dict containing subnet mapping as returned by - `PodSubnetsDriver.get_subnets`. If multiple entries - are present in that mapping, it is guaranteed that - all entries have the same value of `Network.id`. - :param security_groups: list containing security groups' IDs as - returned by - `PodSecurityGroupsDriver.get_security_groups` - :param num_ports: number of ports to be created - :param semaphore: a eventlet Semaphore to limit the number of create - Port in bulk running in parallel - :return: VIF objects list - """ - raise NotImplementedError() - - @abc.abstractmethod - def release_vif(self, pod, vif, project_id=None): - """Unlinks Neutron port corresponding to VIF object from pod. - - Implementing drivers must ensure the port is either deleted or made - available for reuse by `PodVIFDriver.request_vif`. - - :param pod: dict containing Kubernetes Pod object - :param vif: VIF object as returned by `PodVIFDriver.request_vif` - :param project_id: OpenStack project ID - """ - raise NotImplementedError() - - def release_vifs(self, pods, vifs, project_id=None): - """Unlinks Neutron ports corresponding to VIF objects. - - It follows the same pattern as release_vif but releasing num_ports - ports. Ideally it will also make use of bulk request to save Neutron - calls in the release/recycle process. - :param pods: list of dict containing Kubernetes Pod objects - :param vifs: list of VIF objects as returned by - `PodVIFDriver.request_vif` - :param project_id: (optional) OpenStack project ID - """ - raise NotImplementedError() - - @abc.abstractmethod - def activate_vif(self, vif, **kwargs): - """Updates VIF to become active. - - Implementing drivers should update the specified `vif` object's - `active` field to 'True' but must ensure that the corresponding - Neutron port is fully configured (i.e. the container using the `vif` - can access the requested network resources). - - Implementing drivers may raise `ResourceNotReady` exception to - indicate that port activation should be retried later which will - cause `activate_vif` to be called again with the same arguments. - - This method may be called before, after or while the VIF is being - plugged by the CNI plugin. - - :param vif: VIF object as returned by `PodVIFDriver.request_vif` - """ - raise NotImplementedError() - - @abc.abstractmethod - def update_vif_sgs(self, pod, security_groups): - """Update VIF security groups. - - Implementing drivers should update the port associated to the pod - with the specified security groups. - - :param pod: dict containing Kubernetes Pod object - :param security_groups: list containing security groups' IDs as - returned by - `PodSecurityGroupsDriver.get_security_groups` - """ - raise NotImplementedError() - - -class MultiVIFDriver(DriverBase, metaclass=abc.ABCMeta): - """Manages additional ports of Kubernetes Pods.""" - - ALIAS = 'multi_vif' - - @abc.abstractmethod - def request_additional_vifs( - self, pod, project_id, security_groups): - """Links Neutron ports to pod and returns them as a list of VIF objects. - - Implementing drivers must be able to parse the additional interface - definition from pod. The format of the definition is up to the - implementation of each driver. Then implementing drivers must invoke - the VIF drivers to either create new Neutron ports on each request or - reuse available ports when possible. - - :param pod: dict containing Kubernetes Pod object - :param project_id: OpenStack project ID - :param security_groups: list containing security groups' IDs as - returned by - `PodSecurityGroupsDriver.get_security_groups` - :return: VIF object list - """ - raise NotImplementedError() - - @classmethod - def get_enabled_drivers(cls): - if _MULTI_VIF_DRIVERS: - pass - else: - drivers = config.CONF.kubernetes['multi_vif_drivers'] - for driver in drivers: - _MULTI_VIF_DRIVERS.append(cls.get_instance(driver)) - return _MULTI_VIF_DRIVERS - - -class LBaaSDriver(DriverBase): - """Base class for Openstack loadbalancer services.""" - - ALIAS = 'endpoints_lbaas' - - @abc.abstractmethod - def get_service_loadbalancer_name(self, namespace, svc_name): - """Generate name of a load balancer that represents K8S service. - - In case a load balancer represents K8S service/ep, the handler - should call first this API to get the load balancer name and use the - return value of this function as 'name' parameter for the - 'ensure_loadbalancer' function - - :param namespace: K8S service namespace - :param svc_name: K8S service name - """ - raise NotImplementedError() - - @abc.abstractmethod - def ensure_loadbalancer(self, name, project_id, subnet_id, ip, - security_groups_ids, service_type, provider): - """Get or create load balancer. - - :param name: LoadBlancer name - :param project_id: OpenStack project ID - :param subnet_id: Neutron subnet ID to host load balancer - :param ip: IP of the load balancer - :param security_groups_ids: security groups that should be allowed - access to the load balancer - :param service_type: K8s service type (ClusterIP or LoadBalancer) - :param provider: load balancer backend service - """ - raise NotImplementedError() - - @abc.abstractmethod - def release_loadbalancer(self, loadbalancer): - """Release load balancer. - - Should return without errors if load balancer does not exist (e.g. - already deleted). - - :param loadbalancer: `LBaaSLoadBalancer` object - """ - raise NotImplementedError() - - @abc.abstractmethod - def ensure_listener(self, loadbalancer, protocol, port): - """Get or create listener. - - :param loadbalancer: `LBaaSLoadBalancer` object - :param protocol: listener's protocol (only TCP is supported for now) - :param port: listener's port - """ - raise NotImplementedError() - - @abc.abstractmethod - def release_listener(self, loadbalancer, listener): - """Release listener. - - Should return without errors if listener or load balancer does not - exist (e.g. already deleted). - - :param loadbalancer: `LBaaSLoadBalancer` object - :param listener: `LBaaSListener` object - """ - raise NotImplementedError() - - @abc.abstractmethod - def ensure_pool(self, loadbalancer, listener): - """Get or create pool attached to Listener. - - :param loadbalancer: `LBaaSLoadBalancer` object - :param listener: `LBaaSListener` object - """ - raise NotImplementedError() - - @abc.abstractmethod - def ensure_pool_attached_to_lb(self, loadbalancer, namespace, - svc_name, protocol): - """Get or create pool attached to LoadBalancer. - - :param loadbalancer: `LBaaSLoadBalancer` object - :param namespace: K8S service's namespace - :param svc_name: K8S service's name - :param protocol: pool's protocol - """ - raise NotImplementedError() - - @abc.abstractmethod - def get_loadbalancer_pool_name(self, loadbalancer, namespace, svc_name): - """Get name of a load balancer's pool attached to LB. - - The pool's name should be unique per K8S service - - :param loadbalancer: `LBaaSLoadBalancer` object - :param namespace: K8S service's namespace - :param svc_name: K8S service's name - """ - raise NotImplementedError() - - @abc.abstractmethod - def release_pool(self, loadbalancer, pool): - """Release pool. - - Should return without errors if pool or load balancer does not exist - (e.g. already deleted). - - :param loadbalancer: `LBaaSLoadBalancer` object - :param pool: `LBaaSPool` object - """ - raise NotImplementedError() - - @abc.abstractmethod - def ensure_member(self, loadbalancer, pool, - subnet_id, ip, port, target_ref_namespace, - target_ref_name): - """Get or create member. - - :param loadbalancer: `LBaaSLoadBalancer` object - :param pool: `LBaaSPool` object - :param subnet_id: Neutron subnet ID of the target - :param ip: target's IP (e.g. Pod's IP) - :param port: target port - :param target_ref_namespace: Kubernetes EP target_ref namespace - :param target_ref_name: Kubernetes EP target_ref name - """ - raise NotImplementedError() - - @abc.abstractmethod - def release_member(self, loadbalancer, member): - """Release member. - - Should return without errors if memberor load balancer does not exist - (e.g. already deleted). - - :param loadbalancer: `LBaaSLoadBalancer` object - :param member: `LBaaSMember` object - """ - raise NotImplementedError() - - @abc.abstractmethod - def update_lbaas_sg(self, service, sgs): - """Update security group rules associated to the loadbalancer - - :param service: k8s service object - :param sgs: list of security group ids to use for updating the rules - """ - raise NotImplementedError() - - @abc.abstractmethod - def add_tags(self, resource, req): - """Add tags to a request if the resource supports it""" - raise NotImplementedError() - - -class VIFPoolDriver(PodVIFDriver, metaclass=abc.ABCMeta): - """Manages Pool of Neutron ports to provide VIFs for Kubernetes Pods.""" - - ALIAS = 'vif_pool' - - @abc.abstractmethod - def set_vif_driver(self, driver): - """Sets the driver the Pool should use to manage resources - - The driver will be used for acquiring, releasing and updating the - vif resources. - """ - raise NotImplementedError() - - @abc.abstractmethod - def remove_sg_from_pools(self, sg_id, net_id): - """Remove the SG from the ports associated to the pools. - - This method ensure that ports on net_id that belongs to pools and have - the referenced SG are updated to clean up their SGs and put back on - the default pool for that network. - - :param sg_id: Security Group ID that needs to be removed from pool - ports - :param net_id: Network ID associated to the pools to clean up, and - where the ports must belong to. - """ - raise NotImplementedError() - - -class ServicePubIpDriver(DriverBase, metaclass=abc.ABCMeta): - """Manages loadbalancerIP/public ip for neutron lbaas.""" - - ALIAS = 'service_public_ip' - - @abc.abstractmethod - def acquire_service_pub_ip_info(self, spec_type, spec_lb_ip, project_id, - port_id_to_be_associated=None): - """Get k8s service loadbalancer IP info based on service spec - - :param spec_type: service.spec.type field - :param spec_lb_ip: service spec LoadBlaceIP field - :param project_id: openstack project id - :param port_id_to_be_associated: port id to associate - - """ - raise NotImplementedError() - - @abc.abstractmethod - def release_pub_ip(self, service_pub_ip_info): - """Release (if needed) based on service_pub_ip_info content - - :param service_pub_ip_info: service loadbalancer IP info - :returns True/False - - """ - raise NotImplementedError() - - @abc.abstractmethod - def associate_pub_ip(self, service_pub_ip_info, vip_port_id): - """Associate loadbalancer IP to lbaas VIP port ID - - :param service_pub_ip_info: service loadbalancer IP info - :param vip_port_id: Lbaas VIP port id - - """ - raise NotImplementedError() - - @abc.abstractmethod - def disassociate_pub_ip(self, service_pub_ip_info): - """Disassociate loadbalancer IP and lbaas VIP port ID - - :param service_pub_ip_info: service loadbalancer IP info - - """ - - -class NetworkPolicyDriver(DriverBase, metaclass=abc.ABCMeta): - """Provide network-policy for pods""" - - ALIAS = 'network_policy' - - @abc.abstractmethod - def ensure_network_policy(self, policy): - """Policy created or updated - - :param policy: dict containing Kubernetes NP object - """ - raise NotImplementedError() - - @abc.abstractmethod - def release_network_policy(self, kuryrnetpolicy): - """Delete a network policy - - :param kuryrnetpolicy: dict containing NetworkPolicy object - """ - raise NotImplementedError() - - @abc.abstractmethod - def affected_pods(self, policy, selector=None): - """Return affected pods by the policy - - This method returns the list of pod objects affected by the policy, or - by the selector if it is specified. - - :param policy: dict containing Kubernetes NP object - :param selector: (optional) specifc pod selector - :returns: list of Pods objects affected by the policy or the selector - if it is passed - """ - raise NotImplementedError() - - @abc.abstractmethod - def namespaced_pods(self, policy): - """Return pods on the policy namespace - - This method returns the pods on the network policy namespace - - :param policy: dict containing Kubernetes NP object - :returns: list of Pods objects on the policy namespace - """ - raise NotImplementedError() - - -class NetworkPolicyProjectDriver(DriverBase, metaclass=abc.ABCMeta): - """Get an OpenStack project id for K8s network policies""" - - ALIAS = 'network_policy_project' - - @abc.abstractmethod - def get_project(self, policy): - """Get an OpenStack project id for K8s pod ports. - - :param policy: dict containing Kubernetes NP object - :returns: OpenStack project_id - """ - raise NotImplementedError() - - -class NodesSubnetsDriver(DriverBase, metaclass=abc.ABCMeta): - """Keeps list of subnet_ids of the OpenShift Nodes.""" - - ALIAS = 'nodes_subnets' - - @abc.abstractmethod - def get_nodes_subnets(self, raise_on_empty=False): - """Gets list of subnet_ids of OpenShift Nodes. - - :param raise_on_empty: whether it should raise if list is empty. - :return: list of subnets - """ - - raise NotImplementedError() - - @abc.abstractmethod - def add_node(self, node): - """Handles node addition. - - :param node: Node object - """ - pass - - @abc.abstractmethod - def delete_node(self, node): - """Handles node removal - - :param node: Node object - """ - pass diff --git a/kuryr_kubernetes/controller/drivers/default_project.py b/kuryr_kubernetes/controller/drivers/default_project.py deleted file mode 100644 index 03ed860e9..000000000 --- a/kuryr_kubernetes/controller/drivers/default_project.py +++ /dev/null @@ -1,77 +0,0 @@ -# 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. - -from oslo_config import cfg - -from kuryr_kubernetes import config -from kuryr_kubernetes.controller.drivers import base - - -class DefaultPodProjectDriver(base.PodProjectDriver): - """Provides project ID for Pod port based on a configuration option.""" - - def get_project(self, pod): - project_id = config.CONF.neutron_defaults.project - - if not project_id: - raise cfg.RequiredOptError('project', - cfg.OptGroup('neutron_defaults')) - - return project_id - - -class DefaultServiceProjectDriver(base.ServiceProjectDriver): - """Provides project ID for Service based on a configuration option.""" - - def get_project(self, service): - project_id = config.CONF.neutron_defaults.project - - if not project_id: - # NOTE(ivc): this option is only required for - # DefaultServiceProjectDriver and its subclasses, but it may be - # optional for other drivers (e.g. when each namespace has own - # project) - raise cfg.RequiredOptError('project', - cfg.OptGroup('neutron_defaults')) - - return project_id - - -class DefaultNamespaceProjectDriver(base.NamespaceProjectDriver): - """Provides project ID for Namespace based on a configuration option.""" - - def get_project(self, namespace): - project_id = config.CONF.neutron_defaults.project - - if not project_id: - # NOTE(ivc): this option is only required for - # DefaultNamespaceProjectDriver and its subclasses, but it may be - # optional for other drivers (e.g. when each namespace has own - # project) - raise cfg.RequiredOptError('project', - cfg.OptGroup('neutron_defaults')) - - return project_id - - -class DefaultNetworkPolicyProjectDriver(base.NetworkPolicyProjectDriver): - - def get_project(self, policy): - project_id = config.CONF.neutron_defaults.project - - if not project_id: - raise cfg.RequiredOptError('project', - cfg.OptGroup('neutron_defaults')) - return project_id diff --git a/kuryr_kubernetes/controller/drivers/default_security_groups.py b/kuryr_kubernetes/controller/drivers/default_security_groups.py deleted file mode 100644 index d88b6e723..000000000 --- a/kuryr_kubernetes/controller/drivers/default_security_groups.py +++ /dev/null @@ -1,81 +0,0 @@ -# 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. - -from oslo_config import cfg -from oslo_log import log as logging - -from kuryr_kubernetes import config -from kuryr_kubernetes.controller.drivers import base - -LOG = logging.getLogger(__name__) - - -class DefaultPodSecurityGroupsDriver(base.PodSecurityGroupsDriver): - """Provides security groups for Pod based on a configuration option.""" - - def get_security_groups(self, pod, project_id): - sg_list = config.CONF.neutron_defaults.pod_security_groups - - if not sg_list: - # NOTE(ivc): this option is only required for - # Default{Pod,Service}SecurityGroupsDriver and its subclasses, - # but it may be optional for other drivers (e.g. when each - # namespace has own set of security groups) - raise cfg.RequiredOptError('pod_security_groups', - cfg.OptGroup('neutron_defaults')) - - return sg_list[:] - - def create_sg_rules(self, pod): - LOG.debug("Security group driver does not create SG rules for " - "the pods.") - - def delete_sg_rules(self, pod): - LOG.debug("Security group driver does not delete SG rules for " - "the pods.") - - def update_sg_rules(self, pod): - LOG.debug("Security group driver does not update SG rules for " - "the pods.") - - def delete_namespace_sg_rules(self, namespace): - LOG.debug("Security group driver does not delete SG rules for " - "namespace.") - - def create_namespace_sg_rules(self, namespace): - LOG.debug("Security group driver does not create SG rules for " - "namespace.") - - def update_namespace_sg_rules(self, namespace): - LOG.debug("Security group driver does not update SG rules for " - "namespace.") - - -class DefaultServiceSecurityGroupsDriver(base.ServiceSecurityGroupsDriver): - """Provides security groups for Service based on a configuration option.""" - - def get_security_groups(self, service, project_id): - # NOTE(ivc): use the same option as DefaultPodSecurityGroupsDriver - sg_list = config.CONF.neutron_defaults.pod_security_groups - - if not sg_list: - # NOTE(ivc): this option is only required for - # Default{Pod,Service}SecurityGroupsDriver and its subclasses, - # but it may be optional for other drivers (e.g. when each - # namespace has own set of security groups) - raise cfg.RequiredOptError('pod_security_groups', - cfg.OptGroup('neutron_defaults')) - - return sg_list[:] diff --git a/kuryr_kubernetes/controller/drivers/default_subnet.py b/kuryr_kubernetes/controller/drivers/default_subnet.py deleted file mode 100644 index e39294c58..000000000 --- a/kuryr_kubernetes/controller/drivers/default_subnet.py +++ /dev/null @@ -1,54 +0,0 @@ -# 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. - -from oslo_config import cfg - -from kuryr_kubernetes import config -from kuryr_kubernetes.controller.drivers import base -from kuryr_kubernetes import utils - - -class DefaultPodSubnetDriver(base.PodSubnetsDriver): - """Provides subnet for Pod port based on a configuration option.""" - - def get_subnets(self, pod, project_id): - subnet_id = config.CONF.neutron_defaults.pod_subnet - - if not subnet_id: - # NOTE(ivc): this option is only required for - # DefaultPodSubnetDriver and its subclasses, but it may be - # optional for other drivers (e.g. when each namespace has own - # subnet) - raise cfg.RequiredOptError('pod_subnet', - cfg.OptGroup('neutron_defaults')) - - return {subnet_id: utils.get_subnet(subnet_id)} - - -class DefaultServiceSubnetDriver(base.ServiceSubnetsDriver): - """Provides subnet for Service's LBaaS based on a configuration option.""" - - def get_subnets(self, service, project_id): - subnet_id = config.CONF.neutron_defaults.service_subnet - - if not subnet_id: - # NOTE(ivc): this option is only required for - # DefaultServiceSubnetDriver and its subclasses, but it may be - # optional for other drivers (e.g. when each namespace has own - # subnet) - raise cfg.RequiredOptError('service_subnet', - cfg.OptGroup('neutron_defaults')) - - return {subnet_id: utils.get_subnet(subnet_id)} diff --git a/kuryr_kubernetes/controller/drivers/lb_public_ip.py b/kuryr_kubernetes/controller/drivers/lb_public_ip.py deleted file mode 100644 index 88912257f..000000000 --- a/kuryr_kubernetes/controller/drivers/lb_public_ip.py +++ /dev/null @@ -1,113 +0,0 @@ -# Copyright (c) 2017 RedHat, 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. -from kuryr_kubernetes import config -from kuryr_kubernetes.controller.drivers import base -from kuryr_kubernetes.controller.drivers import public_ip -from oslo_log import log as logging - -LOG = logging.getLogger(__name__) - - -class FloatingIpServicePubIPDriver(base.ServicePubIpDriver): - """Manages floating ip for neutron lbaas. - - Service loadbalancerIP support the following : - 1. No loadbalancer IP - k8s service.spec.type != 'LoadBalancer' - 2. Floating IP allocated from pool - - k8s service.spec.type = 'LoadBalancer' and - service.spec.loadBalancerIP NOT defined - 3. Floating IP specified by the user - - k8s service.spec.type = 'LoadBalancer' and - service.spec.loadBalancerIP is defined. - """ - - def __init__(self): - super(FloatingIpServicePubIPDriver, self).__init__() - self._drv_pub_ip = public_ip.FipPubIpDriver() - - def acquire_service_pub_ip_info(self, spec_type, spec_lb_ip, project_id, - port_id_to_be_associated=None): - - if spec_type != 'LoadBalancer': - return None - - # get public network/subnet ids from kuryr.conf - public_network_id = config.CONF.neutron_defaults.external_svc_net - public_subnet_id = config.CONF.neutron_defaults.external_svc_subnet - if not public_network_id: - LOG.warning('Skipping Floating IP allocation on port: %s. ' - 'Missing value for external_svc_net config.', - port_id_to_be_associated) - return None - - if spec_lb_ip: - user_specified_ip = spec_lb_ip.format() - res_id = self._drv_pub_ip.is_ip_available(user_specified_ip, - port_id_to_be_associated) - if res_id: - service_pub_ip_info = { - 'ip_id': res_id, - 'ip_addr': str(user_specified_ip), - 'alloc_method': 'user' - } - - return service_pub_ip_info - else: - # user specified IP is not valid - LOG.error("IP=%s is not available", user_specified_ip) - return None - else: - LOG.debug("Trying to allocate public ip from pool") - - try: - res_id, alloc_ip_addr = (self._drv_pub_ip.allocate_ip( - public_network_id, project_id, pub_subnet_id=public_subnet_id, - description='kuryr_lb', - port_id_to_be_associated=port_id_to_be_associated)) - except Exception: - LOG.exception("Failed to allocate public IP - net_id:%s", - public_network_id) - return None - service_pub_ip_info = { - 'ip_id': res_id, - 'ip_addr': alloc_ip_addr, - 'alloc_method': 'pool' - } - - return service_pub_ip_info - - def release_pub_ip(self, service_pub_ip_info): - if not service_pub_ip_info: - return True - if service_pub_ip_info['alloc_method'] == 'pool': - retcode = self._drv_pub_ip.free_ip(service_pub_ip_info['ip_id']) - if not retcode: - LOG.error("Failed to delete public_ip_id =%s !", - service_pub_ip_info['ip_id']) - return False - return True - - def associate_pub_ip(self, service_pub_ip_info, vip_port_id): - if (not service_pub_ip_info or - not vip_port_id or - not service_pub_ip_info['ip_id']): - return - self._drv_pub_ip.associate( - service_pub_ip_info['ip_id'], vip_port_id) - - def disassociate_pub_ip(self, service_pub_ip_info): - if not service_pub_ip_info or not service_pub_ip_info['ip_id']: - return - self._drv_pub_ip.disassociate(service_pub_ip_info['ip_id']) diff --git a/kuryr_kubernetes/controller/drivers/lbaasv2.py b/kuryr_kubernetes/controller/drivers/lbaasv2.py deleted file mode 100644 index 521b7b2c6..000000000 --- a/kuryr_kubernetes/controller/drivers/lbaasv2.py +++ /dev/null @@ -1,940 +0,0 @@ -# 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 random -import time - -from openstack import exceptions as os_exc -from oslo_config import cfg -from oslo_log import log as logging -from oslo_utils import timeutils -from oslo_utils import versionutils - -from kuryr_kubernetes import clients -from kuryr_kubernetes import config -from kuryr_kubernetes import constants as k_const -from kuryr_kubernetes.controller.drivers import base -from kuryr_kubernetes.controller.drivers import utils as c_utils -from kuryr_kubernetes import exceptions as k_exc -from kuryr_kubernetes import utils - - -CONF = cfg.CONF -LOG = logging.getLogger(__name__) - -_ACTIVATION_TIMEOUT = CONF.neutron_defaults.lbaas_activation_timeout -# NOTE(yboaron):Prior to sending create request to Octavia, LBaaS driver -# verifies that LB is in a stable state by polling LB's provisioning_status -# using backoff timer. -# A similar method is used also for the delete flow. -# Unlike LB creation, rest of octavia operations are completed usually after -# few seconds. Next constants define the intervals values for 'fast' and -# 'slow' (will be used for LB creation) polling. -_LB_STS_POLL_FAST_INTERVAL = 1 -_LB_STS_POLL_SLOW_INTERVAL = 3 -_OCTAVIA_TAGGING_VERSION = 2, 5 -# NOTE(ltomasbo): amphora supports it on 2.11, but ovn-octavia only on 2.13 -# In order to make it simpler, we assume this is supported only from 2.13 -_OCTAVIA_DL_VERSION = 2, 13 -_OCTAVIA_ACL_VERSION = 2, 12 -_OCTAVIA_PROVIDER_VERSION = 2, 6 -_OCTAVIA_SCTP_VERSION = 2, 23 - -# HTTP Codes raised by Octavia when a Resource already exists -OKAY_CODES = (409, 500) - - -class LBaaSv2Driver(base.LBaaSDriver): - """LBaaSv2Driver implements LBaaSDriver for Neutron LBaaSv2 API.""" - - def __init__(self): - super(LBaaSv2Driver, self).__init__() - - self._octavia_tags = False - self._octavia_acls = False - self._octavia_double_listeners = False - self._octavia_providers = False - self._octavia_sctp = False - # Check if Octavia API supports tagging. - # TODO(dulek): *Maybe* this can be replaced with - # lbaas.get_api_major_version(version=_OCTAVIA_TAGGING_VERSION) - # if bug https://storyboard.openstack.org/#!/story/2007040 gets - # fixed one day. - v = self.get_octavia_version() - if v >= _OCTAVIA_ACL_VERSION: - self._octavia_acls = True - LOG.info('Octavia supports ACLs for Amphora provider.') - if v >= _OCTAVIA_DL_VERSION: - self._octavia_double_listeners = True - LOG.info('Octavia supports double listeners (different ' - 'protocol, same port) for Amphora provider.') - if v >= _OCTAVIA_TAGGING_VERSION: - LOG.info('Octavia supports resource tags.') - self._octavia_tags = True - else: - v_str = '%d.%d' % v - LOG.warning('[neutron_defaults]resource_tags is set, but Octavia ' - 'API %s does not support resource tagging. Kuryr ' - 'will put requested tags in the description field of ' - 'Octavia resources.', v_str) - if v >= _OCTAVIA_PROVIDER_VERSION: - self._octavia_providers = True - if v >= _OCTAVIA_SCTP_VERSION: - LOG.info('Octavia API supports SCTP protocol.') - self._octavia_sctp = True - - def double_listeners_supported(self): - return self._octavia_double_listeners - - def providers_supported(self): - return self._octavia_providers - - def sctp_supported(self): - return self._octavia_sctp - - def get_octavia_version(self): - lbaas = clients.get_loadbalancer_client() - region_name = getattr(CONF.neutron, 'region_name', None) - - regions = lbaas.get_all_version_data() - # If region was specified take it, otherwise just take first as default - endpoints = regions.get(region_name, list(regions.values())[0]) - # Take the first endpoint - services = list(endpoints.values())[0] - # Try load-balancer service, if not take the first - versions = services.get('load-balancer', list(services.values())[0]) - # Lookup the latest version. For safety, we won't look for - # version['status'] == 'CURRENT' and assume it's the maximum. Also we - # won't assume this dict is sorted. - max_ver = 0, 0 - for version in versions: - if version.get('version') is None: - raise k_exc.UnreachableOctavia('Unable to reach Octavia API') - v_tuple = versionutils.convert_version_to_tuple( - version['version']) - if v_tuple > max_ver: - max_ver = v_tuple - - LOG.debug("Detected Octavia version %d.%d", *max_ver) - return max_ver - - def get_service_loadbalancer_name(self, namespace, svc_name): - return "%s/%s" % (namespace, svc_name) - - def get_loadbalancer_pool_name(self, loadbalancer, namespace, svc_name): - return "%s/%s/%s" % (loadbalancer['name'], namespace, svc_name) - - def add_tags(self, resource, req): - if CONF.neutron_defaults.resource_tags: - if self._octavia_tags: - req['tags'] = CONF.neutron_defaults.resource_tags - else: - if resource in ('loadbalancer', 'listener', 'pool'): - req['description'] = ','.join( - CONF.neutron_defaults.resource_tags) - - def ensure_loadbalancer(self, name, project_id, subnet_id, ip, - security_groups_ids=None, service_type=None, - provider=None): - request = { - 'name': name, - 'project_id': project_id, - 'subnet_id': subnet_id, - 'ip': ip, - 'security_groups': security_groups_ids, - 'provider': provider - } - - response = self._ensure_loadbalancer(request) - - if not response: - # NOTE(ivc): load balancer was present before 'create', but got - # deleted externally between 'create' and 'find' - # NOTE(ltomasbo): or it is in ERROR status, so we deleted and - # trigger the retry - raise k_exc.ResourceNotReady(request) - - return response - - def release_loadbalancer(self, loadbalancer): - lbaas = clients.get_loadbalancer_client() - self._release( - loadbalancer, - loadbalancer, - lbaas.delete_load_balancer, - loadbalancer['id'], - cascade=True) - self._wait_for_deletion(loadbalancer, _ACTIVATION_TIMEOUT) - - def _create_listeners_acls(self, loadbalancer, port, target_port, - protocol, lb_sg, new_sgs, listener_id): - all_pod_rules = [] - add_default_rules = False - os_net = clients.get_network_client() - sgs = [] - - if new_sgs: - sgs = new_sgs - elif loadbalancer['security_groups']: - sgs = loadbalancer['security_groups'] - else: - # NOTE(gryf): in case there is no new SG rules and loadbalancer - # has the SG removed, just add default ones. - add_default_rules = True - - # Check if Network Policy allows listener on the pods - for sg in sgs: - if sg != lb_sg: - if sg in config.CONF.neutron_defaults.pod_security_groups: - # If default sg is set, this means there is no NP - # associated to the service, thus falling back to the - # default listener rules - add_default_rules = True - break - rules = os_net.security_group_rules(security_group_id=sg) - for rule in rules: - # NOTE(ltomasbo): NP sg can only have rules with - # or without remote_ip_prefix. Rules with remote_group_id - # are not possible, therefore only applying the ones - # with or without remote_ip_prefix. - if rule.remote_group_id: - continue - if ((not rule.protocol or - rule.protocol == protocol.lower()) - and rule.direction == 'ingress'): - # If listener port not in allowed range, skip - min_port = rule.port_range_min - max_port = rule.port_range_max - if (min_port and target_port not in range(min_port, - max_port+1)): - continue - if rule.remote_ip_prefix: - all_pod_rules.append(rule.remote_ip_prefix) - else: - add_default_rules = True - - if add_default_rules: - # update the listener without allowed-cidr - self._update_listener_acls(loadbalancer, listener_id, None) - else: - self._update_listener_acls(loadbalancer, listener_id, - all_pod_rules) - - def _apply_members_security_groups(self, loadbalancer, port, target_port, - protocol, sg_rule_name, listener_id, - new_sgs=None): - LOG.debug("Applying members security groups.") - os_net = clients.get_network_client() - lb_sg = None - if CONF.octavia_defaults.enforce_sg_rules: - vip_port = self._get_vip_port(loadbalancer) - if vip_port: - try: - lb_sg = vip_port.security_group_ids[0] - except IndexError: - LOG.warning("We still waiting for SG to be created for " - "VIP %s", vip_port) - raise k_exc.ResourceNotReady(listener_id) - else: - LOG.debug("Skipping sg update for lb %s", loadbalancer['name']) - return - - # NOTE (maysams) It might happen that the update of LBaaS SG - # has been triggered and the LBaaS SG was not created yet. - # This update is skiped, until the LBaaS members are created. - if not lb_sg: - return - - if self._octavia_acls: - self._create_listeners_acls(loadbalancer, port, target_port, - protocol, lb_sg, new_sgs, listener_id) - return - - lbaas_sg_rules = os_net.security_group_rules( - security_group_id=lb_sg, project_id=loadbalancer['project_id']) - all_pod_rules = [] - add_default_rules = False - - if new_sgs: - sgs = new_sgs - else: - sgs = loadbalancer['security_groups'] - - sg_rule_ethertype = k_const.IPv4 - if utils.get_service_subnet_version() == k_const.IP_VERSION_6: - sg_rule_ethertype = k_const.IPv6 - # Check if Network Policy allows listener on the pods - for sg in sgs: - if sg != lb_sg: - if sg in config.CONF.neutron_defaults.pod_security_groups: - # If default sg is set, this means there is no NP - # associated to the service, thus falling back to the - # default listener rules - add_default_rules = True - break - rules = os_net.security_group_rules(security_group_id=sg) - for rule in rules: - # copying ingress rules with same protocol onto the - # loadbalancer sg rules - # NOTE(ltomasbo): NP sg can only have rules with - # or without remote_ip_prefix. Rules with remote_group_id - # are not possible, therefore only applying the ones - # with or without remote_ip_prefix. - if (rule.protocol == protocol.lower() and - rule.direction == 'ingress'): - # If listener port not in allowed range, skip - min_port = rule.port_range_min - max_port = rule.port_range_max - if (min_port and target_port not in range(min_port, - max_port+1)): - continue - all_pod_rules.append(rule) - try: - LOG.debug("Creating LBaaS sg rule for sg: %r", - lb_sg) - os_net.create_security_group_rule( - direction='ingress', - ether_type=sg_rule_ethertype, - port_range_min=port, - port_range_max=port, - protocol=protocol, - remote_ip_prefix=rule.remote_ip_prefix, - security_group_id=lb_sg, - description=sg_rule_name) - except os_exc.ConflictException: - pass - except os_exc.SDKException: - LOG.exception('Failed when creating security ' - 'group rule for listener %s.', - sg_rule_name) - - # Delete LBaaS sg rules that do not match NP - for rule in lbaas_sg_rules: - if (rule.protocol != protocol.lower() or - rule.port_range_min != port or - rule.direction != 'ingress'): - if all_pod_rules and self._is_default_rule(rule): - LOG.debug("Removing default LBaaS sg rule for sg: %r", - lb_sg) - os_net.delete_security_group_rule(rule.id) - continue - self._delete_rule_if_no_match(rule, all_pod_rules) - - if add_default_rules: - try: - LOG.debug("Restoring default LBaaS sg rule for sg: %r", lb_sg) - os_net.create_security_group_rule(direction='ingress', - ether_type=sg_rule_ethertype, - port_range_min=port, - port_range_max=port, - protocol=protocol, - security_group_id=lb_sg, - description=sg_rule_name) - except os_exc.ConflictException: - pass - except os_exc.SDKException: - LOG.exception('Failed when creating security group rule for ' - 'listener %s.', sg_rule_name) - - def _delete_rule_if_no_match(self, rule, all_pod_rules): - for pod_rule in all_pod_rules: - if pod_rule['remote_ip_prefix'] == rule['remote_ip_prefix']: - return - os_net = clients.get_network_client() - LOG.debug("Deleting sg rule: %r", rule.id) - os_net.delete_security_group_rule(rule.id) - - def _is_default_rule(self, rule): - return (rule.get('direction') == 'ingress' and - not rule.get('remote_ip_prefix') and - 'network-policy' not in rule.get('description')) - - def ensure_listener(self, loadbalancer, protocol, port, - service_type='ClusterIP', timeout_client_data=0, - timeout_member_data=0): - name = "%s:%s:%s" % (loadbalancer['name'], protocol, port) - listener = { - 'name': name, - 'project_id': loadbalancer['project_id'], - 'loadbalancer_id': loadbalancer['id'], - 'protocol': protocol, - 'port': port - } - if timeout_client_data: - listener['timeout_client_data'] = timeout_client_data - if timeout_member_data: - listener['timeout_member_data'] = timeout_member_data - try: - result = self._ensure_provisioned( - loadbalancer, listener, self._create_listener, - self._find_listener, interval=_LB_STS_POLL_SLOW_INTERVAL) - except os_exc.SDKException: - LOG.exception("Failed when creating listener for loadbalancer " - "%r", loadbalancer['id']) - return None - - # NOTE(maysams): When ovn-octavia provider is used - # there is no need to set a security group for - # the load balancer as it wouldn't be enforced. - if not CONF.octavia_defaults.enforce_sg_rules and result: - os_net = clients.get_network_client() - vip_port = self._get_vip_port(loadbalancer) - if vip_port: - os_net.update_port(vip_port.id, security_groups=[]) - loadbalancer['security_groups'] = [] - - return result - - def release_listener(self, loadbalancer, listener): - os_net = clients.get_network_client() - lbaas = clients.get_loadbalancer_client() - self._release(loadbalancer, listener, - lbaas.delete_listener, - listener['id']) - - # NOTE(maysams): since lbs created with ovn-octavia provider - # does not have a sg in place, only need to delete sg rules - # when enforcing sg rules on the lb sg, meaning octavia - # Amphora provider is configured. - if CONF.octavia_defaults.enforce_sg_rules: - try: - sg_id = self._get_vip_port(loadbalancer).security_group_ids[0] - except AttributeError: - sg_id = None - if sg_id: - rules = os_net.security_group_rules(security_group_id=sg_id, - description=listener[ - 'name']) - try: - os_net.delete_security_group_rule(next(rules).id) - except StopIteration: - LOG.warning('Cannot find SG rule for %s (%s) listener.', - listener['id'], listener['name']) - - def ensure_pool(self, loadbalancer, listener): - pool = { - 'name': listener['name'], - 'project_id': loadbalancer['project_id'], - 'loadbalancer_id': loadbalancer['id'], - 'listener_id': listener['id'], - 'protocol': listener['protocol'] - } - return self._ensure_provisioned(loadbalancer, pool, - self._create_pool, - self._find_pool) - - def ensure_pool_attached_to_lb(self, loadbalancer, namespace, - svc_name, protocol): - name = self.get_loadbalancer_pool_name(loadbalancer, - namespace, svc_name) - pool = { - 'name': name, - 'project_id': loadbalancer['project_id'], - 'loadbalancer_id': loadbalancer['id'], - 'listener_id': None, - 'protocol': protocol - } - return self._ensure_provisioned(loadbalancer, pool, - self._create_pool, - self._find_pool_by_name) - - def release_pool(self, loadbalancer, pool): - lbaas = clients.get_loadbalancer_client() - self._release(loadbalancer, pool, lbaas.delete_pool, pool['id']) - - def ensure_member(self, loadbalancer, pool, - subnet_id, ip, port, target_ref_namespace, - target_ref_name, listener_port=None): - lbaas = clients.get_loadbalancer_client() - name = ("%s/%s" % (target_ref_namespace, target_ref_name)) - name += ":%s" % port - member = { - 'name': name, - 'project_id': loadbalancer['project_id'], - 'pool_id': pool['id'], - 'subnet_id': subnet_id, - 'ip': ip, - 'port': port - } - result = self._ensure_provisioned(loadbalancer, member, - self._create_member, - self._find_member, - update=lbaas.update_member) - - network_policy = ( - 'policy' in CONF.kubernetes.enabled_handlers and - CONF.kubernetes.service_security_groups_driver == 'policy') - if (network_policy and CONF.octavia_defaults.enforce_sg_rules and - listener_port): - protocol = pool['protocol'] - sg_rule_name = pool['name'] - listener_id = pool['listener_id'] - self._apply_members_security_groups(loadbalancer, listener_port, - port, protocol, sg_rule_name, - listener_id) - return result - - def release_member(self, loadbalancer, member): - lbaas = clients.get_loadbalancer_client() - self._release(loadbalancer, member, lbaas.delete_member, member['id'], - member['pool_id']) - - def _get_vip_port(self, loadbalancer): - os_net = clients.get_network_client() - try: - fixed_ips = ['subnet_id=%s' % str(loadbalancer['subnet_id']), - 'ip_address=%s' % str(loadbalancer['ip'])] - ports = os_net.ports(fixed_ips=fixed_ips) - except os_exc.SDKException: - LOG.error("Port with fixed ips %s not found!", fixed_ips) - raise - - try: - return next(ports) - except StopIteration: - return None - - def _create_loadbalancer(self, loadbalancer): - request = { - 'name': loadbalancer['name'], - 'project_id': loadbalancer['project_id'], - 'vip_address': str(loadbalancer['ip']), - 'vip_subnet_id': loadbalancer['subnet_id'], - } - - if loadbalancer['provider'] is not None: - request['provider'] = loadbalancer['provider'] - - self.add_tags('loadbalancer', request) - - lbaas = clients.get_loadbalancer_client() - response = lbaas.create_load_balancer(**request) - loadbalancer['id'] = response.id - loadbalancer['port_id'] = self._get_vip_port(loadbalancer).id - if (loadbalancer['provider'] is not None and - loadbalancer['provider'] != response.provider): - LOG.error("Request provider(%s) != Response provider(%s)", - loadbalancer['provider'], response.provider) - return None - loadbalancer['provider'] = response.provider - return loadbalancer - - def _find_loadbalancer(self, loadbalancer): - lbaas = clients.get_loadbalancer_client() - response = lbaas.load_balancers( - name=loadbalancer['name'], - project_id=loadbalancer['project_id'], - vip_address=str(loadbalancer['ip']), - vip_subnet_id=loadbalancer['subnet_id'], - provider=loadbalancer['provider']) - - try: - os_lb = next(response) # openstacksdk returns a generator - loadbalancer['id'] = os_lb.id - loadbalancer['port_id'] = self._get_vip_port(loadbalancer).id - loadbalancer['provider'] = os_lb.provider - if os_lb.provisioning_status == 'ERROR': - self.release_loadbalancer(loadbalancer) - utils.clean_lb_crd_status(loadbalancer['name']) - return None - except (KeyError, StopIteration): - return None - - return loadbalancer - - def _create_listener(self, listener): - request = { - 'name': listener['name'], - 'project_id': listener['project_id'], - 'loadbalancer_id': listener['loadbalancer_id'], - 'protocol': listener['protocol'], - 'protocol_port': listener['port'], - } - timeout_cli = listener.get('timeout_client_data') - timeout_mem = listener.get('timeout_member_data') - if timeout_cli: - request['timeout_client_data'] = timeout_cli - if timeout_mem: - request['timeout_member_data'] = timeout_mem - self.add_tags('listener', request) - lbaas = clients.get_loadbalancer_client() - response = lbaas.create_listener(**request) - listener['id'] = response.id - if timeout_cli: - listener['timeout_client_data'] = response.timeout_client_data - if timeout_mem: - listener['timeout_member_data'] = response.timeout_member_data - return listener - - def _update_listener_acls(self, loadbalancer, listener_id, allowed_cidrs): - admin_state_up = True - if allowed_cidrs is None: - # World accessible, no restriction on the listeners - pass - elif len(allowed_cidrs) == 0: - # Prevent any traffic as no CIDR is allowed - admin_state_up = False - - request = { - 'allowed_cidrs': allowed_cidrs, - 'admin_state_up': admin_state_up, - } - - # Wait for the loadbalancer to be ACTIVE - if not self._wait_for_provisioning( - loadbalancer, _ACTIVATION_TIMEOUT, - _LB_STS_POLL_FAST_INTERVAL): - LOG.debug('Skipping ACLs update. ' - 'No Load Balancer Provisioned.') - return - - lbaas = clients.get_loadbalancer_client() - try: - lbaas.update_listener(listener_id, **request) - except os_exc.SDKException: - LOG.error('Error when updating listener %s' % listener_id) - raise k_exc.ResourceNotReady(listener_id) - - def _find_listener(self, listener, loadbalancer): - lbaas = clients.get_loadbalancer_client() - timeout_cli = listener.get('timeout_client_data') - timeout_mb = listener.get('timeout_member_data') - response = lbaas.listeners( - name=listener['name'], - project_id=listener['project_id'], - load_balancer_id=listener['loadbalancer_id'], - protocol=listener['protocol'], - protocol_port=listener['port']) - - request = {} - request['timeout_client_data'] = timeout_cli - request['timeout_member_data'] = timeout_mb - try: - os_listener = next(response) - listener['id'] = os_listener.id - if os_listener.provisioning_status == 'ERROR': - LOG.debug("Releasing listener %s", os_listener.id) - self.release_listener(loadbalancer, listener) - return None - if (timeout_cli and ( - os_listener.timeout_client_data != timeout_cli)) or ( - timeout_mb and ( - os_listener.timeout_member_data != timeout_mb)): - LOG.debug("Updating listener %s", os_listener.id) - n_listen = lbaas.update_listener(os_listener.id, **request) - listener['timeout_client_data'] = n_listen.timeout_client_data - listener['timeout_member_data'] = n_listen.timeout_member_data - elif not timeout_cli or not timeout_mb: - LOG.debug("Updating listener %s", os_listener.id) - lbaas.update_listener(os_listener.id, **request) - - except (KeyError, StopIteration): - return None - except os_exc.SDKException: - LOG.error('Error when updating listener %s' % listener['id']) - raise k_exc.ResourceNotReady(listener['id']) - return listener - - def _create_pool(self, pool): - # TODO(ivc): make lb_algorithm configurable - lb_algorithm = CONF.octavia_defaults.lb_algorithm - request = { - 'name': pool['name'], - 'project_id': pool['project_id'], - 'listener_id': pool['listener_id'], - 'loadbalancer_id': pool['loadbalancer_id'], - 'protocol': pool['protocol'], - 'lb_algorithm': lb_algorithm, - } - self.add_tags('pool', request) - lbaas = clients.get_loadbalancer_client() - response = lbaas.create_pool(**request) - pool['id'] = response.id - return pool - - def _find_pool(self, pool, loadbalancer, by_listener=True): - lbaas = clients.get_loadbalancer_client() - response = lbaas.pools( - name=pool['name'], - project_id=pool['project_id'], - loadbalancer_id=pool['loadbalancer_id'], - protocol=pool['protocol']) - # TODO(scavnic) check response - try: - if by_listener: - pools = [p for p in response if pool['listener_id'] - in {listener['id'] for listener in p.listeners}] - else: - pools = [p for p in response if pool.name == p.name] - pool['id'] = pools[0].id - if pools[0].provisioning_status == 'ERROR': - LOG.debug("Releasing pool %s", pool.id) - self.release_pool(loadbalancer, pool) - return None - except (KeyError, IndexError): - return None - return pool - - def _find_pool_by_name(self, pool, loadbalancer): - return self._find_pool(pool, loadbalancer, by_listener=False) - - def _create_member(self, member): - request = { - 'name': member['name'], - 'project_id': member['project_id'], - 'subnet_id': member['subnet_id'], - 'address': str(member['ip']), - 'protocol_port': member['port'], - } - self.add_tags('member', request) - lbaas = clients.get_loadbalancer_client() - try: - response = lbaas.create_member(member['pool_id'], **request) - except os_exc.BadRequestException as e: - details = e.response.json() - if (details['faultstring'] == f'Subnet {member["subnet_id"]} not ' - f'found.'): - # Most likely the subnet is deleted already as the namespace is - # being deleted. Ignore, we'll delete that LB soon anyway. - LOG.warning('Member %s not created as subnet %s is being ' - 'deleted.', member['name'], member['subnet_id']) - return None - raise - member['id'] = response.id - return member - - def _find_member(self, member, loadbalancer): - lbaas = clients.get_loadbalancer_client() - member = dict(member) - response = lbaas.members( - member['pool_id'], - project_id=member['project_id'], - subnet_id=member['subnet_id'], - address=member['ip'], - protocol_port=member['port']) - - try: - os_members = next(response) - member['id'] = os_members.id - member['name'] = os_members.name - if os_members.provisioning_status == 'ERROR': - LOG.debug("Releasing Member %s", os_members.id) - self.release_member(loadbalancer, member) - return None - except (KeyError, StopIteration): - return None - - return member - - def _ensure(self, create, find, obj, loadbalancer, update=None): - try: - result = create(obj) - LOG.debug("Created %(obj)s", {'obj': result}) - return result - except os_exc.HttpException as e: - if e.status_code not in OKAY_CODES: - raise - result = find(obj, loadbalancer) - # NOTE(maysams): A conflict may happen when a member is - # a lefover and a new pod uses the same address. Let's - # attempt to udpate the member name if already existent. - if result and obj['name'] != result.get('name') and update: - update(result['id'], obj['pool_id'], name=obj['name']) - result['name'] = obj['name'] - if result: - LOG.debug("Found %(obj)s", {'obj': result}) - return result - - def _ensure_loadbalancer(self, loadbalancer): - result = self._find_loadbalancer(loadbalancer) - if result: - LOG.debug("Found %(obj)s", {'obj': result}) - return result - - result = self._create_loadbalancer(loadbalancer) - LOG.debug("Created %(obj)s", {'obj': result}) - return result - - def _ensure_provisioned(self, loadbalancer, obj, create, find, - interval=_LB_STS_POLL_FAST_INTERVAL, **kwargs): - for remaining in self._provisioning_timer(_ACTIVATION_TIMEOUT, - interval): - if not self._wait_for_provisioning( - loadbalancer, remaining, interval): - return None - try: - result = self._ensure( - create, find, obj, loadbalancer, **kwargs) - if result: - return result - except os_exc.BadRequestException: - raise - except os_exc.HttpException as e: - if e.status_code == 501: - LOG.exception("Listener creation failed, most probably " - "because protocol %(prot)s is not supported", - {'prot': obj['protocol']}) - return None - else: - raise - except os_exc.SDKException: - pass - - raise k_exc.ResourceNotReady(obj) - - def _release(self, loadbalancer, obj, delete, *args, **kwargs): - for remaining in self._provisioning_timer(_ACTIVATION_TIMEOUT): - try: - try: - delete(*args, **kwargs) - return - except (os_exc.ConflictException, os_exc.BadRequestException): - if not self._wait_for_provisioning( - loadbalancer, remaining): - return - except os_exc.NotFoundException: - return - - raise k_exc.ResourceNotReady(obj) - - def _wait_for_provisioning(self, loadbalancer, timeout, - interval=_LB_STS_POLL_FAST_INTERVAL): - lbaas = clients.get_loadbalancer_client() - - for remaining in self._provisioning_timer(timeout, interval): - try: - response = lbaas.get_load_balancer(loadbalancer['id']) - except os_exc.ResourceNotFound: - LOG.debug("Cleaning CRD status for deleted " - "loadbalancer %s", loadbalancer['name']) - utils.clean_lb_crd_status(loadbalancer['name']) - return None - - status = response.provisioning_status - if status == 'ACTIVE': - LOG.debug("Provisioning complete for %(lb)s", { - 'lb': loadbalancer}) - return loadbalancer - elif status == 'ERROR': - LOG.debug("Releasing loadbalancer %s with error status", - loadbalancer['id']) - self.release_loadbalancer(loadbalancer) - utils.clean_lb_crd_status(loadbalancer['name']) - return None - elif status == 'DELETED': - LOG.debug("Cleaning CRD status for deleted " - "loadbalancer %s", loadbalancer['name']) - utils.clean_lb_crd_status(loadbalancer['name']) - return None - else: - LOG.debug("Provisioning status %(status)s for %(lb)s, " - "%(rem).3gs remaining until timeout", - {'status': status, 'lb': loadbalancer, - 'rem': remaining}) - - raise k_exc.LoadBalancerNotReady(loadbalancer['id'], status) - - def _wait_for_deletion(self, loadbalancer, timeout, - interval=_LB_STS_POLL_FAST_INTERVAL): - lbaas = clients.get_loadbalancer_client() - - status = 'PENDING_DELETE' - for remaining in self._provisioning_timer(timeout, interval): - try: - lb = lbaas.get_load_balancer(loadbalancer['id']) - status = lb.provisioning_status - except os_exc.NotFoundException: - return - - raise k_exc.LoadBalancerNotReady(loadbalancer['id'], status) - - def _provisioning_timer(self, timeout, - interval=_LB_STS_POLL_FAST_INTERVAL): - # REVISIT(ivc): consider integrating with Retry - max_interval = 15 - with timeutils.StopWatch(duration=timeout) as timer: - while not timer.expired(): - yield timer.leftover() - interval = interval * 2 * random.gauss(0.8, 0.05) - interval = min(interval, max_interval) - interval = min(interval, timer.leftover()) - if interval: - time.sleep(interval) - - def update_lbaas_sg(self, service, sgs): - LOG.debug('Setting SG for LBaaS VIP port') - - svc_namespace = service['metadata']['namespace'] - svc_name = service['metadata']['name'] - svc_ports = service['spec'].get('ports', []) - - lbaas_name = c_utils.get_resource_name(svc_name, - prefix=svc_namespace + "/") - - endpoints_link = utils.get_endpoints_link(service) - k8s = clients.get_kubernetes_client() - try: - k8s.get(endpoints_link) - except k_exc.K8sResourceNotFound: - LOG.debug("Endpoint not Found. Skipping LB SG update for " - "%s as the LB resources are not present", lbaas_name) - return - - try: - klb = k8s.get(f'{k_const.K8S_API_CRD_NAMESPACES}/{svc_namespace}/' - f'kuryrloadbalancers/{svc_name}') - except k_exc.K8sResourceNotFound: - LOG.debug('No KuryrLoadBalancer for service %s created yet.', - lbaas_name) - raise k_exc.ResourceNotReady(svc_name) - - if (not klb.get('status', {}).get('loadbalancer') or - klb.get('status', {}).get('listeners') is None): - LOG.debug('KuryrLoadBalancer for service %s not populated yet.', - lbaas_name) - raise k_exc.ResourceNotReady(svc_name) - - klb['status']['loadbalancer']['security_groups'] = sgs - - lb = klb['status']['loadbalancer'] - try: - k8s.patch_crd('status/loadbalancer', utils.get_res_link(klb), - {'security_groups': sgs}) - except k_exc.K8sResourceNotFound: - LOG.debug('KuryrLoadBalancer CRD not found %s', lbaas_name) - return - except k_exc.K8sClientException: - LOG.exception('Error updating KuryLoadBalancer CRD %s', lbaas_name) - raise - - lsnr_ids = {(listener['protocol'], listener['port']): listener['id'] - for listener in klb['status']['listeners']} - - for port in svc_ports: - port_protocol = port['protocol'] - lbaas_port = port['port'] - target_port = port['targetPort'] - suffix = f"{port_protocol}:{lbaas_port}" - sg_rule_name = c_utils.get_resource_name(lbaas_name, - suffix=':' + suffix) - listener_id = lsnr_ids.get((port_protocol, lbaas_port)) - if listener_id is None: - LOG.warning("There is no listener associated to the protocol " - "%s and port %s. Skipping", port_protocol, - lbaas_port) - continue - self._apply_members_security_groups(lb, lbaas_port, - target_port, port_protocol, - sg_rule_name, listener_id, sgs) diff --git a/kuryr_kubernetes/controller/drivers/multi_vif.py b/kuryr_kubernetes/controller/drivers/multi_vif.py deleted file mode 100644 index 16752dce9..000000000 --- a/kuryr_kubernetes/controller/drivers/multi_vif.py +++ /dev/null @@ -1,117 +0,0 @@ -# Copyright (c) 2018 RedHat, 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. - -from oslo_log import log as logging -from oslo_serialization import jsonutils - -from kuryr_kubernetes import clients -from kuryr_kubernetes import config as kuryr_config -from kuryr_kubernetes import constants -from kuryr_kubernetes.controller.drivers import base -from kuryr_kubernetes import exceptions -from kuryr_kubernetes import utils - -LOG = logging.getLogger(__name__) - - -class NoopMultiVIFDriver(base.MultiVIFDriver): - - def request_additional_vifs( - self, pod, project_id, security_groups): - return [] - - -class NPWGMultiVIFDriver(base.MultiVIFDriver): - def __init__(self): - super(NPWGMultiVIFDriver, self).__init__() - self._drv_vif_pool = base.VIFPoolDriver.get_instance( - specific_driver='multi_pool') - self._drv_vif_pool.set_vif_driver() - - def request_additional_vifs(self, pod, project_id, security_groups): - vifs = [] - networks = self._get_networks(pod) - if not networks: - return vifs - - kubernetes = clients.get_kubernetes_client() - namespace = pod['metadata']['namespace'] - - for network in networks: - if 'name' not in network: - raise exceptions.InvalidKuryrNetworkAnnotation() - - if 'namespace' in network: - namespace = network['namespace'] - - try: - url = '%s/namespaces/%s/network-attachment-definitions/%s' % ( - constants.K8S_API_NPWG_CRD, namespace, network['name']) - nad_obj = kubernetes.get(url) - except exceptions.K8sClientException: - LOG.exception("Kubernetes Client Exception") - raise - - config = jsonutils.loads(nad_obj['metadata']['annotations'] - ['openstack.org/kuryr-config']) - - subnet_id = config.get(constants.K8S_ANNOTATION_NPWG_CRD_SUBNET_ID) - neutron_defaults = kuryr_config.CONF.neutron_defaults - if constants.K8S_ANNOTATION_NPWG_CRD_DRIVER_TYPE not in config: - vif_drv = self._drv_vif_pool - if not subnet_id: - subnet_id = neutron_defaults.pod_subnet - else: - alias = config[constants.K8S_ANNOTATION_NPWG_CRD_DRIVER_TYPE] - vif_drv = base.PodVIFDriver.get_instance( - specific_driver=alias) - if not subnet_id: - try: - subnet_id = neutron_defaults.subnet_mapping[alias] - except KeyError: - subnet_id = neutron_defaults.pod_subnet - LOG.debug("Default subnet mapping in config file " - "doesn't contain any subnet for %s driver " - "alias. Default pod_subnet was used.", alias) - subnet = {subnet_id: utils.get_subnet(subnet_id)} - vif = vif_drv.request_vif(pod, project_id, subnet, security_groups) - if vif: - vifs.append(vif) - return vifs - - def _get_networks(self, pod): - networks = [] - try: - annotations = pod['metadata']['annotations'] - key = constants.K8S_ANNOTATION_NPWG_NETWORK - networks_annotation = annotations[key] - except KeyError: - return [] - - try: - networks = jsonutils.loads(networks_annotation) - except ValueError: - # if annotation is not in json format, convert it to json. - net_list = networks_annotation.split(',') - for net in net_list: - net_details = net.split('/') - if len(net_details) == 1: - networks.append({'name': net_details[0]}) - elif len(net_details) == 2: - networks.append( - {'namespace': net_details[0], 'name': net_details[1]} - ) - else: - raise exceptions.InvalidKuryrNetworkAnnotation() - return networks diff --git a/kuryr_kubernetes/controller/drivers/namespace_subnet.py b/kuryr_kubernetes/controller/drivers/namespace_subnet.py deleted file mode 100644 index c1c2e78be..000000000 --- a/kuryr_kubernetes/controller/drivers/namespace_subnet.py +++ /dev/null @@ -1,214 +0,0 @@ -# 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 kuryr.lib._i18n import _ -from kuryr.lib import constants as kl_const -from oslo_config import cfg as oslo_cfg -from oslo_log import log as logging - -from kuryr_kubernetes import clients -from kuryr_kubernetes import constants -from kuryr_kubernetes.controller.drivers import default_subnet -from kuryr_kubernetes.controller.drivers import utils as c_utils -from kuryr_kubernetes import exceptions -from kuryr_kubernetes import utils - -from openstack import exceptions as os_exc - -LOG = logging.getLogger(__name__) - -namespace_subnet_driver_opts = [ - oslo_cfg.StrOpt('pod_router', - help=_("Default Neutron router ID where pod subnet(s) is " - "connected")), - oslo_cfg.StrOpt('pod_subnet_pool', - help=_("Default Neutron subnet pool ID where pod subnets " - "get their cidr from")), -] - -oslo_cfg.CONF.register_opts(namespace_subnet_driver_opts, "namespace_subnet") -TAGS = oslo_cfg.CONF.neutron_defaults.resource_tags - - -class NamespacePodSubnetDriver(default_subnet.DefaultPodSubnetDriver): - """Provides subnet for Pod port based on a Pod's namespace.""" - - def get_subnets(self, pod, project_id): - pod_namespace = pod['metadata']['namespace'] - return self.get_namespace_subnet(pod_namespace) - - def get_namespace_subnet(self, namespace, subnet_id=None): - if not subnet_id: - subnet_id = self._get_namespace_subnet_id(namespace) - return {subnet_id: utils.get_subnet(subnet_id)} - - def _get_namespace_subnet_id(self, namespace): - kubernetes = clients.get_kubernetes_client() - try: - net_crd_path = (f"{constants.K8S_API_CRD_NAMESPACES}/" - f"{namespace}/kuryrnetworks/{namespace}") - net_crd = kubernetes.get(net_crd_path) - except exceptions.K8sResourceNotFound: - LOG.debug("Kuryrnetwork resource not yet created, retrying...") - raise exceptions.ResourceNotReady(namespace) - except exceptions.K8sClientException: - LOG.exception("Kubernetes Client Exception.") - raise - - try: - subnet_id = net_crd['status']['subnetId'] - except KeyError: - LOG.debug("Subnet for namespace %s not yet created, retrying.", - namespace) - raise exceptions.ResourceNotReady(namespace) - return subnet_id - - def delete_namespace_subnet(self, net_crd): - subnet_id = net_crd['status'].get('subnetId') - net_id = net_crd['status'].get('netId') - - if net_id: - self._delete_namespace_network_resources(subnet_id, net_id) - - def _delete_namespace_network_resources(self, subnet_id, net_id): - os_net = clients.get_network_client() - if subnet_id: - router_id = oslo_cfg.CONF.namespace_subnet.pod_router - try: - clients.handle_neutron_errors( - os_net.remove_interface_from_router, router_id, - subnet_id=subnet_id) - except os_exc.NotFoundException as e: - # Nothing to worry about, either router or subnet is no more, - # or subnet is already detached. - LOG.debug(e.message) - pass - except os_exc.SDKException: - LOG.exception("Error deleting subnet %(subnet)s from router " - "%(router)s.", - {'subnet': subnet_id, 'router': router_id}) - raise - - try: - os_net.delete_network(net_id) - except os_exc.ConflictException: - LOG.warning("One or more ports in use on the network %s. " - "Deleting leftovers ports before retrying", net_id) - # NOTE(dulek): '' is there because Neutron seems to unset - # device_owner on detach. - leftover_ports = [p for p in os_net.ports(network_id=net_id) - if p.device_owner in - ['', 'trunk:subport', kl_const.DEVICE_OWNER]] - c_utils.delete_ports(leftover_ports) - raise exceptions.ResourceNotReady(net_id) - except os_exc.SDKException: - LOG.exception("Error deleting network %s.", net_id) - raise - - def create_network(self, ns, project_id): - os_net = clients.get_network_client() - ns_name = ns['metadata']['name'] - ns_uid = ns['metadata']['uid'] - net_name = c_utils.get_resource_name(ns_name, ns_uid) - old_net_name = c_utils.get_resource_name(ns_name, prefix='ns/', - suffix='-net') - # TODO(gryf): remove old_net_name support in next release, and precise - # the query by adding additional query parameter 'description' which - # should contain namespace uid. - networks = os_net.networks(name=(net_name, old_net_name)) - tags = ",".join(TAGS) - - try: - # NOTE(ltomasbo): only one network must exists - net = next(networks) - # NOTE(gryf): It might happen, that network has been created, but - # for some reason tagging has failed. - if TAGS and not set(TAGS).issubset(set(net.tags)): - c_utils.tag_neutron_resources([net], exceptions=True) - return net.id - except (StopIteration, ValueError): - LOG.debug('Network does not exist. Creating.') - - mtu_cfg = oslo_cfg.CONF.neutron_defaults.network_device_mtu - attrs = {'name': net_name, 'project_id': project_id, - 'description': tags} - if mtu_cfg: - attrs['mtu'] = mtu_cfg - - try: - net = os_net.create_network(**attrs) - except os_exc.SDKException: - LOG.exception("Error creating neutron resources for the namespace " - "%s", ns_name) - raise - c_utils.tag_neutron_resources([net], exceptions=True) - return net.id - - def create_subnet(self, ns, project_id, net_id): - os_net = clients.get_network_client() - ns_name = ns['metadata']['name'] - ns_uid = ns['metadata']['uid'] - tags = ",".join(TAGS) - - # NOTE(gryf): assumption is, that all the subnets (well, currently - # only one) in specific k8s namespaces are under exactly one network, - # which have proper namespace uid in its description, so there is no - # need to put it on the subnet as well. - subnet_name = c_utils.get_resource_name(ns_name, ns_uid) - subnets = os_net.subnets(network_id=net_id) - - try: - # NOTE(ltomasbo): only one subnet must exists - subnet = next(subnets) - # NOTE(gryf): same situation as in networks. - if TAGS and not set(TAGS).issubset(set(subnet.tags)): - c_utils.tag_neutron_resources([subnet], exceptions=True) - return subnet.id, subnet.cidr - except StopIteration: - LOG.debug('Subnet does not exist. Creating.') - - # create subnet with namespace as name - subnet_pool_id = oslo_cfg.CONF.namespace_subnet.pod_subnet_pool - ip_version = utils.get_subnetpool_version(subnet_pool_id) - try: - neutron_subnet = (os_net - .create_subnet(network_id=net_id, - description=tags, - ip_version=ip_version, - name=subnet_name, - enable_dhcp=False, - subnetpool_id=subnet_pool_id, - project_id=project_id)) - except os_exc.ConflictException: - LOG.debug("Max number of retries on neutron side achieved, " - "raising ResourceNotReady to retry subnet creation " - "for %s", subnet_name) - raise exceptions.ResourceNotReady(subnet_name) - c_utils.tag_neutron_resources([neutron_subnet], exceptions=True) - - return neutron_subnet.id, neutron_subnet.cidr - - def add_subnet_to_router(self, subnet_id): - os_net = clients.get_network_client() - router_id = oslo_cfg.CONF.namespace_subnet.pod_router - try: - # connect the subnet to the router - os_net.add_interface_to_router(router_id, subnet_id=subnet_id) - except os_exc.BadRequestException: - LOG.debug("Subnet %s already connected to the router", subnet_id) - except os_exc.SDKException: - LOG.exception("Error attaching the subnet %s to the router", - subnet_id) - raise - return router_id diff --git a/kuryr_kubernetes/controller/drivers/nested_dpdk_vif.py b/kuryr_kubernetes/controller/drivers/nested_dpdk_vif.py deleted file mode 100644 index 307a2ef44..000000000 --- a/kuryr_kubernetes/controller/drivers/nested_dpdk_vif.py +++ /dev/null @@ -1,75 +0,0 @@ -# Copyright (C) 2020 Intel Corporation -# 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. - -from openstack import exceptions as o_exc -from oslo_log import log as logging - -from kuryr_kubernetes import clients -from kuryr_kubernetes.controller.drivers import nested_vif -from kuryr_kubernetes.controller.drivers import utils -from kuryr_kubernetes import os_vif_util as ovu - - -LOG = logging.getLogger(__name__) - - -class NestedDpdkPodVIFDriver(nested_vif.NestedPodVIFDriver): - """Manages ports for DPDK based nested-containers to provide VIFs.""" - - # TODO(garyloug): maybe log a warning if the vswitch is not ovs-dpdk? - - def request_vif(self, pod, project_id, subnets, security_groups): - os_net = clients.get_network_client() - compute = clients.get_compute_client() - - vm_id = self._get_parent_port(pod).device_id - net_id = utils.get_network_id(subnets) - - try: - result = compute.create_server_interface(vm_id, net_id=net_id) - except o_exc.SDKException: - LOG.warning("Unable to create interface for server %s.", - vm_id) - raise - port = os_net.get_port(result.port_id) - return ovu.neutron_to_osvif_vif_dpdk(port, subnets, pod) - - def request_vifs(self, pod, project_id, subnets, security_groups, - num_ports): - # TODO(garyloug): provide an implementation - raise NotImplementedError() - - def release_vif(self, pod, vif, project_id=None): - compute = clients.get_compute_client() - - vm_id = self._get_parent_port(pod).device_id - LOG.debug("release_vif for vm_id %s %s", vm_id, vif.id) - - try: - compute.delete_server_interface(vif.id, server=vm_id) - except o_exc.SDKException: - LOG.warning("Unable to delete interface %s for server %s.", - vif.id, vm_id) - raise - - def activate_vif(self, vif, **kwargs): - # NOTE(danil): new virtual interface was created in nova instance - # during request_vif call, thus if it was not created successfully - # an exception o_exc.SDKException would be throwed. During binding - # process only rebinding of interface on userspace driver was done. - # There is no any chance to check the state of rebinded interface. - # Thus just set 'active' immediately to let the CNI driver make - # progress. - vif.active = True diff --git a/kuryr_kubernetes/controller/drivers/nested_macvlan_vif.py b/kuryr_kubernetes/controller/drivers/nested_macvlan_vif.py deleted file mode 100755 index 7493ae24f..000000000 --- a/kuryr_kubernetes/controller/drivers/nested_macvlan_vif.py +++ /dev/null @@ -1,179 +0,0 @@ -# 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 threading - -from openstack import exceptions as o_exc -from oslo_config import cfg -from oslo_log import log as logging - -from kuryr_kubernetes import clients -from kuryr_kubernetes import config as kuryr_config -from kuryr_kubernetes.controller.drivers import nested_vif -from kuryr_kubernetes.controller.drivers import utils -from kuryr_kubernetes import exceptions as k_exc -from kuryr_kubernetes import os_vif_util as ovu - -LOG = logging.getLogger(__name__) -CONF = cfg.CONF - - -class NestedMacvlanPodVIFDriver(nested_vif.NestedPodVIFDriver): - """Manages ports for nested-containers using MACVLAN to provide VIFs.""" - - def __init__(self): - self.lock = threading.Lock() - - def request_vif(self, pod, project_id, subnets, security_groups): - os_net = clients.get_network_client() - req = self._get_port_request(pod, project_id, subnets, - security_groups) - attempts = kuryr_config.CONF.pod_vif_nested.rev_update_attempts - container_port = None - while attempts > 0: - vm_port = self._get_parent_port(pod) - - if not container_port: - container_port = os_net.create_port(**req) - self._check_port_binding([container_port]) - if not self._tag_on_creation: - utils.tag_neutron_resources([container_port]) - - container_mac = container_port.mac_address - container_ips = frozenset(entry['ip_address'] for entry in - container_port.fixed_ips) - - attempts = self._try_update_port( - attempts, self._add_to_allowed_address_pairs, vm_port, - container_ips, container_mac) - - return ovu.neutron_to_osvif_vif_nested_macvlan(container_port, subnets) - - def request_vifs(self, pod, project_id, subnets, security_groups, - num_ports): - # TODO(mchiappe): provide an implementation - raise NotImplementedError() - - def release_vif(self, pod, vif, project_id=None): - os_net = clients.get_network_client() - - attempts = kuryr_config.CONF.pod_vif_nested.rev_update_attempts - while attempts > 0: - container_port = os_net.get_port(vif.id) - - container_mac = container_port.mac_address - container_ips = frozenset(entry['ip_address'] for entry in - container_port.fixed_ips) - vm_port = self._get_parent_port(pod) - attempts = self._try_update_port( - attempts, self._remove_from_allowed_address_pairs, - vm_port, container_ips, container_mac) - - try: - os_net.delete_port(vif.id, ignore_missing=False) - except o_exc.ResourceNotFound: - LOG.warning("Unable to release port %s as it no longer exists.", - vif.id) - - def activate_vif(self, vif, **kwargs): - # NOTE(mchiappe): there is no way to get feedback on the actual - # interface creation or activation as no plugging can happen for this - # interface type. However the status of the port is not relevant as - # it is used for IPAM purposes only, thus just set 'active' - # immediately to let the CNI driver make progress. - vif.active = True - - def _add_to_allowed_address_pairs(self, port, ip_addresses, - mac_address=None): - if not ip_addresses: - raise k_exc.IntegrityError( - "Cannot add pair from the " - "allowed_address_pairs of port %s: missing IP address" % - port.id) - - mac = mac_address if mac_address else port.mac_address - address_pairs = port.allowed_address_pairs - - # look for duplicates or near-matches - for pair in address_pairs: - if pair['ip_address'] in ip_addresses: - if pair['mac_address'] is mac: - raise k_exc.AllowedAddressAlreadyPresent( - "Pair %s already " - "present in the 'allowed_address_pair' list. This is " - "due to a misconfiguration or a bug" % str(pair)) - else: - LOG.warning( - "A pair with IP %s but different MAC address " - "is already present in the 'allowed_address_pair'. " - "This could indicate a misconfiguration or a " - "bug", pair['ip_address']) - - for ip in ip_addresses: - address_pairs.append({'ip_address': ip, 'mac_address': mac}) - - self._update_port_address_pairs( - port.id, address_pairs, - revision_number=port.revision_number) - - LOG.debug("Added allowed_address_pair %s %s" % - (str(ip_addresses,), mac_address)) - - def _remove_from_allowed_address_pairs(self, port, ip_addresses, - mac_address=None): - if not ip_addresses: - raise k_exc.IntegrityError( - "Cannot remove pair from the " - "allowed_address_pairs of port %s: missing IP address" % - port.id) - - mac = mac_address if mac_address else port.mac_address - address_pairs = port.allowed_address_pairs - updated = False - - for ip in ip_addresses: - try: - address_pairs.remove({'ip_address': ip, 'mac_address': mac}) - updated = True - except ValueError: - LOG.error("No {'ip_address': %s, 'mac_address': %s} pair " - "found in the 'allowed_address_pair' list while " - "trying to remove it.", ip, mac) - - if updated: - self._update_port_address_pairs( - port.id, - address_pairs, - revision_number=port.revision_number) - - def _update_port_address_pairs(self, port_id, address_pairs, - revision_number=None): - os_net = clients.get_network_client() - os_net.update_port(port_id, allowed_address_pairs=address_pairs, - if_revision=revision_number) - - def _try_update_port(self, attempts, f, - vm_port, container_ips, container_mac): - try: - with self.lock: - f(vm_port, container_ips, container_mac) - attempts = 0 - except o_exc.SDKException: - attempts -= 1 - if attempts == 0: - LOG.exception("Error happened during updating port %s", - vm_port['id'] if vm_port else None) - raise - - return attempts diff --git a/kuryr_kubernetes/controller/drivers/nested_vif.py b/kuryr_kubernetes/controller/drivers/nested_vif.py deleted file mode 100644 index b6c3c9c88..000000000 --- a/kuryr_kubernetes/controller/drivers/nested_vif.py +++ /dev/null @@ -1,75 +0,0 @@ -# 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 - -from kuryr.lib import exceptions as kl_exc -from openstack import exceptions as os_exc -from oslo_config import cfg as oslo_cfg -from oslo_log import log as logging - -from kuryr_kubernetes import clients -from kuryr_kubernetes.controller.drivers import base -from kuryr_kubernetes.controller.drivers import neutron_vif - - -CONF = oslo_cfg.CONF -LOG = logging.getLogger(__name__) - - -class NestedPodVIFDriver(neutron_vif.NeutronPodVIFDriver, - metaclass=abc.ABCMeta): - """Skeletal handler driver for VIFs for Nested Pods.""" - - def __init__(self): - super().__init__() - self.nodes_subnets_driver = base.NodesSubnetsDriver.get_instance() - - def _get_parent_port_by_host_ip(self, node_fixed_ip): - os_net = clients.get_network_client() - node_subnet_ids = self.nodes_subnets_driver.get_nodes_subnets( - raise_on_empty=True) - - fixed_ips = ['ip_address=%s' % str(node_fixed_ip)] - filters = {'fixed_ips': fixed_ips} - tags = CONF.neutron_defaults.resource_tags - if tags: - filters['tags'] = tags - try: - ports = os_net.ports(**filters) - except os_exc.SDKException: - LOG.error("Parent VM port with fixed IPs %s not found!", fixed_ips) - raise - - for port in ports: - for fip in port.fixed_ips: - if fip.get('subnet_id') in node_subnet_ids: - return port - - LOG.error("Neutron port for VM port with fixed IPs %s not found!", - fixed_ips) - raise kl_exc.NoResourceException() - - def _get_parent_port(self, pod): - try: - # REVISIT(vikasc): Assumption is being made that hostIP is the IP - # of trunk interface on the node(vm). - node_fixed_ip = pod['status']['hostIP'] - except KeyError: - if pod['status']['conditions'][0]['type'] != "Initialized": - LOG.debug("Pod condition type is not 'Initialized'") - - LOG.error("Failed to get parent vm port ip") - raise - return self._get_parent_port_by_host_ip(node_fixed_ip) diff --git a/kuryr_kubernetes/controller/drivers/nested_vlan_vif.py b/kuryr_kubernetes/controller/drivers/nested_vlan_vif.py deleted file mode 100644 index e3dabea10..000000000 --- a/kuryr_kubernetes/controller/drivers/nested_vlan_vif.py +++ /dev/null @@ -1,311 +0,0 @@ -# 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. -from time import sleep - -from kuryr.lib import constants as kl_const -from kuryr.lib import exceptions as kl_exc -from kuryr.lib import segmentation_type_drivers as seg_driver -from openstack import exceptions as os_exc -from oslo_config import cfg -from oslo_log import log as logging - -from kuryr_kubernetes import clients -from kuryr_kubernetes import config -from kuryr_kubernetes import constants -from kuryr_kubernetes.controller.drivers import nested_vif -from kuryr_kubernetes.controller.drivers import utils -from kuryr_kubernetes import exceptions as k_exc -from kuryr_kubernetes import os_vif_util as ovu - - -LOG = logging.getLogger(__name__) - -DEFAULT_MAX_RETRY_COUNT = 3 -DEFAULT_RETRY_INTERVAL = 1 -ACTIVE_TIMEOUT = 90 - -CONF = cfg.CONF - - -class NestedVlanPodVIFDriver(nested_vif.NestedPodVIFDriver): - """Manages ports for nested-containers using VLANs to provide VIFs.""" - - def request_vif(self, pod, project_id, subnets, security_groups): - os_net = clients.get_network_client() - parent_port = self._get_parent_port(pod) - trunk_id = self._get_trunk_id(parent_port) - - rq = self._get_port_request(pod, project_id, subnets, security_groups) - port = os_net.create_port(**rq) - self._check_port_binding([port]) - if not self._tag_on_creation: - utils.tag_neutron_resources([port]) - vlan_id = self._add_subport(trunk_id, port.id) - - return ovu.neutron_to_osvif_vif_nested_vlan(port, subnets, vlan_id) - - def request_vifs(self, pod, project_id, subnets, security_groups, - num_ports, semaphore, trunk_ip=None): - """This method creates subports and returns a list with their vifs. - - It creates up to num_ports subports and attaches them to the trunk - port. - - If not enough vlan ids are available for all the subports to create, - it creates as much as available vlan ids. - - Note the os_net add_trunk_subports is an atomic operation that will - either attach all or none of the subports. Therefore, if there is a - vlan id collision, all the created ports will be deleted and the - exception is raised. - """ - os_net = clients.get_network_client() - if trunk_ip: - parent_port = self._get_parent_port_by_host_ip(trunk_ip) - else: - parent_port = self._get_parent_port(pod) - trunk_id = self._get_trunk_id(parent_port) - - port_rq, subports_info = self._create_subports_info( - pod, project_id, subnets, security_groups, - trunk_id, num_ports, unbound=True) - - if not subports_info: - LOG.error("There are no vlan ids available to create subports") - return [] - - bulk_port_rq = [port_rq] * len(subports_info) - # restrict amount of create Ports in bulk that might be running - # in parallel. - with semaphore: - try: - ports = list(os_net.create_ports(bulk_port_rq)) - except os_exc.SDKException: - for subport_info in subports_info: - self._release_vlan_id(subport_info['segmentation_id']) - LOG.exception("Error creating bulk ports: %s", bulk_port_rq) - raise - self._check_port_binding(ports) - if not self._tag_on_creation: - utils.tag_neutron_resources(ports) - - for index, port in enumerate(ports): - subports_info[index]['port_id'] = port['id'] - - try: - try: - os_net.add_trunk_subports(trunk_id, subports_info) - except os_exc.ConflictException: - LOG.error("vlan ids already in use on trunk") - utils.delete_ports(ports) - for subport_info in subports_info: - self._release_vlan_id(subport_info['segmentation_id']) - return [] - except os_exc.SDKException: - LOG.exception("Error happened during subport addition to trunk") - utils.delete_ports(ports) - for subport_info in subports_info: - self._release_vlan_id(subport_info['segmentation_id']) - return [] - - vifs = [] - for index, port in enumerate(ports): - vlan_id = subports_info[index]['segmentation_id'] - vif = ovu.neutron_to_osvif_vif_nested_vlan(port, subnets, vlan_id) - vifs.append(vif) - return vifs - - def activate_vif(self, vif, pod=None, retry_info=None): - try: - super().activate_vif(vif) - except k_exc.PortNotReady: - if retry_info and retry_info.get('elapsed', 0) > ACTIVE_TIMEOUT: - parent_port = self._get_parent_port(pod) - trunk_id = self._get_trunk_id(parent_port) - # NOTE(dulek): We don't need a lock to prevent VLAN ID from - # being taken over because the SegmentationDriver - # will keep it reserved in memory unless we - # release it. And we won't. - LOG.warning('Subport %s is in DOWN status for more than %d ' - 'seconds. This is a Neutron issue. Attempting to ' - 'reattach the subport to trunk %s using VLAN ID %s' - ' to fix it.', vif.id, retry_info['elapsed'], - trunk_id, vif.vlan_id) - try: - self._remove_subport(trunk_id, vif.id) - except os_exc.NotFoundException: - # NOTE(dulek): This may happen when _add_subport() failed - # or Kuryr crashed between the calls. Let's - # try to fix it hoping that VLAN ID is still - # free. - LOG.warning('Subport %s was not attached to the trunk. ' - 'Trying to attach it anyway.', vif.id) - self._add_subport(trunk_id, vif.id, - requested_vlan_id=vif.vlan_id) - LOG.warning("Reattached subport %s, its state will be " - "rechecked when event will be retried.", vif.id) - raise - - def release_vif(self, pod, vif, project_id=None): - os_net = clients.get_network_client() - parent_port = self._get_parent_port(pod) - trunk_id = self._get_trunk_id(parent_port) - try: - self._remove_subport(trunk_id, vif.id) - except os_exc.NotFoundException: - pass - self._release_vlan_id(vif.vlan_id) - os_net.delete_port(vif.id) - - def _get_port_request(self, pod, project_id, subnets, security_groups, - unbound=False): - port_req_body = {'project_id': project_id, - 'network_id': utils.get_network_id(subnets), - 'fixed_ips': ovu.osvif_to_neutron_fixed_ips(subnets), - 'device_owner': kl_const.DEVICE_OWNER, - 'admin_state_up': True} - - # only set name if port_debug is enabled - if config.CONF.kubernetes.port_debug: - if unbound: - port_req_body['name'] = constants.KURYR_PORT_NAME - else: - port_req_body['name'] = utils.get_port_name(pod) - - if security_groups: - port_req_body['security_groups'] = security_groups - - if self._tag_on_creation: - tags = CONF.neutron_defaults.resource_tags - if tags: - port_req_body['tags'] = tags - - return port_req_body - - def _create_subports_info(self, pod, project_id, subnets, - security_groups, trunk_id, num_ports, - unbound=False): - subports_info = [] - - in_use_vlan_ids = self._get_in_use_vlan_ids_set(trunk_id) - port_rq = self._get_port_request(pod, project_id, subnets, - security_groups, unbound) - for _ in range(num_ports): - try: - vlan_id = seg_driver.allocate_segmentation_id(in_use_vlan_ids) - except kl_exc.SegmentationIdAllocationFailure: - LOG.warning("There is not enough vlan ids available to " - "create a batch of %d subports.", num_ports) - break - in_use_vlan_ids.add(vlan_id) - - subports_info.append({'segmentation_id': vlan_id, - 'port_id': '', - 'segmentation_type': 'vlan'}) - return port_rq, subports_info - - def _get_trunk_id(self, port): - try: - return port['trunk_details']['trunk_id'] - except (KeyError, TypeError): - LOG.error("Neutron port is missing trunk details. " - "Please ensure that k8s node port is associated " - "with a Neutron vlan trunk") - raise k_exc.K8sNodeTrunkPortFailure - - def _add_subport(self, trunk_id, subport, requested_vlan_id=None): - """Adds subport port to Neutron trunk - - This method gets vlanid allocated from kuryr segmentation driver. - In active/active HA type deployment, possibility of vlanid conflict - is there. In such a case, vlanid will be requested again and subport - addition is re-tried. This is tried DEFAULT_MAX_RETRY_COUNT times in - case of vlanid conflict. - """ - # TODO(vikasc): Better approach for retrying in case of - # vlan-id conflict. - os_net = clients.get_network_client() - retry_count = 1 - while True: - if requested_vlan_id: - vlan_id = requested_vlan_id - else: - try: - vlan_id = self._get_vlan_id(trunk_id) - except os_exc.SDKException: - LOG.error("Getting VLAN ID for subport on " - "trunk %s failed!!", trunk_id) - raise - - subport = [{'segmentation_id': vlan_id, - 'port_id': subport, - 'segmentation_type': 'vlan'}] - try: - os_net.add_trunk_subports(trunk_id, subport) - except os_exc.ConflictException: - if (retry_count < DEFAULT_MAX_RETRY_COUNT and - not requested_vlan_id): - LOG.error("VLAN ID already in use on trunk %s. " - "Retrying.", trunk_id) - retry_count += 1 - sleep(DEFAULT_RETRY_INTERVAL) - continue - else: - LOG.error("Failed to add subport %s to trunk %s due to " - "VLAN ID %d conflict.", subport, trunk_id, - vlan_id) - raise - except os_exc.SDKException: - self._release_vlan_id(vlan_id) - LOG.exception("Error happened during subport " - "addition to trunk %s", trunk_id) - raise - return vlan_id - - def _remove_subports(self, trunk_id, subports_id): - os_net = clients.get_network_client() - subports_body = [] - for subport_id in set(subports_id): - subports_body.append({'port_id': subport_id}) - try: - os_net.delete_trunk_subports(trunk_id, subports_body) - except os_exc.NotFoundException: - if len(subports_id) > 1: - LOG.debug('Not Found on subport deletion, most likely some ' - 'subports in the list got detached already.') - raise # We don't know if all ports are detached, so raise. - # If single requested port is detached already, we're cool, ignore. - except os_exc.SDKException: - LOG.exception("Error happened during subport removal from " - "trunk %s", trunk_id) - raise - - def _remove_subport(self, trunk_id, subport_id): - self._remove_subports(trunk_id, [subport_id]) - - def _get_vlan_id(self, trunk_id): - vlan_ids = self._get_in_use_vlan_ids_set(trunk_id) - return seg_driver.allocate_segmentation_id(vlan_ids) - - def _release_vlan_id(self, id): - return seg_driver.release_segmentation_id(id) - - def _get_in_use_vlan_ids_set(self, trunk_id): - vlan_ids = set() - os_net = clients.get_network_client() - trunk = os_net.get_trunk(trunk_id) - for port in trunk.sub_ports: - vlan_ids.add(port['segmentation_id']) - - return vlan_ids diff --git a/kuryr_kubernetes/controller/drivers/network_policy.py b/kuryr_kubernetes/controller/drivers/network_policy.py deleted file mode 100644 index f97d626c0..000000000 --- a/kuryr_kubernetes/controller/drivers/network_policy.py +++ /dev/null @@ -1,792 +0,0 @@ -# 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. - -import ipaddress -import netaddr - -from openstack import exceptions as os_exc -from oslo_log import log as logging - -from kuryr_kubernetes import clients -from kuryr_kubernetes import config -from kuryr_kubernetes import constants -from kuryr_kubernetes.controller.drivers import base -from kuryr_kubernetes.controller.drivers import utils as driver_utils -from kuryr_kubernetes import exceptions -from kuryr_kubernetes import utils - -CONF = config.CONF - -LOG = logging.getLogger(__name__) - - -class NetworkPolicyDriver(base.NetworkPolicyDriver): - """Provide security groups actions based on K8s Network Policies""" - - def __init__(self): - super().__init__() - self.os_net = clients.get_network_client() - self.kubernetes = clients.get_kubernetes_client() - self.nodes_subnets_driver = base.NodesSubnetsDriver.get_instance() - - def affected_pods(self, policy, selector=None): - if selector is not None: - pod_selector = selector - else: - pod_selector = policy['spec'].get('podSelector') - if pod_selector: - policy_namespace = policy['metadata']['namespace'] - pods = driver_utils.get_pods(pod_selector, policy_namespace) - return pods.get('items') - else: - # NOTE(ltomasbo): It affects all the pods on the namespace - return self.namespaced_pods(policy) - - def create_security_group(self, knp, project_id): - sg_name = driver_utils.get_resource_name(knp['metadata']['namespace'] + - '-' + - knp['metadata']['name'], - prefix='sg/') - desc = ("Kuryr-Kubernetes Network Policy %s SG" % - utils.get_res_unique_name(knp)) - try: - # Create initial security group - sg = self.os_net.create_security_group(name=sg_name, - project_id=project_id, - description=desc) - driver_utils.tag_neutron_resources([sg]) - # NOTE(dulek): Neutron populates every new SG with two rules - # allowing egress on IPv4 and IPv6. This collides with - # how network policies are supposed to work, because - # initially even egress traffic should be blocked. - # To work around this we will delete those two SG - # rules just after creation. - for sgr in sg.security_group_rules: - self.os_net.delete_security_group_rule(sgr['id']) - except (os_exc.SDKException, exceptions.ResourceNotReady) as exc: - np = utils.get_referenced_object(knp, 'NetworkPolicy') - if np: - self.kubernetes.add_event(np, 'FailedToAddSecurityGroup', - f'Adding new security group or ' - f'security group rules for ' - f'corresponding network policy has ' - f'failed: {exc}', 'Warning') - LOG.exception("Error creating security group for network policy " - " %s", knp['metadata']['name']) - raise - - return sg.id - - def delete_np_sg(self, sg_id): - try: - self.os_net.delete_security_group(sg_id) - except os_exc.ConflictException: - LOG.debug("Security Group %s still in use!", sg_id) - # raising ResourceNotReady to retry this action in case ports - # associated to affected pods are not updated on time, i.e., - # they are still using the security group to be removed - raise exceptions.ResourceNotReady(sg_id) - except os_exc.SDKException: - LOG.exception("Error deleting security group %s.", sg_id) - raise - - def ensure_network_policy(self, policy): - """Create security group rules out of network policies - - Triggered by events from network policies, this method ensures that - KuryrNetworkPolicy object is created with the security group rules - definitions required to represent the NetworkPolicy. - """ - LOG.debug("Creating network policy %s", policy['metadata']['name']) - - i_rules, e_rules = self._get_security_group_rules_from_network_policy( - policy) - - knp = self._get_knp_crd(policy) - if not knp: - try: - self._create_knp_crd(policy, i_rules, e_rules) - except exceptions.K8sNamespaceTerminating: - LOG.debug('Namespace %s is being terminated, ignoring ' - 'NetworkPolicy %s in that namespace.', - policy['metadata']['namespace'], - policy['metadata']['name']) - return - else: - self._patch_knp_crd(policy, i_rules, e_rules, knp) - - def namespaced_pods(self, policy): - pod_namespace = policy['metadata']['namespace'] - pods = self.kubernetes.get('{}/namespaces/{}/pods'.format( - constants.K8S_API_BASE, pod_namespace)) - return pods.get('items') - - def _get_security_group_rules_from_network_policy(self, policy): - """Get security group rules required to represent an NP - - This method creates the security group rules bodies coming out of a - network policies' parsing. - """ - i_rules, e_rules = self._parse_network_policy_rules(policy) - # Add default rules to allow traffic from host and svc subnet - i_rules += self._get_default_np_rules() - # Add rules allowing ingress from LBs - # FIXME(dulek): Rules added below cannot work around the Amphora - # source-ip problem as Amphora does not use LB VIP for - # LB->members traffic, but that other IP attached to the - # Amphora VM in the service subnet. It's ridiculous. - i_rules += self._get_service_ingress_rules(policy) - - return i_rules, e_rules - - def _get_service_ingress_rules(self, policy): - """Get SG rules allowing traffic from Services in the namespace - - This methods returns ingress rules allowing traffic from all - services clusterIPs in the cluster. This is required for OVN LBs in - order to work around the fact that it changes source-ip to LB IP in - hairpin traffic. This shouldn't be a security problem as this can only - happen when the pod receiving the traffic is the one that calls the - service. - - FIXME(dulek): Once OVN supports selecting a single, configurable - source-IP for hairpin traffic, consider using it instead. - """ - if CONF.octavia_defaults.enforce_sg_rules: - # When enforce_sg_rules is True, one of the default rules will - # open ingress from all the services subnets, so those rules would - # be redundant. - return [] - - ns = policy['metadata']['namespace'] - rules = [] - services = self.kubernetes.get( - f'{constants.K8S_API_NAMESPACES}/{ns}/services').get('items', []) - for svc in services: - if svc['metadata'].get('deletionTimestamp'): - # Ignore services being deleted - continue - ip = svc['spec'].get('clusterIP') - if not ip or ip == 'None': - # Ignore headless services - continue - rules.append(driver_utils.create_security_group_rule_body( - 'ingress', cidr=ip, - description=f"Allow traffic from local namespace service " - f"{svc['metadata']['name']}")) - return rules - - def _get_default_np_rules(self): - """Add extra SG rule to allow traffic from svcs and host. - - This method adds the base security group rules for the NP security - group: - - Ensure traffic is allowed from the services subnet - - Ensure traffic is allowed from the host - """ - rules = [] - default_cidrs = [] - if CONF.octavia_defaults.enforce_sg_rules: - default_cidrs.append(utils.get_subnet_cidr( - CONF.neutron_defaults.service_subnet)) - worker_subnet_ids = self.nodes_subnets_driver.get_nodes_subnets() - default_cidrs.extend(utils.get_subnets_cidrs(worker_subnet_ids)) - - for cidr in default_cidrs: - ethertype = constants.IPv4 - if ipaddress.ip_network(cidr).version == constants.IP_VERSION_6: - ethertype = constants.IPv6 - rules.append({ - 'sgRule': { - 'ethertype': ethertype, - 'direction': 'ingress', - 'description': 'Kuryr-Kubernetes NetPolicy SG rule', - 'remote_ip_prefix': cidr, - }}) - - return rules - - def _get_pods(self, pod_selector, namespace=None, namespace_selector=None): - matching_pods = {"items": []} - if namespace_selector: - matching_namespaces = driver_utils.get_namespaces( - namespace_selector) - for ns in matching_namespaces.get('items'): - matching_pods = driver_utils.get_pods(pod_selector, - ns['metadata']['name']) - else: - matching_pods = driver_utils.get_pods(pod_selector, namespace) - return matching_pods.get('items') - - def _get_namespaces(self, namespace_selector, namespace=None): - matching_namespaces = [] - if not namespace_selector and namespace: - matching_namespaces.append(self.kubernetes.get( - '{}/namespaces/{}'.format(constants.K8S_API_BASE, namespace))) - else: - matching_namespaces.extend(driver_utils.get_namespaces( - namespace_selector).get('items')) - return matching_namespaces - - def _parse_selectors(self, rule_block, rule_direction, policy_namespace): - allowed_resources = [] - allowed_cidrs = None - selectors = False - for rule in rule_block.get(rule_direction, []): - namespace_selector = rule.get('namespaceSelector') - pod_selector = rule.get('podSelector') - if namespace_selector == {}: - selectors = True - if pod_selector: - # allow matching pods in all namespaces - allowed_resources.extend(self._get_pods( - pod_selector)) - else: - # allow from all the cluster, which means pod subnets and - # service subnet. - allowed_cidrs = utils.get_subnetpool_cidrs( - CONF.namespace_subnet.pod_subnet_pool) - allowed_cidrs.append(utils.get_subnet_cidr( - CONF.neutron_defaults.service_subnet)) - elif namespace_selector: - selectors = True - if pod_selector: - # allow matching pods on matching namespaces - allowed_resources.extend(self._get_pods( - pod_selector, - namespace_selector=namespace_selector)) - else: - # allow from/to all on the matching namespaces - allowed_resources.extend(self._get_namespaces( - namespace_selector)) - else: - if pod_selector == {}: - # allow from/to all pods on the network policy - # namespace - selectors = True - allowed_resources.extend(self._get_namespaces( - None, - namespace=policy_namespace)) - elif pod_selector: - # allow matching pods on the network policy - # namespace - selectors = True - allowed_resources.extend(self._get_pods( - pod_selector, - namespace=policy_namespace)) - - return allowed_cidrs, selectors, allowed_resources - - def _create_sg_rules_with_container_ports( - self, container_ports, allowed_cidrs, resource, matched_pods, - crd_rules, direction, port, pod_selector=None, - policy_namespace=None): - cidr, ns = self._get_resource_details(resource) - for pod, container_port in container_ports: - pod_label = pod['metadata'].get('labels') - pod_ip = pod['status'].get('podIP') - pod_namespace = pod['metadata']['namespace'] - pod_info = {pod_ip: pod_namespace} - # NOTE(maysams) Avoid to take into account pods that are also - # matched by NetworkPolicySpec's podSelector. This way we do - # not allow egress traffic to the actual set of pods the NP - # is enforced on. - if (direction == 'egress' and - (driver_utils.match_selector(pod_selector, pod_label) and - policy_namespace == pod_namespace)): - continue - if container_port in matched_pods: - matched_pods[container_port].update(pod_info) - else: - matched_pods[container_port] = pod_info - if not allowed_cidrs and matched_pods and cidr: - for container_port, pods in matched_pods.items(): - sg_rule = driver_utils.create_security_group_rule_body( - direction, container_port, - # Pod's spec.containers[].port.protocol defaults to TCP - protocol=port.get('protocol', 'TCP'), - cidr=cidr, pods=pods) - if sg_rule not in crd_rules: - crd_rules.append(sg_rule) - if direction == 'egress': - self._create_svc_egress_sg_rule( - policy_namespace, crd_rules, - resource=resource, port=container_port, - # Pod's spec.containers[].port.protocol defaults to TCP - protocol=port.get('protocol', 'TCP')) - - def _create_sg_rule_body_on_text_port(self, direction, port, - resources, crd_rules, pod_selector, - policy_namespace, - allowed_cidrs=None): - """Create SG rules when named port is used in the NP rule - - In case of ingress, the pods selected by NetworkPolicySpec's - podSelector have its containers checked for ports with same name as - the named port. If true, rules are created for the resource matched - in the NP rule selector with that port. In case of egress, all the pods - selected by the NetworkPolicyEgressRule's selector have its containers - checked for containers ports with same name as the ones defined in - NP rule, and if true the rule is created. - - param sg_id: String with the Security Group ID - param direction: String with ingress or egress - param port: dict containing port and protocol - param resources: list of K8S resources(pod/namespace) or - a dict with cird - param crd_rules: list of parsed SG rules - param pod_selector: dict with NetworkPolicySpec's podSelector - param policy_namespace: string with policy namespace - param allowed_cidrs: None, or a list of cidrs, where/from the traffic - should be allowed. - """ - matched_pods = {} - if direction == "ingress": - selected_pods = driver_utils.get_pods( - pod_selector, policy_namespace).get('items') - for selected_pod in selected_pods: - container_ports = driver_utils.get_ports(selected_pod, port) - for resource in resources: - self._create_sg_rules_with_container_ports( - container_ports, allowed_cidrs, resource, matched_pods, - crd_rules, direction, port) - elif direction == "egress": - for resource in resources: - # NOTE(maysams) Skipping objects that refers to ipblocks - # and consequently do not contains a spec field - if not resource.get('spec'): - LOG.warning("IPBlock for egress with named ports is " - "not supported.") - continue - container_ports = driver_utils.get_ports(resource, port) - self._create_sg_rules_with_container_ports( - container_ports, allowed_cidrs, resource, matched_pods, - crd_rules, direction, port, pod_selector, - policy_namespace) - if allowed_cidrs: - for container_port, pods in matched_pods.items(): - for cidr in allowed_cidrs: - sg_rule = driver_utils.create_security_group_rule_body( - direction, container_port, - # Pod's spec.containers[].port.protocol defaults to TCP - protocol=port.get('protocol', 'TCP'), - cidr=cidr, - pods=pods) - crd_rules.append(sg_rule) - - def _create_sg_rule_on_number_port(self, allowed_resources, - direction, port, sg_rule_body_list, - policy_namespace): - for resource in allowed_resources: - cidr, ns = self._get_resource_details(resource) - # NOTE(maysams): Skipping resource that do not have - # an IP assigned. The security group rule creation - # will be triggered again after the resource is running. - if not cidr: - continue - sg_rule = ( - driver_utils.create_security_group_rule_body( - direction, port.get('port'), - # NP's ports[].protocol defaults to TCP - protocol=port.get('protocol', 'TCP'), - cidr=cidr, - namespace=ns)) - sg_rule_body_list.append(sg_rule) - if direction == 'egress': - self._create_svc_egress_sg_rule( - policy_namespace, sg_rule_body_list, - resource=resource, port=port.get('port'), - # NP's ports[].protocol defaults to TCP - protocol=port.get('protocol', 'TCP')) - - def _create_all_pods_sg_rules(self, port, direction, - sg_rule_body_list, pod_selector, - policy_namespace, allowed_cidrs=None): - if not isinstance(port.get('port'), int): - all_pods = driver_utils.get_namespaced_pods().get('items') - self._create_sg_rule_body_on_text_port( - direction, port, all_pods, - sg_rule_body_list, pod_selector, policy_namespace, - allowed_cidrs=allowed_cidrs) - elif allowed_cidrs: - for cidr in allowed_cidrs: - sg_rule = driver_utils.create_security_group_rule_body( - direction, port.get('port'), - protocol=port.get('protocol'), - cidr=cidr) - sg_rule_body_list.append(sg_rule) - else: - for ethertype in (constants.IPv4, constants.IPv6): - sg_rule = ( - driver_utils.create_security_group_rule_body( - direction, port.get('port'), - ethertype=ethertype, - # NP's ports[].protocol defaults to TCP - protocol=port.get('protocol', 'TCP'))) - sg_rule_body_list.append(sg_rule) - - def _create_default_sg_rule(self, direction, sg_rule_body_list): - for ethertype in (constants.IPv4, constants.IPv6): - default_rule = { - 'sgRule': { - 'ethertype': ethertype, - 'direction': direction, - 'description': 'Kuryr-Kubernetes NetPolicy SG rule', - }} - sg_rule_body_list.append(default_rule) - - def _parse_sg_rules(self, sg_rule_body_list, direction, policy): - """Parse policy into security group rules. - - This method inspects the policy object and create the equivalent - security group rules associating them to the referenced sg_id. - It returns the rules by adding them to the sg_rule_body_list list, - for the stated direction. - - It accounts for special cases, such as: - - PolicyTypes stating only Egress: ensuring ingress is not restricted - - PolicyTypes not including Egress: ensuring egress is not restricted - - {} ingress/egress rules: applying default open for all the cluster - """ - _create_sg_rule_body = driver_utils.create_security_group_rule_body - rule_list = policy['spec'].get(direction) - - if not rule_list: - policy_types = policy['spec'].get('policyTypes') - if direction == 'ingress': - if len(policy_types) == 1 and policy_types[0] == 'Egress': - # NOTE(ltomasbo): add default rule to enable all ingress - # traffic as NP policy is not affecting ingress - LOG.debug('Applying default all open for ingress for ' - 'policy %s', utils.get_res_link(policy)) - self._create_default_sg_rule(direction, sg_rule_body_list) - elif direction == 'egress': - if policy_types and 'Egress' not in policy_types: - # NOTE(ltomasbo): add default rule to enable all egress - # traffic as NP policy is not affecting egress - LOG.debug('Applying default all open for egress for ' - 'policy %s', utils.get_res_link(policy)) - self._create_default_sg_rule(direction, sg_rule_body_list) - else: - LOG.warning('Not supported policyType at network policy %s', - utils.get_res_link(policy)) - return - - policy_namespace = policy['metadata']['namespace'] - pod_selector = policy['spec'].get('podSelector') - - rule_direction = 'from' - if direction == 'egress': - rule_direction = 'to' - - if rule_list[0] == {}: - LOG.debug('Applying default all open policy from %s', - utils.get_res_link(policy)) - for ethertype in (constants.IPv4, constants.IPv6): - rule = _create_sg_rule_body(direction, ethertype=ethertype) - sg_rule_body_list.append(rule) - - for rule_block in rule_list: - LOG.debug('Parsing %(dir)s Rule %(rule)s', {'dir': direction, - 'rule': rule_block}) - (allowed_cidrs, selectors, - allowed_resources) = self._parse_selectors(rule_block, - rule_direction, - policy_namespace) - - ipblock_list = [] - - if rule_direction in rule_block: - ipblock_list = [ipblock.get('ipBlock') for ipblock in - rule_block[rule_direction] if 'ipBlock' - in ipblock] - - for ipblock in ipblock_list: - if ipblock.get('except'): - for cidr_except in ipblock.get('except'): - cidr_list = netaddr.cidr_exclude( - ipblock.get('cidr'), cidr_except) - cidr_list = [{'cidr': str(cidr)} - for cidr in cidr_list] - allowed_resources.extend(cidr_list) - else: - allowed_resources.append(ipblock) - - if 'ports' in rule_block: - for port in rule_block['ports']: - if allowed_resources or allowed_cidrs or selectors: - if not isinstance(port.get('port'), int): - self._create_sg_rule_body_on_text_port( - direction, port, allowed_resources, - sg_rule_body_list, pod_selector, - policy_namespace) - else: - self._create_sg_rule_on_number_port( - allowed_resources, direction, port, - sg_rule_body_list, policy_namespace) - if allowed_cidrs: - self._create_all_pods_sg_rules( - port, direction, sg_rule_body_list, - pod_selector, policy_namespace, allowed_cidrs) - else: - self._create_all_pods_sg_rules( - port, direction, sg_rule_body_list, - pod_selector, policy_namespace) - elif allowed_resources or allowed_cidrs or selectors: - for resource in allowed_resources: - cidr, namespace = self._get_resource_details(resource) - # NOTE(maysams): Skipping resource that do not have - # an IP assigned. The security group rule creation - # will be triggered again after the resource is running. - if not cidr: - continue - rule = _create_sg_rule_body(direction, cidr=cidr, - namespace=namespace) - sg_rule_body_list.append(rule) - if direction == 'egress': - self._create_svc_egress_sg_rule( - policy_namespace, sg_rule_body_list, - resource=resource) - if allowed_cidrs: - for cidr in allowed_cidrs: - rule = _create_sg_rule_body(direction, cidr=cidr) - sg_rule_body_list.append(rule) - else: - LOG.debug('This network policy specifies no %(direction)s ' - '%(rule_direction)s and no ports: %(policy)s', - {'direction': direction, - 'rule_direction': rule_direction, - 'policy': utils.get_res_link(policy)}) - - def _create_svc_egress_sg_rule(self, policy_namespace, sg_rule_body_list, - resource=None, port=None, protocol=None): - # FIXME(dulek): We could probably filter by namespace here for pods - # and namespace resources? - services = driver_utils.get_services() - if not resource: - svc_subnet = utils.get_subnet_cidr( - CONF.neutron_defaults.service_subnet) - rule = driver_utils.create_security_group_rule_body( - 'egress', port, protocol=protocol, cidr=svc_subnet) - if rule not in sg_rule_body_list: - sg_rule_body_list.append(rule) - return - - for service in services.get('items'): - if service['metadata'].get('deletionTimestamp'): - # Ignore services being deleted - continue - - cluster_ip = service['spec'].get('clusterIP') - if not cluster_ip or cluster_ip == 'None': - # Headless services has 'None' as clusterIP, ignore. - continue - - svc_name = service['metadata']['name'] - svc_namespace = service['metadata']['namespace'] - if self._is_pod(resource): - pod_labels = resource['metadata'].get('labels') - svc_selector = service['spec'].get('selector') - if not svc_selector: - targets = driver_utils.get_endpoints_targets( - svc_name, svc_namespace) - pod_ip = resource['status'].get('podIP') - if pod_ip and pod_ip not in targets: - continue - elif pod_labels: - if not driver_utils.match_labels(svc_selector, pod_labels): - continue - elif resource.get('cidr'): - # NOTE(maysams) Accounts for traffic to pods under - # a service matching an IPBlock rule. - svc_selector = service['spec'].get('selector') - if not svc_selector: - # Retrieving targets of services on any Namespace - targets = driver_utils.get_endpoints_targets( - svc_name, svc_namespace) - if (not targets or - not self._targets_in_ip_block(targets, resource)): - continue - else: - if svc_namespace != policy_namespace: - continue - pods = driver_utils.get_pods({'selector': svc_selector}, - svc_namespace).get('items') - if not self._pods_in_ip_block(pods, resource): - continue - else: - ns_name = service['metadata']['namespace'] - if ns_name != resource['metadata']['name']: - continue - rule = driver_utils.create_security_group_rule_body( - 'egress', port, protocol=protocol, cidr=cluster_ip) - if rule not in sg_rule_body_list: - sg_rule_body_list.append(rule) - - def _pods_in_ip_block(self, pods, resource): - for pod in pods: - pod_ip = driver_utils.get_pod_ip(pod) - if pod_ip is None: - continue - if (ipaddress.ip_address(pod_ip) - in ipaddress.ip_network(resource.get('cidr'))): - return True - return False - - def _targets_in_ip_block(self, targets, resource): - for target in targets: - if (ipaddress.ip_address(target) - not in ipaddress.ip_network(resource.get('cidr'))): - return False - return True - - def _parse_network_policy_rules(self, policy): - """Create security group rule bodies out of network policies. - - Whenever a notification from the handler 'on-present' method is - received, security group rules are created out of network policies' - ingress and egress ports blocks. - """ - LOG.debug('Parsing Network Policy %s' % policy['metadata']['name']) - ingress_sg_rule_body_list = [] - egress_sg_rule_body_list = [] - - self._parse_sg_rules(ingress_sg_rule_body_list, 'ingress', policy) - self._parse_sg_rules(egress_sg_rule_body_list, 'egress', policy) - - return ingress_sg_rule_body_list, egress_sg_rule_body_list - - def release_network_policy(self, policy): - return self._del_knp_crd(policy) - - def _get_knp_crd(self, policy): - netpolicy_crd_name = policy['metadata']['name'] - netpolicy_crd_namespace = policy['metadata']['namespace'] - try: - netpolicy_crd = self.kubernetes.get( - '{}/{}/kuryrnetworkpolicies/{}'.format( - constants.K8S_API_CRD_NAMESPACES, netpolicy_crd_namespace, - netpolicy_crd_name)) - except exceptions.K8sResourceNotFound: - return None - except exceptions.K8sClientException: - LOG.exception("Kubernetes Client Exception.") - raise - return netpolicy_crd - - def _create_knp_crd(self, policy, i_rules, e_rules): - networkpolicy_name = policy['metadata']['name'] - namespace = policy['metadata']['namespace'] - pod_selector = policy['spec'].get('podSelector') - policy_types = policy['spec'].get('policyTypes', []) - - owner_reference = {'apiVersion': policy['apiVersion'], - 'kind': policy['kind'], - 'name': policy['metadata']['name'], - 'uid': policy['metadata']['uid']} - - netpolicy_crd = { - 'apiVersion': 'openstack.org/v1', - 'kind': constants.K8S_OBJ_KURYRNETWORKPOLICY, - 'metadata': { - 'name': networkpolicy_name, - 'namespace': namespace, - 'annotations': { - 'networkPolicyLink': utils.get_res_link(policy) - }, - 'finalizers': [constants.NETWORKPOLICY_FINALIZER], - 'ownerReferences': [owner_reference] - }, - 'spec': { - 'ingressSgRules': i_rules, - 'egressSgRules': e_rules, - 'podSelector': pod_selector, - 'policyTypes': policy_types, - }, - 'status': { - 'securityGroupRules': [], - }, - } - - try: - LOG.debug("Creating KuryrNetworkPolicy CRD %s" % netpolicy_crd) - url = '{}/{}/kuryrnetworkpolicies'.format( - constants.K8S_API_CRD_NAMESPACES, - namespace) - netpolicy_crd = self.kubernetes.post(url, netpolicy_crd) - except exceptions.K8sNamespaceTerminating: - raise - except exceptions.K8sClientException as exc: - self.kubernetes.add_event(policy, 'FailedToCreateNetworkPolicyCRD', - f'Adding corresponding Kuryr Network ' - f'Policy CRD has failed: {exc}', - 'Warning') - LOG.exception("Kubernetes Client Exception creating " - "KuryrNetworkPolicy CRD.") - raise - return netpolicy_crd - - def _patch_knp_crd(self, policy, i_rules, e_rules, knp): - networkpolicy_name = policy['metadata']['name'] - namespace = policy['metadata']['namespace'] - pod_selector = policy['spec'].get('podSelector') - url = (f'{constants.K8S_API_CRD_NAMESPACES}/{namespace}' - f'/kuryrnetworkpolicies/{networkpolicy_name}') - - # FIXME(dulek): Rules should be hashable objects, not dict so that - # we could compare them easily here. - data = { - 'ingressSgRules': i_rules, - 'egressSgRules': e_rules, - } - if knp['spec'].get('podSelector') != pod_selector: - data['podSelector'] = pod_selector - - self.kubernetes.patch_crd('spec', url, data) - - def _del_knp_crd(self, policy): - try: - ns = policy['metadata']['namespace'] - name = policy['metadata']['name'] - LOG.debug("Deleting KuryrNetworkPolicy CRD %s" % name) - self.kubernetes.delete('{}/{}/kuryrnetworkpolicies/{}'.format( - constants.K8S_API_CRD_NAMESPACES, ns, name)) - return True - except exceptions.K8sResourceNotFound: - LOG.debug("KuryrNetworkPolicy CRD Object not found: %s", name) - return False - except exceptions.K8sClientException as exc: - self.kubernetes.add_event(policy, 'FailedToDeleteNetworkPolicyCRD', - f'Deleting corresponding Kuryr Network ' - f'Policy CRD has failed: {exc}', - 'Warning') - LOG.exception("Kubernetes Client Exception deleting " - "KuryrNetworkPolicy CRD %s." % name) - raise - - def _get_resource_details(self, resource): - namespace = None - if self._is_pod(resource): - cidr = resource['status'].get('podIP') - namespace = resource['metadata']['namespace'] - elif resource.get('cidr'): - cidr = resource.get('cidr') - else: - cidr = driver_utils.get_namespace_subnet_cidr(resource) - namespace = resource['metadata']['name'] - return cidr, namespace - - def _is_pod(self, resource): - if resource.get('spec'): - return resource['spec'].get('containers') diff --git a/kuryr_kubernetes/controller/drivers/network_policy_security_groups.py b/kuryr_kubernetes/controller/drivers/network_policy_security_groups.py deleted file mode 100644 index f40d34c19..000000000 --- a/kuryr_kubernetes/controller/drivers/network_policy_security_groups.py +++ /dev/null @@ -1,437 +0,0 @@ -# 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_config import cfg -from oslo_log import log as logging - -from kuryr_kubernetes import clients -from kuryr_kubernetes import config -from kuryr_kubernetes import constants -from kuryr_kubernetes.controller.drivers import base -from kuryr_kubernetes.controller.drivers import utils as driver_utils -from kuryr_kubernetes import exceptions -from kuryr_kubernetes import utils - -LOG = logging.getLogger(__name__) - - -def _get_namespace_labels(namespace): - kubernetes = clients.get_kubernetes_client() - - try: - path = '{}/{}'.format(constants.K8S_API_NAMESPACES, namespace) - namespaces = kubernetes.get(path) - LOG.debug("Return Namespace: %s", namespaces) - except exceptions.K8sResourceNotFound: - LOG.exception("Namespace not found") - raise - except exceptions.K8sClientException: - LOG.exception("Kubernetes Client Exception") - raise - return namespaces['metadata'].get('labels') - - -def _create_sg_rules_with_container_ports(container_ports, matched): - """Checks if security group rules based on container ports will be updated - - param container_ports: List of tuples with pods and port values - param matched: If a sg rule was created for the NP rule - - return: True if a sg rule needs to be created, False otherwise. - """ - for pod, container_port in container_ports: - pod_ip = driver_utils.get_pod_ip(pod) - if not pod_ip: - LOG.debug("Skipping SG rule creation for pod %s due to " - "no IP assigned", pod['metadata']['name']) - continue - return matched - return False - - -def _create_sg_rule_on_text_port(direction, port, rule_selected_pods, matched, - crd): - spec_pod_selector = crd['spec'].get('podSelector') - policy_namespace = crd['metadata']['namespace'] - spec_pods = driver_utils.get_pods( - spec_pod_selector, policy_namespace).get('items') - if direction == 'ingress': - for spec_pod in spec_pods: - container_ports = driver_utils.get_ports(spec_pod, port) - matched = _create_sg_rules_with_container_ports( - container_ports, matched) - elif direction == 'egress': - for rule_selected_pod in rule_selected_pods: - pod_label = rule_selected_pod['metadata'].get('labels') - pod_ns = rule_selected_pod['metadata'].get('namespace') - # NOTE(maysams) Do not allow egress traffic to the actual - # set of pods the NP is enforced on. - if (driver_utils.match_selector(spec_pod_selector, pod_label) and - policy_namespace == pod_ns): - continue - container_ports = driver_utils.get_ports( - rule_selected_pod, port) - matched = _create_sg_rules_with_container_ports( - container_ports, matched) - return matched - - -def _create_sg_rules(crd, pod, pod_selector, rule_block, direction, matched): - pod_labels = pod['metadata'].get('labels') - pod_ip = driver_utils.get_pod_ip(pod) - if not pod_ip: - LOG.debug("Skipping SG rule creation for pod %s due to " - "no IP assigned", pod['metadata']['name']) - return None - - # NOTE (maysams) No need to differentiate between podSelector - # with empty value or with '{}', as they have same result in here. - if pod_selector: - if driver_utils.match_selector(pod_selector, pod_labels): - if 'ports' in rule_block: - for port in rule_block['ports']: - if type(port.get('port')) is not int: - matched = _create_sg_rule_on_text_port( - direction, port, [pod], matched, crd) - else: - matched = True - else: - matched = True - else: - # NOTE (maysams) When a policy with namespaceSelector and text port - # is applied the port on the pods needs to be retrieved. - if 'ports' in rule_block: - for port in rule_block['ports']: - if type(port.get('port')) is not int: - matched = _create_sg_rule_on_text_port( - direction, port, [pod], matched, crd) - return matched - - -def _parse_selectors_on_pod(crd, pod, pod_selector, namespace_selector, - rule_block, direction, matched): - pod_namespace = pod['metadata']['namespace'] - pod_namespace_labels = _get_namespace_labels(pod_namespace) - policy_namespace = crd['metadata']['namespace'] - - if namespace_selector == {}: - matched = _create_sg_rules(crd, pod, pod_selector, rule_block, - direction, matched) - elif namespace_selector: - if (pod_namespace_labels and - driver_utils.match_selector(namespace_selector, - pod_namespace_labels)): - matched = _create_sg_rules(crd, pod, pod_selector, - rule_block, direction, matched) - else: - if pod_namespace == policy_namespace: - matched = _create_sg_rules(crd, pod, pod_selector, rule_block, - direction, matched) - return matched - - -def _parse_selectors_on_namespace(crd, direction, pod_selector, - ns_selector, rule_block, namespace, matched): - ns_name = namespace['metadata'].get('name') - ns_labels = namespace['metadata'].get('labels') - - if (ns_selector and ns_labels and - driver_utils.match_selector(ns_selector, ns_labels)): - if pod_selector: - pods = driver_utils.get_pods(pod_selector, ns_name).get('items') - if 'ports' in rule_block: - for port in rule_block['ports']: - if type(port.get('port')) is not int: - matched = ( - _create_sg_rule_on_text_port( - direction, port, pods, matched, crd)) - else: - for pod in pods: - pod_ip = driver_utils.get_pod_ip(pod) - if not pod_ip: - pod_name = pod['metadata']['name'] - LOG.debug("Skipping SG rule creation for pod " - "%s due to no IP assigned", pod_name) - continue - matched = True - else: - for pod in pods: - pod_ip = driver_utils.get_pod_ip(pod) - if not pod_ip: - pod_name = pod['metadata']['name'] - LOG.debug("Skipping SG rule creation for pod %s due" - " to no IP assigned", pod_name) - continue - matched = True - else: - ns_pods = driver_utils.get_pods(ns_selector)['items'] - if 'ports' in rule_block: - for port in rule_block['ports']: - if type(port.get('port')) is not int: - matched = ( - _create_sg_rule_on_text_port( - direction, port, ns_pods, matched, crd)) - else: - matched = True - else: - matched = True - return matched - - -def _parse_rules(direction, crd, policy, pod=None, namespace=None): - rule_direction = 'from' - if direction == 'egress': - rule_direction = 'to' - - matched = False - rule_list = policy.get(direction, []) - for rule_block in rule_list: - for rule in rule_block.get(rule_direction, []): - namespace_selector = rule.get('namespaceSelector') - pod_selector = rule.get('podSelector') - if pod: - matched = _parse_selectors_on_pod( - crd, pod, pod_selector, namespace_selector, - rule_block, direction, matched) - elif namespace: - matched = _parse_selectors_on_namespace( - crd, direction, pod_selector, namespace_selector, - rule_block, namespace, matched) - - # NOTE(maysams): Cover the case of a network policy that allows - # from everywhere on a named port, e.g., when there is no 'from' - # specified. - if pod and not matched: - for port in rule_block.get('ports', []): - if type(port.get('port')) is not int: - if (not rule_block.get(rule_direction, []) - or direction == "ingress"): - matched = _create_sg_rule_on_text_port( - direction, port, [pod], matched, crd) - return matched - - -def _parse_rules_on_delete_namespace(rule_list, direction, ns_name): - for rule in rule_list: - LOG.debug('Parsing %(dir)s Rule %(r)s', {'dir': direction, 'r': rule}) - rule_namespace = rule.get('namespace', None) - affectedPods = rule.get('affectedPods', []) - if rule_namespace and rule_namespace == ns_name: - return True - elif affectedPods: - for pod_info in affectedPods: - if pod_info['podNamespace'] == ns_name: - return True - return False - - -def _parse_rules_on_delete_pod(rule_list, direction, pod_ip): - for rule in rule_list: - LOG.debug('Parsing %(dir)s Rule %(r)s', {'dir': direction, 'r': rule}) - remote_ip_prefix = rule['sgRule'].get('remote_ip_prefix') - affectedPods = rule.get('affectedPods', []) - if remote_ip_prefix and remote_ip_prefix == pod_ip: - return True - elif affectedPods: - for pod_info in affectedPods: - if pod_info['podIP'] == pod_ip: - return True - return False - - -def _get_pod_sgs(pod): - sg_list = [] - - pod_labels = pod['metadata'].get('labels') - pod_namespace = pod['metadata']['namespace'] - - knp_crds = driver_utils.get_kuryrnetworkpolicy_crds( - namespace=pod_namespace) - for crd in knp_crds: - pod_selector = crd['spec'].get('podSelector') - if driver_utils.match_selector(pod_selector, pod_labels): - sg_id = crd['status'].get('securityGroupId') - if not sg_id: - # NOTE(dulek): We could just assume KNP handler will apply it, - # but it's possible that when it gets this pod it - # will have no IP yet and will be skipped. - LOG.warning('SG for NP %s not created yet, will retry.', - utils.get_res_unique_name(crd)) - raise exceptions.ResourceNotReady(pod) - LOG.debug("Appending %s", crd['status']['securityGroupId']) - sg_list.append(crd['status']['securityGroupId']) - - # NOTE(maysams) Pods that are not selected by any Networkpolicy - # are fully accessible. Thus, the default security group is associated. - if not sg_list: - sg_list = config.CONF.neutron_defaults.pod_security_groups - if not sg_list: - raise cfg.RequiredOptError('pod_security_groups', - cfg.OptGroup('neutron_defaults')) - - return sg_list[:] - - -class NetworkPolicySecurityGroupsDriver(base.PodSecurityGroupsDriver): - """Provides security groups for pods based on network policies""" - - def get_security_groups(self, pod, project_id): - return _get_pod_sgs(pod) - - def create_sg_rules(self, pod): - LOG.debug("Creating SG rules for pod: %s", pod['metadata']['name']) - crd_pod_selectors = [] - knp_crds = driver_utils.get_kuryrnetworkpolicy_crds() - nps = driver_utils.get_networkpolicies() - pairs = driver_utils.zip_knp_np(knp_crds, nps) - - for crd, policy in pairs: - crd_selector = crd['spec'].get('podSelector') - spec = policy.get('spec') - - i_matched = _parse_rules('ingress', crd, spec, pod=pod) - e_matched = _parse_rules('egress', crd, spec, pod=pod) - - if i_matched or e_matched: - try: - driver_utils.bump_networkpolicy(crd) - except exceptions.K8sResourceNotFound: - # The NP got deleted, ignore it. - continue - if i_matched: - crd_pod_selectors.append(crd_selector) - return crd_pod_selectors - - def delete_sg_rules(self, pod): - LOG.debug("Deleting SG rules for pod: %s", pod['metadata']['name']) - pod_ip = driver_utils.get_pod_ip(pod) - crd_pod_selectors = [] - if not pod_ip: - LOG.debug("Skipping SG rule deletion as pod %s has no IP assigned", - pod['metadata']['name']) - return crd_pod_selectors - knp_crds = driver_utils.get_kuryrnetworkpolicy_crds() - for crd in knp_crds: - crd_selector = crd['spec'].get('podSelector') - ingress_rule_list = crd['spec'].get('ingressSgRules') - egress_rule_list = crd['spec'].get('egressSgRules') - - i_matched = _parse_rules_on_delete_pod( - ingress_rule_list, "ingress", pod_ip) - e_matched = _parse_rules_on_delete_pod( - egress_rule_list, "egress", pod_ip) - - if i_matched or e_matched: - try: - driver_utils.bump_networkpolicy(crd) - except exceptions.K8sResourceNotFound: - # The NP got deleted, ignore it. - continue - if i_matched: - crd_pod_selectors.append(crd_selector) - return crd_pod_selectors - - def update_sg_rules(self, pod): - LOG.debug("Updating SG rules for pod: %s", pod['metadata']['name']) - # FIXME(dulek): No need to bump twice. - crd_pod_selectors = [] - crd_pod_selectors.extend(self.delete_sg_rules(pod)) - crd_pod_selectors.extend(self.create_sg_rules(pod)) - return crd_pod_selectors - - def delete_namespace_sg_rules(self, namespace): - ns_name = namespace['metadata']['name'] - LOG.debug("Deleting SG rules for namespace: %s", ns_name) - - crd_selectors = [] - knp_crds = driver_utils.get_kuryrnetworkpolicy_crds() - for crd in knp_crds: - crd_selector = crd['spec'].get('podSelector') - ingress_rule_list = crd['spec'].get('ingressSgRules') - egress_rule_list = crd['spec'].get('egressSgRules') - - i_matched = _parse_rules_on_delete_namespace( - ingress_rule_list, "ingress", ns_name) - e_matched = _parse_rules_on_delete_namespace( - egress_rule_list, "egress", ns_name) - - if i_matched or e_matched: - try: - driver_utils.bump_networkpolicy(crd) - except exceptions.K8sResourceNotFound: - # The NP got deleted, ignore it. - continue - if i_matched: - crd_selectors.append(crd_selector) - return crd_selectors - - def create_namespace_sg_rules(self, namespace): - ns_name = namespace['metadata']['name'] - LOG.debug("Creating SG rules for namespace: %s", ns_name) - crd_selectors = [] - knp_crds = driver_utils.get_kuryrnetworkpolicy_crds() - nps = driver_utils.get_networkpolicies() - pairs = driver_utils.zip_knp_np(knp_crds, nps) - for crd, policy in pairs: - crd_selector = crd['spec'].get('podSelector') - spec = policy.get('spec') - i_matched = _parse_rules('ingress', crd, spec, namespace=namespace) - e_matched = _parse_rules('egress', crd, spec, namespace=namespace) - - if i_matched or e_matched: - try: - driver_utils.bump_networkpolicy(crd) - except exceptions.K8sResourceNotFound: - # The NP got deleted, ignore it. - continue - if i_matched: - crd_selectors.append(crd_selector) - return crd_selectors - - def update_namespace_sg_rules(self, namespace): - LOG.debug("Updating SG rules for namespace: %s", - namespace['metadata']['name']) - crd_selectors = [] - crd_selectors.extend(self.delete_namespace_sg_rules(namespace)) - crd_selectors.extend(self.create_namespace_sg_rules(namespace)) - return crd_selectors - - -class NetworkPolicyServiceSecurityGroupsDriver( - base.ServiceSecurityGroupsDriver): - """Provides security groups for services based on network policies""" - - def get_security_groups(self, service, project_id): - sg_list = [] - svc_namespace = service['metadata']['namespace'] - svc_selector = service['spec'].get('selector') - - if svc_selector: - # get affected pods by svc selector - pods = driver_utils.get_pods({'selector': svc_selector}, - svc_namespace).get('items') - # NOTE(ltomasbo): We assume all the pods pointed by a service - # have the same labels, and the same policy will be applied to - # all of them. Hence only considering the security groups applied - # to the first one. - if pods: - return _get_pod_sgs(pods[0]) - else: - # NOTE(maysams): Network Policy is not enforced on Services - # without selectors. - sg_list = config.CONF.neutron_defaults.pod_security_groups - if not sg_list: - raise cfg.RequiredOptError('pod_security_groups', - cfg.OptGroup('neutron_defaults')) - return sg_list[:] diff --git a/kuryr_kubernetes/controller/drivers/neutron_vif.py b/kuryr_kubernetes/controller/drivers/neutron_vif.py deleted file mode 100644 index e6242b6a9..000000000 --- a/kuryr_kubernetes/controller/drivers/neutron_vif.py +++ /dev/null @@ -1,161 +0,0 @@ -# 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. - -from kuryr.lib import constants as kl_const -from openstack import exceptions as os_exc -from oslo_config import cfg -from oslo_log import log as logging - -from kuryr_kubernetes import clients -from kuryr_kubernetes import config -from kuryr_kubernetes import constants -from kuryr_kubernetes.controller.drivers import base -from kuryr_kubernetes.controller.drivers import utils -from kuryr_kubernetes import exceptions as k_exc -from kuryr_kubernetes import os_vif_util as ovu - - -LOG = logging.getLogger(__name__) - -CONF = cfg.CONF - - -class NeutronPodVIFDriver(base.PodVIFDriver): - """Manages normal Neutron ports to provide VIFs for Kubernetes Pods.""" - - def __init__(self): - super(NeutronPodVIFDriver, self).__init__() - - self._tag_on_creation = utils.check_tag_on_creation() - if self._tag_on_creation: - LOG.info('Neutron supports tagging during bulk port creation.') - else: - LOG.warning('Neutron does not support tagging during bulk ' - 'port creation. Kuryr will tag resources after ' - 'port creation.') - - def request_vif(self, pod, project_id, subnets, security_groups): - os_net = clients.get_network_client() - - rq = self._get_port_request(pod, project_id, subnets, security_groups) - port = os_net.create_port(**rq) - - self._check_port_binding([port]) - if not self._tag_on_creation: - utils.tag_neutron_resources([port]) - return ovu.neutron_to_osvif_vif(port.binding_vif_type, port, subnets) - - def request_vifs(self, pod, project_id, subnets, security_groups, - num_ports, semaphore): - os_net = clients.get_network_client() - - rq = self._get_port_request(pod, project_id, subnets, security_groups, - unbound=True) - - bulk_port_rq = [rq] * num_ports - # restrict amount of create Ports in bulk that might be running - # in parallel. - with semaphore: - try: - ports = list(os_net.create_ports(bulk_port_rq)) - except os_exc.SDKException: - LOG.exception("Error creating bulk ports: %s", bulk_port_rq) - raise - - vif_plugin = ports[0].binding_vif_type - - # NOTE(ltomasbo): Due to the bug (1696051) on neutron bulk port - # creation request returning the port objects without binding - # information, an additional port show is performed to get the binding - # information - if vif_plugin == 'unbound': - port_info = os_net.get_port(ports[0].id) - vif_plugin = port_info.binding_vif_type - - self._check_port_binding(ports) - if not self._tag_on_creation: - utils.tag_neutron_resources(ports) - vifs = [] - for port in ports: - vif = ovu.neutron_to_osvif_vif(vif_plugin, port, subnets) - vifs.append(vif) - return vifs - - def release_vif(self, pod, vif, project_id=None): - clients.get_network_client().delete_port(vif.id) - - def activate_vif(self, vif, **kwargs): - if vif.active: - return - - os_net = clients.get_network_client() - try: - port = os_net.get_port(vif.id) - except os_exc.SDKException: - LOG.debug("Unable to obtain port information, retrying.") - raise k_exc.ResourceNotReady(vif) - - if port['status'] != kl_const.PORT_STATUS_ACTIVE: - raise k_exc.PortNotReady(vif.id, port['status']) - - vif.active = True - - def update_vif_sgs(self, pod, security_groups): - os_net = clients.get_network_client() - kp = utils.get_kuryrport(pod) - vifs = utils.get_vifs(kp) - if vifs: - # NOTE(ltomasbo): It just updates the default_vif security group - port_id = vifs[constants.DEFAULT_IFNAME].id - os_net.update_port(port_id, security_groups=list(security_groups)) - - def _get_port_request(self, pod, project_id, subnets, security_groups, - unbound=False): - port_req_body = {'project_id': project_id, - 'network_id': utils.get_network_id(subnets), - 'fixed_ips': ovu.osvif_to_neutron_fixed_ips(subnets), - 'device_owner': kl_const.DEVICE_OWNER, - 'admin_state_up': True, - 'binding_host_id': utils.get_host_id(pod)} - - # if unbound argument is set to true, it means the port requested - # should not be bound and not associated to the pod. Thus the port dict - # is filled with a generic name (constants.KURYR_PORT_NAME) if - # port_debug is enabled, and without device_id - if unbound and config.CONF.kubernetes.port_debug: - port_req_body['name'] = constants.KURYR_PORT_NAME - else: - # only set the name if port_debug is enabled - if config.CONF.kubernetes.port_debug: - port_req_body['name'] = utils.get_port_name(pod) - port_req_body['device_id'] = utils.get_device_id(pod) - - if security_groups: - port_req_body['security_groups'] = security_groups - - if self._tag_on_creation: - tags = CONF.neutron_defaults.resource_tags - if tags: - port_req_body['tags'] = tags - - return port_req_body - - def _check_port_binding(self, ports): - if ports[0].binding_vif_type == "binding_failed": - for port in ports: - clients.get_network_client().delete_port(port.id) - LOG.error("Binding failed error for ports: %s." - " Please check Neutron for errors.", ports) - raise k_exc.ResourceNotReady(ports) diff --git a/kuryr_kubernetes/controller/drivers/node_subnets.py b/kuryr_kubernetes/controller/drivers/node_subnets.py deleted file mode 100644 index baacdcdd1..000000000 --- a/kuryr_kubernetes/controller/drivers/node_subnets.py +++ /dev/null @@ -1,179 +0,0 @@ -# Copyright 2020 Red Hat, 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. - -from openstack import exceptions as os_exc -from oslo_concurrency import lockutils -from oslo_config import cfg -from oslo_log import log as logging - -from kuryr_kubernetes import clients -from kuryr_kubernetes import constants -from kuryr_kubernetes.controller.drivers import base -from kuryr_kubernetes import exceptions -from kuryr_kubernetes import utils - - -CONF = cfg.CONF -LOG = logging.getLogger(__name__) - - -class ConfigNodesSubnets(base.NodesSubnetsDriver): - """Provides list of nodes subnets from configuration.""" - - def get_nodes_subnets(self, raise_on_empty=False): - node_subnet_ids = CONF.pod_vif_nested.worker_nodes_subnets - if not node_subnet_ids: - if raise_on_empty: - raise cfg.RequiredOptError( - 'worker_nodes_subnets', cfg.OptGroup('pod_vif_nested')) - else: - return [] - - return node_subnet_ids - - def add_node(self, node): - return False - - def delete_node(self, node): - return False - - -class OpenShiftNodesSubnets(base.NodesSubnetsDriver): - """Provides list of nodes subnets based on OpenShift Machine objects.""" - - def __init__(self): - super().__init__() - self.subnets = set() - - def _get_subnet_from_machine(self, machine): - spec = machine['spec'].get('providerSpec', {}).get('value') - subnet_id = None - trunk = spec.get('trunk') - k8s = clients.get_kubernetes_client() - - if 'primarySubnet' in spec: - # NOTE(gryf) in old OpenShift versions, primarySubnet was used for - # selecting primary subnet from multiple networks. In the future - # this field will be deprecated. - - os_net = clients.get_network_client() - try: - subnet = os_net.find_subnet(spec['primarySubnet']) - except os_exc.DuplicateResource: - LOG.error('Name "%s" defined in primarySubnet for Machine/' - 'MachineSet found in more than one subnets, which ' - 'may lead to issues. Please, use desired subnet id ' - 'instead.', spec['primarySubnet']) - k8s.add_event(machine, 'AmbiguousPrimarySubnet', - f'Name "{spec["primarySubnet"]}" defined in ' - f'primarySubnet for Machine/MachineSet found in ' - f'multiple subnets, which may lead to issues. ' - f'Please, use desired subnet id instead.', - 'Warning', 'kuryr-controller') - return None - except os_exc.SDKException as ex: - raise exceptions.ResourceNotReady(f'OpenStackSDK threw an ' - f'exception {ex}, retrying.') - - if not subnet: - LOG.error('Subnet name/id `%s` provided in MachineSet ' - 'primarySubnet field does not match any subnet. ' - 'Check the configuration.', spec['primarySubnet']) - k8s.add_event(machine, 'PrimarySubnetNotFound', - f'Name "{spec["primarySubnet"]}" defined in ' - f'primarySubnet for Machine/MachineSet does ' - f'not match any subnet. Check the configuration.' - 'Warning', 'kuryr-controller') - return None - - return subnet.id - - if trunk and 'networks' in spec and spec['networks']: - subnets = spec['networks'][0].get('subnets') - if not subnets: - LOG.warning('No `subnets` in Machine `providerSpec.values.' - 'networks`.') - else: - primary_subnet = subnets[0] - if primary_subnet.get('uuid'): - subnet_id = primary_subnet['uuid'] - else: - subnet_filter = primary_subnet['filter'] - subnet_id = utils.get_subnet_id(**subnet_filter) - - if not subnet_id and 'ports' in spec and spec['ports']: - for port in spec['ports']: - if port.get('trunk', trunk) and port.get('fixedIPs'): - for fip in port['fixedIPs']: - if fip.get('subnetID'): - subnet_id = fip['subnetID'] - break - - if not subnet_id: - LOG.warning('No `subnets` found in Machine `providerSpec`') - - return subnet_id - - def get_nodes_subnets(self, raise_on_empty=False): - with lockutils.lock('kuryr-machine-add'): - # We add any hardcoded ones from config anyway. - result = self.subnets - if CONF.pod_vif_nested.worker_nodes_subnets: - result = result.union( - set(CONF.pod_vif_nested.worker_nodes_subnets)) - if not result and raise_on_empty: - raise exceptions.ResourceNotReady( - 'OpenShift Machines does not exist or are not yet ' - 'handled. Cannot determine worker nodes subnets.') - - return list(result) - - def add_node(self, machine): - subnet_id = self._get_subnet_from_machine(machine) - if not subnet_id: - LOG.warning('Could not determine subnet of Machine %s', - machine['metadata']['name']) - return False - - with lockutils.lock('kuryr-machine-add'): - if subnet_id not in self.subnets: - LOG.info('Adding subnet %s to the worker nodes subnets as ' - 'machine %s runs in it.', subnet_id, - machine['metadata']['name']) - self.subnets.add(subnet_id) - return True - return False - - def delete_node(self, machine): - k8s = clients.get_kubernetes_client() - affected_subnet_id = self._get_subnet_from_machine(machine) - if not affected_subnet_id: - LOG.warning('Could not determine subnet of Machine %s', - machine['metadata']['name']) - return False - - machines = k8s.get(constants.OPENSHIFT_API_CRD_MACHINES) - for existing_machine in machines.get('items', []): - if affected_subnet_id == self._get_subnet_from_machine( - existing_machine): - return False - - # We know that subnet is no longer used, so we remove it. - LOG.info('Removing subnet %s from the worker nodes subnets', - affected_subnet_id) - with lockutils.lock('kuryr-machine-add'): - self.subnets.remove(affected_subnet_id) - - return True diff --git a/kuryr_kubernetes/controller/drivers/public_ip.py b/kuryr_kubernetes/controller/drivers/public_ip.py deleted file mode 100644 index f9d55ab90..000000000 --- a/kuryr_kubernetes/controller/drivers/public_ip.py +++ /dev/null @@ -1,178 +0,0 @@ -# Copyright (c) 2017 RedHat, 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 - -from openstack import exceptions as os_exc -from oslo_log import log as logging - -from kuryr_kubernetes import clients -from kuryr_kubernetes.controller.drivers import utils - -LOG = logging.getLogger(__name__) - - -class BasePubIpDriver(object, metaclass=abc.ABCMeta): - """Base class for public IP functionality.""" - - @abc.abstractmethod - def is_ip_available(self, ip_addr, port_id_to_be_associated): - """check availability of ip address - - :param ip_address: - :param port_id_to_be_associated - :returns res_id in case ip is available returns resources id else None - - """ - raise NotImplementedError() - - @abc.abstractmethod - def allocate_ip(self, pub_net_id, project_id, pub_subnet_id=None, - description=None, port_id_to_be_associated=None): - """allocate ip address from public network id - - :param pub_net_id: public network id - :param project_id: - :param pub_subnet_id: public subnet id (Optional) - :param description: string describing request (Optional) - :param port_id_to_be_associated: (optional) - :returns res_id , ip_addr - :res_id - resource id - :ip_addr - ip aaddress - - - """ - raise NotImplementedError() - - @abc.abstractmethod - def free_ip(self, res_id): - """free ip by resource ID - - :param res_id: resource_id - :returns True/False - - """ - raise NotImplementedError() - - @abc.abstractmethod - def associate(self, res_id, vip_port_id): - """Associate VIP port id with resource_id - - :param res_id: id represents pub ip resource - :param vip_port_id: VIP port id - - """ - raise NotImplementedError() - - @abc.abstractmethod - def disassociate(self, res_id): - """Clear association between res_id to any vip port - - :param res_id: id represents pub ip resource - - """ - - -class FipPubIpDriver(BasePubIpDriver): - """Floating IP implementation for public IP capability .""" - - def is_ip_available(self, ip_addr, port_id_to_be_associated=None): - if ip_addr: - os_net = clients.get_network_client() - floating_ips_list = os_net.ips(floating_ip_address=ip_addr) - for entry in floating_ips_list: - if not entry: - continue - if (entry.floating_ip_address == ip_addr): - if not entry.port_id or ( - port_id_to_be_associated is not None - and entry.port_id == port_id_to_be_associated): - return entry.id - # floating IP not available - LOG.error("Floating IP=%s not available", ip_addr) - else: - LOG.error("Invalid parameter ip_addr=%s", ip_addr) - return None - - def allocate_ip(self, pub_net_id, project_id, pub_subnet_id=None, - description=None, port_id_to_be_associated=None): - os_net = clients.get_network_client() - - if port_id_to_be_associated is not None: - floating_ips_list = os_net.ips( - port_id=port_id_to_be_associated) - for entry in floating_ips_list: - if not entry: - continue - if (entry['floating_ip_address']): - LOG.debug('FIP %s already allocated to port %s', - entry['floating_ip_address'], - port_id_to_be_associated) - return entry['id'], entry['floating_ip_address'] - - try: - fip = os_net.create_ip(floating_network_id=pub_net_id, - project_id=project_id, - subnet_id=pub_subnet_id, - description=description) - except os_exc.SDKException: - LOG.exception("Failed to create floating IP - netid=%s ", - pub_net_id) - raise - utils.tag_neutron_resources([fip]) - return fip.id, fip.floating_ip_address - - def free_ip(self, res_id): - os_net = clients.get_network_client() - try: - os_net.delete_ip(res_id) - except os_exc.SDKException: - LOG.error("Failed to delete floating_ip_id =%s !", res_id) - return False - return True - - def _update(self, res_id, vip_port_id): - response = None - os_net = clients.get_network_client() - try: - response = os_net.update_ip(res_id, port_id=vip_port_id) - except os_exc.ConflictException: - LOG.warning("Conflict when assigning floating IP with id %s. " - "Checking if it's already assigned correctly.", res_id) - try: - fip = os_net.get_ip(res_id) - except os_exc.NotFoundException: - LOG.exception("Failed to get FIP %s - it doesn't exist.", - res_id) - raise - - if fip.port_id == vip_port_id: - LOG.debug('FIP %s already assigned to %s', res_id, - vip_port_id) - else: - LOG.exception('Failed to assign FIP %s to VIP port %s. It is ' - 'probably already bound', res_id, vip_port_id) - raise - except os_exc.SDKException: - # NOTE(gryf): the response will be None, since in case of - # exception, there will be no value assigned to response variable. - LOG.error("Failed to update_ip, floating_ip_id=%s," - "response=%s!", res_id, response) - raise - - def associate(self, res_id, vip_port_id): - self._update(res_id, vip_port_id) - - def disassociate(self, res_id): - self._update(res_id, None) diff --git a/kuryr_kubernetes/controller/drivers/utils.py b/kuryr_kubernetes/controller/drivers/utils.py deleted file mode 100644 index f2d335e5c..000000000 --- a/kuryr_kubernetes/controller/drivers/utils.py +++ /dev/null @@ -1,770 +0,0 @@ -# Copyright (c) 2018 Samsung Electronics Co.,Ltd -# 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 urllib -import uuid - -import eventlet -import netaddr -from openstack import exceptions as os_exc -from os_vif import objects -from oslo_config import cfg -from oslo_log import log -from oslo_serialization import jsonutils - -from kuryr_kubernetes import clients -from kuryr_kubernetes import constants -from kuryr_kubernetes import exceptions as k_exc -from kuryr_kubernetes import utils - - -OPERATORS_WITH_VALUES = [constants.K8S_OPERATOR_IN, - constants.K8S_OPERATOR_NOT_IN] - -LOG = log.getLogger(__name__) - -CONF = cfg.CONF - - -def get_network_id(subnets): - ids = list({net.id for net in subnets.values()}) - - if len(ids) != 1: - raise k_exc.IntegrityError( - "Subnet mapping %(subnets)s is not valid: " - "%(num_networks)s unique networks found" % - {'subnets': subnets, 'num_networks': len(ids)}) - - return ids[0] - - -def get_port_name(pod): - return get_resource_name(pod['metadata']['name'], - prefix=pod['metadata']['namespace'] + "/") - - -def get_device_id(pod): - return pod['metadata']['uid'] - - -def get_host_id(pod): - return pod['spec']['nodeName'] - - -def get_kuryrport(pod): - k8s = clients.get_kubernetes_client() - try: - return k8s.get(f'{constants.K8S_API_CRD_NAMESPACES}/' - f'{pod["metadata"]["namespace"]}/kuryrports/' - f'{pod["metadata"]["name"]}') - except k_exc.K8sResourceNotFound: - return None - - -def get_vifs(kp): - try: - return {k: objects.base.VersionedObject.obj_from_primitive(v['vif']) - for k, v in kp['status']['vifs'].items()} - except (KeyError, AttributeError, TypeError): - return {} - - -def is_pod_scheduled(pod): - try: - return bool(pod['spec']['nodeName']) - except KeyError: - return False - - -def get_pods(selector, namespace=None): - """Return a k8s object list with the pods matching the selector. - - It accepts an optional parameter to state the namespace where the pod - selector will be apply. If empty namespace is passed, then the pod - selector is applied in all namespaces. - - param selector: k8s selector of types matchLabels or matchExpressions - param namespace: namespace name where the selector will be applied. If - None, the pod selector is applied in all namespaces - return: k8s list object containing all matching pods - - """ - kubernetes = clients.get_kubernetes_client() - - svc_selector = selector.get('selector') - if svc_selector: - labels = replace_encoded_characters(svc_selector) - else: - labels = selector.get('matchLabels', None) - if labels: - # Removing pod-template-hash as pods will not have it and - # otherwise there will be no match - labels.pop('pod-template-hash', None) - labels = replace_encoded_characters(labels) - - exps = selector.get('matchExpressions', None) - if exps: - exps = ', '.join(format_expression(exp) for exp in exps) - if labels: - expressions = urllib.parse.quote("," + exps) - labels += expressions - else: - labels = urllib.parse.quote(exps) - - if namespace: - pods = kubernetes.get( - '{}/namespaces/{}/pods?labelSelector={}'.format( - constants.K8S_API_BASE, namespace, labels)) - else: - pods = kubernetes.get( - '{}/pods?labelSelector={}'.format(constants.K8S_API_BASE, labels)) - - return pods - - -def get_namespaces(selector): - """Return a k8s object list with the namespaces matching the selector. - - param selector: k8s selector of types matchLabels or matchExpressions - return: k8s list object containing all matching namespaces - - """ - kubernetes = clients.get_kubernetes_client() - labels = selector.get('matchLabels', None) - if labels: - labels = replace_encoded_characters(labels) - - exps = selector.get('matchExpressions', None) - if exps: - exps = ', '.join(format_expression(exp) for exp in exps) - if labels: - expressions = urllib.parse.quote("," + exps) - labels += expressions - else: - labels = urllib.parse.quote(exps) - - namespaces = kubernetes.get( - '{}/namespaces?labelSelector={}'.format( - constants.K8S_API_BASE, labels)) - - return namespaces - - -def format_expression(expression): - key = expression['key'] - operator = expression['operator'].lower() - if operator in OPERATORS_WITH_VALUES: - values = expression['values'] - values = str(', '.join(values)) - values = "(%s)" % values - return "%s %s %s" % (key, operator, values) - else: - if operator == constants.K8S_OPERATOR_DOES_NOT_EXIST: - return "!%s" % key - else: - return key - - -def replace_encoded_characters(labels): - labels = urllib.parse.urlencode(labels) - # NOTE(ltomasbo): K8s API does not accept &, so we need to AND - # the matchLabels with ',' or '%2C' instead - labels = labels.replace('&', ',') - return labels - - -def create_security_group_rule(body, knp): - os_net = clients.get_network_client() - k8s = clients.get_kubernetes_client() - - try: - params = dict(body) - if 'ethertype' in params: - # NOTE(gryf): in openstacksdk, there is ether_type attribute in - # the security_group_rule object, in CRD we have 'ethertype' - # instead, just like it was returned by the neutron client. - params['ether_type'] = params['ethertype'] - del params['ethertype'] - sgr = os_net.create_security_group_rule(**params) - return sgr.id - except os_exc.ConflictException as ex: - if 'quota' in ex.details.lower(): - np = utils.get_referenced_object(knp, 'NetworkPolicy') - k8s.add_event(np, 'FailedToCreateSecurityGroupRule', - f'Creating security group rule for corresponding ' - f'Network Policy has failed: {ex}', - 'Warning') - LOG.error("Failed to create security group rule %s: %s", body, - ex.details) - raise - else: - LOG.debug("Failed to create already existing security group " - "rule %s", body) - # Get existent sg rule id from exception message - return str(ex).split()[-1][:-1] - except os_exc.SDKException as exc: - np = utils.get_referenced_object(knp, 'NetworkPolicy') - k8s.add_event(np, 'FailedToCreateSecurityGroupRule', - f'Creating security group rule for corresponding ' - f'Network Policy has failed: {exc}', - 'Warning') - LOG.debug("Error creating security group rule") - raise - - -def check_tag_on_creation(): - """Checks if Neutron supports tagging during bulk port creation. - - :param os_net: Network proxy object from Openstacksdk. - :return: Boolean - """ - os_net = clients.get_network_client() - extension = os_net.find_extension( - name_or_id='tag-ports-during-bulk-creation') - return bool(extension) - - -def delete_security_group_rule(security_group_rule_id, knp): - os_net = clients.get_network_client() - k8s = clients.get_kubernetes_client() - - try: - LOG.debug("Deleting sg rule with ID: %s", security_group_rule_id) - os_net.delete_security_group_rule(security_group_rule_id) - except os_exc.SDKException as exc: - np = utils.get_referenced_object(knp, 'NetworkPolicy') - k8s.add_event(np, 'FailedToDeleteSecurityGroupRule', - f'Deleting security group rule for corresponding ' - f'Network Policy has failed: {exc}', - 'Warning') - LOG.debug("Error deleting security group rule: %s", - security_group_rule_id) - raise - - -def patch_kuryrnetworkpolicy_crd(crd, i_rules, e_rules): - kubernetes = clients.get_kubernetes_client() - crd_name = crd['metadata']['name'] - LOG.debug('Patching KuryrNetworkPolicy CRD %s' % crd_name) - try: - spec = { - 'ingressSgRules': i_rules, - 'egressSgRules': e_rules, - } - - kubernetes.patch_crd('spec', utils.get_res_link(crd), spec) - except k_exc.K8sResourceNotFound: - LOG.debug('KuryrNetworkPolicy CRD not found %s', crd_name) - except k_exc.K8sClientException: - LOG.exception('Error updating KuryrNetworkPolicy CRD %s', crd_name) - raise - - -def create_security_group_rule_body( - direction, port_range_min=None, port_range_max=None, protocol=None, - ethertype='IPv4', cidr=None, - description="Kuryr-Kubernetes NetPolicy SG rule", namespace=None, - pods=None): - - if port_range_min and not port_range_max: - port_range_max = port_range_min - - if cidr and netaddr.IPNetwork(cidr).version == 6: - ethertype = 'IPv6' - - security_group_rule_body = { - 'sgRule': { - 'ethertype': ethertype, - 'description': description, - 'direction': direction, - } - } - if port_range_min and port_range_max: - security_group_rule_body['sgRule']['port_range_min'] = port_range_min - security_group_rule_body['sgRule']['port_range_max'] = port_range_max - if protocol: - security_group_rule_body['sgRule']['protocol'] = protocol.lower() - if cidr: - security_group_rule_body['sgRule']['remote_ip_prefix'] = cidr - if namespace: - security_group_rule_body['namespace'] = namespace - if pods: - security_group_rule_body['affectedPods'] = [ - {'podIP': ip, 'podNamespace': ns} for ip, ns in pods.items() if ip] - LOG.debug("Creating sg rule body %s", security_group_rule_body) - return security_group_rule_body - - -def get_pod_ip(pod): - try: - kp = get_kuryrport(pod) - vif = [x['vif'] for x in kp['status']['vifs'].values() - if x['default']][0] - except (KeyError, TypeError, IndexError): - return None - return (vif['versioned_object.data']['network'] - ['versioned_object.data']['subnets'] - ['versioned_object.data']['objects'][0] - ['versioned_object.data']['ips'] - ['versioned_object.data']['objects'][0] - ['versioned_object.data']['address']) - - -def get_annotations(resource, annotation): - try: - annotations = resource['metadata']['annotations'] - return annotations[annotation] - except KeyError: - return None - - -def get_annotated_labels(resource, annotation_labels): - labels_annotation = get_annotations(resource, annotation_labels) - if labels_annotation: - return jsonutils.loads(labels_annotation) - return None - - -def get_kuryrnetworkpolicy_crds(namespace=None): - - try: - if namespace: - knp_path = '{}/{}/kuryrnetworkpolicies'.format( - constants.K8S_API_CRD_NAMESPACES, namespace) - else: - knp_path = constants.K8S_API_CRD_KURYRNETWORKPOLICIES - knps = get_k8s_resources(knp_path) - LOG.debug("Returning KuryrNetworkPolicies %s", knps) - except k_exc.K8sClientException: - LOG.exception("Exception during fetch KuryrNetworkPolicies. Retrying.") - raise k_exc.ResourceNotReady(knp_path) - return knps - - -def get_kuryrloadbalancer_crds(namespace=None): - if namespace: - klb_path = '{}/{}/kuryrloadbalancers'.format( - constants.K8S_API_CRD_KURYRLOADBALANCERS, namespace) - else: - klb_path = constants.K8S_API_CRD_KURYRLOADBALANCERS - klbs = get_k8s_resources(klb_path) - return klbs - - -def get_k8s_resources(resource_path): - kubernetes = clients.get_kubernetes_client() - k8s_resource = {} - try: - k8s_resource = kubernetes.get(resource_path) - except k_exc.K8sResourceNotFound: - LOG.exception('Kubernetes CRD not found') - return [] - return k8s_resource.get('items', []) - - -def get_k8s_resource(resource_path): - kubernetes = clients.get_kubernetes_client() - k8s_resource = {} - try: - k8s_resource = kubernetes.get(resource_path) - except k_exc.K8sResourceNotFound: - LOG.debug('Kubernetes CRD not found %s', resource_path) - return k8s_resource - return k8s_resource - - -def get_networkpolicies(namespace=None): - # FIXME(dulek): This is awful, shouldn't we have list method on k8s_client? - kubernetes = clients.get_kubernetes_client() - - try: - if namespace: - np_path = '{}/{}/networkpolicies'.format( - constants.K8S_API_NETWORKING_NAMESPACES, namespace) - else: - np_path = constants.K8S_API_POLICIES - nps = kubernetes.get(np_path) - except k_exc.K8sResourceNotFound: - LOG.exception("NetworkPolicy or namespace %s not found", namespace) - raise - except k_exc.K8sClientException: - LOG.exception("Exception when listing NetworkPolicies.") - raise - return nps.get('items', []) - - -def zip_resources(xs, ys): - """Returns tuples of resources matched by namespace and name. - - :param xs: List of objects x, first level of iteration. - :param ys: List of objects y. - :return: List of tuples of matching (x, y) - """ - pairs = [] - for x in xs: - for y in ys: - if utils.get_res_unique_name(x) == utils.get_res_unique_name(y): - pairs.append((x, y)) - break - return pairs - - -def zip_knp_np(knps, nps): - """Returns tuples of matching KuryrNetworkPolicy and NetworkPolicy objs. - - :param knps: List of KuryrNetworkPolicy objects - :param nps: List of NetworkPolicy objects - :return: List of tuples of matching (knp, np) - """ - return zip_resources(knps, nps) - - -def match_expressions(expressions, labels): - for exp in expressions: - exp_op = exp['operator'].lower() - if labels: - if exp_op in OPERATORS_WITH_VALUES: - exp_values = exp['values'] - label_value = labels.get(str(exp['key']), None) - if exp_op == constants.K8S_OPERATOR_IN: - if label_value is None or label_value not in exp_values: - return False - elif exp_op == constants.K8S_OPERATOR_NOT_IN: - if label_value in exp_values: - return False - else: - if exp_op == constants.K8S_OPERATOR_EXISTS: - exists = labels.get(str(exp['key']), None) - if exists is None: - return False - elif exp_op == constants.K8S_OPERATOR_DOES_NOT_EXIST: - exists = labels.get(str(exp['key']), None) - if exists is not None: - return False - else: - if exp_op in (constants.K8S_OPERATOR_IN, - constants.K8S_OPERATOR_EXISTS): - return False - return True - - -def match_labels(crd_labels, labels): - for crd_key, crd_value in crd_labels.items(): - label_value = labels.get(crd_key, None) - if not label_value or crd_value != label_value: - return False - return True - - -def match_selector(selector, labels): - if selector is None: - return True - if labels is None: - labels = {} - crd_labels = selector.get('matchLabels', None) - crd_expressions = selector.get('matchExpressions', None) - - match_exp = match_lb = True - if crd_expressions: - match_exp = match_expressions(crd_expressions, labels) - if crd_labels: - match_lb = match_labels(crd_labels, labels) - return match_exp and match_lb - - -def get_namespace_subnet_cidr(namespace): - kubernetes = clients.get_kubernetes_client() - try: - net_crd_path = (f"{constants.K8S_API_CRD_NAMESPACES}/" - f"{namespace['metadata']['name']}/kuryrnetworks/" - f"{namespace['metadata']['name']}") - net_crd = kubernetes.get(net_crd_path) - except k_exc.K8sResourceNotFound: - LOG.warning('Namespace %s not yet ready', - namespace['metadata']['name']) - return None - except k_exc.K8sClientException: - LOG.exception("Kubernetes Client Exception.") - raise - try: - subnet_cidr = net_crd['status']['subnetCIDR'] - except KeyError: - LOG.debug('Namespace not yet ready %s', - namespace['metadata']['name']) - return None - return subnet_cidr - - -def tag_neutron_resources(resources, exceptions=False): - """Set tags to the provided resources. - - param resources: list of openstacksdk objects to tag. - param exceptions: if true, SDKException will not be ignored - """ - tags = CONF.neutron_defaults.resource_tags - if not tags: - return - - os_net = clients.get_network_client() - for res in resources: - try: - os_net.set_tags(res, tags=tags) - except os_exc.SDKException: - LOG.warning("Failed to tag %s with %s. Ignoring, but this is " - "still unexpected.", res, tags, exc_info=True) - if exceptions: - raise - - -def get_services(namespace=None): - kubernetes = clients.get_kubernetes_client() - try: - if namespace: - services = kubernetes.get( - '{}/namespaces/{}/services'.format(constants.K8S_API_BASE, - namespace)) - else: - services = kubernetes.get( - '{}/services'.format(constants.K8S_API_BASE)) - except k_exc.K8sClientException: - LOG.exception('Exception when getting K8s services.') - raise - return services - - -def service_matches_affected_pods(service, pod_selectors): - """Returns if the service is affected by the pod selectors - - Checks if the service selector matches the labelSelectors of - NetworkPolicies. - - param service: k8s service - param pod_selectors: a list of kubernetes labelSelectors - return: True if the service is selected by any of the labelSelectors - and False otherwise. - """ - svc_selector = service['spec'].get('selector') - if not svc_selector: - return False - for selector in pod_selectors: - if match_selector(selector, svc_selector): - return True - return False - - -def get_namespaced_pods(namespace=None): - kubernetes = clients.get_kubernetes_client() - if namespace: - namespace = namespace['metadata']['name'] - pods = kubernetes.get( - '{}/namespaces/{}/pods'.format( - constants.K8S_API_BASE, namespace)) - else: - pods = kubernetes.get( - '{}/pods'.format( - constants.K8S_API_BASE)) - return pods - - -def get_container_ports(containers, np_port_name, pod): - matched_ports = [] - if utils.is_host_network(pod): - return matched_ports - for container in containers: - for container_port in container.get('ports', []): - if container_port.get('name') == np_port_name: - container_port = container_port.get('containerPort') - if container_port not in matched_ports: - matched_ports.append((pod, container_port)) - return matched_ports - - -def get_ports(resource, port): - """Returns values of ports that have a given port name - - Retrieves the values of ports, defined in the containers - associated to the resource, that has its name matching a - given port. - - param resource: k8s Pod or Namespace - param port: a dict containing a port and protocol - return: A list of tuples of port values and associated pods - """ - containers = resource['spec'].get('containers') - ports = [] - np_port = port.get('port') - if containers: - ports.extend(get_container_ports(containers, np_port, resource)) - else: - pods = get_namespaced_pods(resource).get('items') - for pod in pods: - containers = pod['spec']['containers'] - ports.extend(get_container_ports( - containers, np_port, pod)) - return ports - - -def get_namespace(namespace_name): - kubernetes = clients.get_kubernetes_client() - try: - return kubernetes.get( - '{}/namespaces/{}'.format( - constants.K8S_API_BASE, namespace_name)) - except k_exc.K8sResourceNotFound: - LOG.debug("Namespace not found: %s", - namespace_name) - return None - - -def get_endpoints_targets(name, namespace): - kubernetes = clients.get_kubernetes_client() - target_ips = [] - try: - klb_crd = kubernetes.get( - f'{constants.K8S_API_CRD_NAMESPACES}/{namespace}/' - f'kuryrloadbalancers/{name}') - except k_exc.K8sResourceNotFound: - LOG.debug("KuryrLoadBalancer %s not found on Namespace %s.", - name, namespace) - return target_ips - except k_exc.K8sClientException: - LOG.exception('Exception when getting K8s Endpoints.') - raise - - for ep_slice in klb_crd['spec'].get('endpointSlices', []): - for endpoint in ep_slice.get('endpoints', []): - target_ips.extend(endpoint.get('addresses', [])) - return target_ips - - -def bump_networkpolicy(knp): - kubernetes = clients.get_kubernetes_client() - - try: - kubernetes.annotate( - knp['metadata']['annotations']['networkPolicyLink'], - {constants.K8S_ANNOTATION_POLICY: str(uuid.uuid4())}) - except k_exc.K8sResourceNotFound: - raise - except k_exc.K8sClientException: - LOG.exception("Failed to annotate network policy %s to force its " - "recalculation.", utils.get_res_unique_name(knp)) - raise - - -def bump_networkpolicies(namespace=None): - k8s = clients.get_kubernetes_client() - nps = get_networkpolicies(namespace) - for np in nps: - try: - k8s.annotate(utils.get_res_link(np), - {constants.K8S_ANNOTATION_POLICY: str(uuid.uuid4())}) - except k_exc.K8sResourceNotFound: - # Ignore if NP got deleted. - pass - except k_exc.K8sClientException: - LOG.warning("Failed to annotate network policy %s to force its " - "recalculation.", utils.get_res_unique_name(np)) - continue - - -def is_network_policy_enabled(): - enabled_handlers = CONF.kubernetes.enabled_handlers - svc_sg_driver = CONF.kubernetes.service_security_groups_driver - return 'policy' in enabled_handlers and svc_sg_driver == 'policy' - - -def delete_port(leftover_port): - os_net = clients.get_network_client() - - try: - # NOTE(gryf): there is unlikely, that we get an exception - # like PortNotFound or something, since openstacksdk - # doesn't raise an exception if port doesn't exists nor - # return any information. - os_net.delete_port(leftover_port.id) - return True - except os_exc.SDKException as e: - if "currently a subport for trunk" in str(e): - if leftover_port.status == "DOWN": - LOG.warning("Port %s is in DOWN status but still " - "associated to a trunk. This should " - "not happen. Trying to delete it from " - "the trunk.", leftover_port.id) - - # Get the trunk_id from the error message - trunk_id = ( - str(e).split('trunk')[1].split('.')[0].strip()) - try: - os_net.delete_trunk_subports( - trunk_id, [{'port_id': leftover_port.id}]) - except os_exc.ResourceNotFound: - LOG.debug( - "Port %s already removed from trunk %s", - leftover_port.id, trunk_id) - try: - os_net.delete_port(leftover_port.id) - return True - except os_exc.SDKException: - LOG.exception("Unexpected error deleting " - "leftover port %s. Skipping it " - "and continue with the other " - "rest.", leftover_port.id) - else: - LOG.exception("Unexpected error deleting leftover " - "port %s. Skipping it and " - "continue with the other " - "rest.", leftover_port.id) - return False - - -def get_resource_name(name, uid='', prefix='', suffix=''): - """Get OpenStack resource name out of Kubernetes resources - - Return name for the OpenStack resource, which usually is up to 255 chars - long. And while Kubernetes allows to set resource names up to 253 - characters, that makes a risk to have too long name. This function will - favor UID, prefix and suffix over name of the k8s resource, which will get - truncated if needed. - - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/ - """ - if uid: - uid += '/' - - length = len(f'{prefix}{uid}{name}{suffix}') - - if length > 255: - name = name[:254-(length-254)] - - return f'{prefix}{uid}{name}{suffix}' - - -def delete_ports(leftover_port_list): - pool = eventlet.GreenPool(constants.LEFTOVER_RM_POOL_SIZE) - return all([i for i in pool.imap(delete_port, leftover_port_list)]) - - -def delete_neutron_port(port): - os_net = clients.get_network_client() - try: - os_net.delete_port(port) - except Exception as ex: - # NOTE(gryf): Catching all the exceptions here is intentional, since - # this function is intended to be run in the greenthread. User needs - # to examine return value and decide which exception can be safely - # skipped, and which need to be handled/raised. - return ex - return None diff --git a/kuryr_kubernetes/controller/drivers/vif_pool.py b/kuryr_kubernetes/controller/drivers/vif_pool.py deleted file mode 100644 index 566adf4ba..000000000 --- a/kuryr_kubernetes/controller/drivers/vif_pool.py +++ /dev/null @@ -1,1289 +0,0 @@ -# Copyright (c) 2017 Red Hat, 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 collections -import os -import threading - -import eventlet -from kuryr.lib._i18n import _ -from kuryr.lib import constants as kl_const -from openstack import exceptions as os_exc -from oslo_cache import core as cache -from oslo_concurrency import lockutils -from oslo_config import cfg as oslo_cfg -from oslo_log import log as logging -from oslo_log import versionutils - -from kuryr_kubernetes import clients -from kuryr_kubernetes import config -from kuryr_kubernetes import constants -from kuryr_kubernetes.controller.drivers import base -from kuryr_kubernetes.controller.drivers import utils as c_utils -from kuryr_kubernetes.controller.managers import pool -from kuryr_kubernetes import exceptions -from kuryr_kubernetes import os_vif_util as ovu -from kuryr_kubernetes import utils - -LOG = logging.getLogger(__name__) - -# Moved out from neutron_default group -vif_pool_driver_opts = [ - oslo_cfg.IntOpt('ports_pool_max', - help=_("Set a maximum amount of ports per pool. " - "0 to disable"), - default=0), - oslo_cfg.IntOpt('ports_pool_min', - help=_("Set a target minimum size of the pool of ports"), - default=5), - oslo_cfg.IntOpt('ports_pool_batch', - help=_("Number of ports to be created in a bulk request"), - default=10), - oslo_cfg.IntOpt('ports_pool_update_frequency', - help=_("Minimum interval (in seconds) " - "between pool updates"), - default=20), - oslo_cfg.DictOpt('pools_vif_drivers', - help=_("Dict with the pool driver and pod driver to be " - "used. If not set, it will take them from the " - "kubernetes driver options for pool and pod " - "drivers respectively"), - default={}, deprecated_for_removal=True, - deprecated_since="Stein", - deprecated_reason=_( - "Mapping from pool->vif does not allow different " - "vifs to use the same pool driver. " - "Use vif_pool_mapping instead.")), - oslo_cfg.DictOpt('vif_pool_mapping', - help=_("Dict with the pod driver and the corresponding " - "pool driver to be used. If not set, it will take " - "them from the kubernetes driver options for pool " - "and pod drivers respectively"), - default={}), -] - -oslo_cfg.CONF.register_opts(vif_pool_driver_opts, "vif_pool") - -node_vif_driver_caching_opts = [ - oslo_cfg.BoolOpt('caching', default=True, - help=_('Enable caching of vifs.')), - oslo_cfg.IntOpt('cache_time', default=3600, - help=_('TTL, in seconds, for cached vifs')), -] - -oslo_cfg.CONF.register_opts(node_vif_driver_caching_opts, - "node_driver_caching") - -cache.configure(oslo_cfg.CONF) -node_driver_cache_region = cache.create_region() -MEMOIZE = cache.get_memoization_decorator( - oslo_cfg.CONF, node_driver_cache_region, "node_driver_caching") - -cache.configure_cache_region(oslo_cfg.CONF, node_driver_cache_region) - -VIF_TYPE_TO_DRIVER_MAPPING = { - 'VIFOpenVSwitch': 'neutron-vif', - 'VIFBridge': 'neutron-vif', - 'VIFVlanNested': 'nested-vlan', - 'VIFMacvlanNested': 'nested-macvlan', - 'VIFDPDKNested': 'nested-dpdk', - 'VIFVHostUser': 'neutron-vif', -} - -NODE_PORTS_CLEAN_FREQUENCY = 600 # seconds -POPULATE_POOL_TIMEOUT = 420 # seconds -BULK_PORTS_CREATION_REQUESTS = 20 - - -class NoopVIFPool(base.VIFPoolDriver): - """No pool VIFs for Kubernetes Pods""" - - def set_vif_driver(self, driver): - self._drv_vif = driver - - def request_vif(self, pod, project_id, subnets, security_groups): - return self._drv_vif.request_vif(pod, project_id, subnets, - security_groups) - - def release_vif(self, pod, vif, *argv): - self._drv_vif.release_vif(pod, vif, *argv) - - def activate_vif(self, vif, **kwargs): - self._drv_vif.activate_vif(vif, **kwargs) - - def update_vif_sgs(self, pod, sgs): - self._drv_vif.update_vif_sgs(pod, sgs) - - def remove_sg_from_pools(self, sg_id, net_id): - pass - - def sync_pools(self): - pass - - -class BaseVIFPool(base.VIFPoolDriver, metaclass=abc.ABCMeta): - """Skeletal pool driver. - - In order to handle the pools of ports, a few dicts are used: - _available_ports_pool is a dictionary with the ready to use Neutron ports - information. The keys are the 'pool_key' and the values the 'port_id's. - _existing_vifs is a dictionary containing the port vif objects. The keys - are the 'port_id' and the values are the vif objects. - _recyclable_ports is a dictionary with the Neutron ports to be - recycled. The keys are the 'port_id' and their values are the 'pool_key'. - _populate_pool_lock is a dict with the pool_key as key and a lock as value. - Also, there is a _lock to control access to _populate_pool_lock dict. - - The following driver configuration options exist: - - ports_pool_max: it specifies how many ports can be kept at each pool. - If the pool already reached the specified size, the ports to be recycled - are deleted instead. If set to 0, the limit is disabled and ports are - always recycled. - - ports_pool_min: minimum desired number of ready to use ports at populated - pools. Should be smaller than ports_pool_max (if enabled). - - ports_pool_batch: target number of ports to be created in bulk requests - when populating pools. - - ports_pool_update_frequency: interval in seconds between ports pool - updates for recycling ports. - Also, it has a Semaphore _create_ports_semaphore to restrict the number of - bulk Ports creation calls running in parallel. - """ - - def __init__(self): - # Note(ltomasbo) Execute the port recycling periodic actions in a - # background thread - self._recovered_pools = False - eventlet.spawn(self._return_ports_to_pool) - eventlet.spawn(self._cleanup_removed_nodes) - - def set_vif_driver(self, driver): - self._drv_vif = driver - - def activate_vif(self, vif, **kwargs): - self._drv_vif.activate_vif(vif, **kwargs) - - def update_vif_sgs(self, pod, sgs): - self._drv_vif.update_vif_sgs(pod, sgs) - - def _get_pool_size(self, pool_key): - pool = self._available_ports_pools.get(pool_key, {}) - pool_members = [] - for port_list in pool.values(): - pool_members.extend(port_list) - return len(pool_members) - - def _get_host_addr(self, pod): - return pod['status']['hostIP'] - - def _get_pool_key(self, host, project_id, net_id=None, subnets=None): - if not net_id and subnets: - net_obj = list(subnets.values())[0] - net_id = net_obj.id - pool_key = (host, project_id, net_id) - return pool_key - - def _get_pool_key_net(self, pool_key): - return pool_key[2] - - def request_vif(self, pod, project_id, subnets, security_groups): - if not self._recovered_pools: - LOG.debug("Kuryr-controller not yet ready to handle new pods.") - raise exceptions.ResourceNotReady(pod) - try: - host_addr = self._get_host_addr(pod) - except KeyError: - return None - - pool_key = self._get_pool_key(host_addr, project_id, None, subnets) - - # NOTE(maysams): It's possible that more recent Pods will retrieve - # the Ports from the pool that older Pods were waiting for. In case - # this happens, the event will be retried. - try: - return self._get_port_from_pool(pool_key, pod, subnets, - tuple(sorted(security_groups))) - except exceptions.ResourceNotReady: - LOG.debug("Ports pool does not have available ports: %s", pool_key) - if self._populate_pool(pool_key, pod, subnets, - tuple(sorted(security_groups))): - return self._get_port_from_pool( - pool_key, pod, subnets, tuple(sorted(security_groups))) - raise - - def _set_port_debug(self, port_id, pod): - """_set_port_debug sets name to the port to simplify debugging""" - raise NotImplementedError() - - def _get_populate_pool_lock(self, pool_key): - with self._lock: - return self._populate_pool_lock[pool_key] - - def _get_port_from_pool(self, pool_key, pod, subnets, security_groups): - try: - pool_ports = self._available_ports_pools[pool_key] - except (KeyError, AttributeError): - raise exceptions.ResourceNotReady(pod) - - try: - port_id = pool_ports[security_groups].pop() - except (KeyError, IndexError): - # Get another port from the pool and update the SG to the - # appropriate one. It uses a port from the group that was updated - # longer ago - these will be at the front of the OrderedDict. - for sg_group, ports in pool_ports.items(): - try: - port_id = pool_ports[sg_group].pop() - break - except (IndexError, KeyError): - continue - else: - # pool is empty, no port to reuse - raise exceptions.ResourceNotReady(pod) - os_net = clients.get_network_client() - os_net.update_port(port_id, security_groups=list(security_groups)) - if config.CONF.kubernetes.port_debug: - self._set_port_debug(port_id, pod) - eventlet.spawn(self._populate_pool, pool_key, pod, subnets, - security_groups) - # Add protection from port_id not in existing_vifs - try: - port = self._existing_vifs[port_id] - except KeyError: - LOG.debug('Missing port on existing_vifs, this should not happen.' - ' Retrying.') - raise exceptions.ResourceNotReady(pod) - return port - - def _populate_pool(self, pool_key, pod, subnets, security_groups): - # REVISIT(ltomasbo): Drop the subnets parameter and get the information - # from the pool_key, which will be required when multi-network is - # supported - kubernetes = clients.get_kubernetes_client() - - if not self._recovered_pools: - LOG.debug("Kuryr-controller not yet ready to populate pools.") - return False - ports_pool_min = oslo_cfg.CONF.vif_pool.ports_pool_min - lock = self._get_populate_pool_lock(pool_key) - # NOTE(maysams): Only allow one request vifs per pool and times out - # if takes 420 sec. - if lock.acquire(timeout=POPULATE_POOL_TIMEOUT): - pool_size = self._get_pool_size(pool_key) - try: - if pool_size < ports_pool_min: - num_ports = max(oslo_cfg.CONF.vif_pool.ports_pool_batch, - ports_pool_min - pool_size) - try: - vifs = self._drv_vif.request_vifs( - pod=pod, - project_id=pool_key[1], - subnets=subnets, - security_groups=security_groups, - num_ports=num_ports, - semaphore=self._create_ports_semaphore) - except os_exc.SDKException as exc: - kubernetes.add_event( - pod, 'FailToPopulateVIFPool', - f'There was an error during populating VIF pool ' - f'for pod: {exc.message}', type_='Warning') - raise - - for vif in vifs: - self._existing_vifs[vif.id] = vif - self._available_ports_pools[pool_key].setdefault( - security_groups, []).append(vif.id) - if vifs: - # Mark it as updated most recently. - self._available_ports_pools[pool_key].move_to_end( - security_groups) - finally: - lock.release() - else: - return False - return True - - def release_vif(self, pod, vif, project_id, host_addr=None): - if not self._recovered_pools: - LOG.debug("Kuryr-controller not yet ready to remove pods.") - raise exceptions.ResourceNotReady(pod) - if not host_addr: - host_addr = self._get_host_addr(pod) - - pool_key = self._get_pool_key(host_addr, project_id, vif.network.id, - None) - - try: - if not self._existing_vifs.get(vif.id): - self._existing_vifs[vif.id] = vif - self._recyclable_ports[vif.id] = pool_key - except AttributeError: - LOG.debug("Kuryr-controller is not ready to handle the pools yet.") - raise exceptions.ResourceNotReady(pod) - - def _return_ports_to_pool(self): - raise NotImplementedError() - - def _recover_precreated_ports(self): - raise NotImplementedError() - - def _get_in_use_ports_info(self): - kubernetes = clients.get_kubernetes_client() - in_use_ports = [] - networks = {} - kuryr_ports = kubernetes.get(constants.K8S_API_CRD_KURYRPORTS) - for kp in kuryr_ports['items']: - vifs = c_utils.get_vifs(kp) - for data in vifs.values(): - in_use_ports.append(data.id) - networks[data.network.id] = data.network - return in_use_ports, networks - - def list_pools(self): - return self._available_ports_pools - - def show_pool(self, pool_key): - return self._available_ports_pools.get(pool_key) - - def delete_network_pools(self, net_id): - raise NotImplementedError() - - def remove_sg_from_pools(self, sg_id, net_id): - os_net = clients.get_network_client() - for pool_key, pool_ports in list(self._available_ports_pools.items()): - if self._get_pool_key_net(pool_key) != net_id: - continue - for sg_key, ports in list(pool_ports.items()): - if sg_id not in sg_key: - continue - # remove the pool associated to that SG - try: - del self._available_ports_pools[pool_key][sg_key] - except KeyError: - LOG.debug("SG already removed from the pool. Ports " - "already re-used, no need to change their " - "associated SGs.") - continue - for port_id in ports: - # remove all SGs from the port to be reused - os_net.update_port(port_id, security_groups=None) - # add the port to the default pool - self._available_ports_pools[pool_key].setdefault( - (), []).append(port_id) - # NOTE(ltomasbo): as this ports were not created for this - # pool, ensuring they are used first, marking them as the - # most outdated - self._available_ports_pools[pool_key].move_to_end( - (), last=False) - - def _create_healthcheck_file(self): - # Note(ltomasbo): Create a health check file when the pre-created - # ports are loaded into their corresponding pools. This file is used - # by the readiness probe when the controller is deployed in - # containerized mode. This way the controller pod will not be ready - # until all the pre-created ports have been loaded - try: - with open('/tmp/pools_loaded', 'a'): - LOG.debug("Health check file created for readiness probe") - except IOError: - LOG.exception("I/O error creating the health check file.") - - @lockutils.synchronized('return_to_pool_baremetal') - @lockutils.synchronized('return_to_pool_nested') - def sync_pools(self): - # NOTE(ltomasbo): Ensure readiness probe is not set to true until the - # pools sync is completed in case of controller restart - try: - os.remove('/tmp/pools_loaded') - except OSError: - pass - - self._available_ports_pools = collections.defaultdict( - collections.OrderedDict) - self._existing_vifs = collections.defaultdict() - self._recyclable_ports = collections.defaultdict() - self._lock = threading.Lock() - self._populate_pool_lock = collections.defaultdict(threading.Lock) - semaphore = eventlet.semaphore.Semaphore(BULK_PORTS_CREATION_REQUESTS) - self._create_ports_semaphore = semaphore - - def _get_trunks_info(self): - """Returns information about trunks and their subports. - - This method searches for parent ports and subports among the active - neutron ports. - To find the parent ports it filters the ones that have trunk_details, - i.e., the ones that are the parent port of a trunk. - To find the subports to recover, it filters out the ports that are - already in used by running kubernetes pods. It also filters out the - ports whose device_owner is not related to subports, i.e., the ports - that are not attached to trunks, such as active ports allocated to - running VMs. - At the same time it collects information about ports subnets to - minimize the number of interaction with Neutron API. - - It returns three dictionaries with the needed information about the - parent ports, subports and subnets - - :return: 3 dicts with the trunk details (Key: trunk_id; Value: dict - containing ip and subports), subport details (Key: port_id; Value: - port_object), and subnet details (Key: subnet_id; Value: subnet dict) - """ - # REVISIT(ltomasbo): there is no need to recover the subports - # belonging to trunk ports whose parent port is DOWN as that means no - # pods can be scheduled there. We may need to update this if we allow - # lively extending the kubernetes cluster with VMs that already have - # precreated subports. For instance by shutting down and up a - # kubernetes Worker VM with subports already attached, and the - # controller is restarted in between. - os_net = clients.get_network_client() - parent_ports = {} - subports = {} - subnets = {} - - attrs = {'status': 'ACTIVE'} - tags = config.CONF.neutron_defaults.resource_tags - if tags: - attrs['tags'] = tags - - all_active_ports = os_net.ports(**attrs) - in_use_ports, in_use_networks = self._get_in_use_ports_info() - - for port in all_active_ports: - # Parent port - # NOTE(dulek): We do not filter by worker_nodes_subnets here - # meaning that we might include some unrelated trunks, - # but the consequence is only memory usage. - if port.trunk_details and port.fixed_ips: - parent_ports[port.trunk_details['trunk_id']] = { - 'ip': port.fixed_ips[0]['ip_address'], - 'subports': port.trunk_details['sub_ports']} - else: - # Filter to only get subports that are not in use - if (port.id not in in_use_ports and - port.device_owner in ['trunk:subport', - kl_const.DEVICE_OWNER]): - subports[port.id] = port - # NOTE(ltomasbo): _get_subnet can be costly as it - # needs to call neutron to get network and subnet - # information. This ensures it is only called once - # per subnet in use - subnet_id = port.fixed_ips[0]['subnet_id'] - if not subnets.get(subnet_id): - # NOTE(maysams): Avoid calling Neutron by - # getting the Network and Subnet info from - # Network defined on an existing KuryrPort CR. - # This assumes only one Subnet exists per Network. - network = in_use_networks.get(port.network_id) - if network: - subnets[subnet_id] = {subnet_id: network} - else: - subnets[subnet_id] = { - subnet_id: utils.get_subnet(subnet_id)} - return parent_ports, subports, subnets - - def _cleanup_leftover_ports(self): - os_net = clients.get_network_client() - existing_ports = os_net.ports(device_owner=kl_const.DEVICE_OWNER, - status='DOWN') - - tags = config.CONF.neutron_defaults.resource_tags - if tags: - nets = os_net.networks(tags=tags) - nets_ids = [n.id for n in nets] - for port in existing_ports: - net_id = port.network_id - if net_id in nets_ids: - if port.binding_host_id: - if set(tags).difference(set(port.tags)): - # delete the port if it has binding details, it - # belongs to the deployment subnet and it does not - # have the right tags - try: - os_net.delete_port(port.id) - except os_exc.SDKException: - LOG.debug("Problem deleting leftover port %s. " - "Skipping.", port.id) - else: - # delete port if they have no binding but belong to the - # deployment networks, regardless of their tagging - try: - os_net.delete_port(port.id) - except os_exc.SDKException: - LOG.debug("Problem deleting leftover port %s. " - "Skipping.", port.id) - continue - else: - c_utils.delete_ports([p for p in existing_ports - if not p.binding_host_id]) - - def _cleanup_removed_nodes(self): - """Remove ports associated to removed nodes.""" - previous_ports_to_remove = [] - while True: - # NOTE(ltomasbo): Nodes are not expected to be removed - # frequently, so there is no need to execute this frequently - # either - eventlet.sleep(NODE_PORTS_CLEAN_FREQUENCY) - try: - self._trigger_removed_nodes_ports_cleanup( - previous_ports_to_remove) - except Exception: - LOG.exception('Error while removing the ports associated to ' - 'deleted nodes. It will be retried in %s ' - 'seconds', NODE_PORTS_CLEAN_FREQUENCY) - - def _trigger_removed_nodes_ports_cleanup(self, previous_ports_to_remove): - """Remove ports associated to removed nodes. - - There are two types of ports pool, one for neutron and one for nested. - For the nested, the ports lost their device_owner after being detached, - i.e., after the node they belong to got removed. This means we cannot - find them unless they have been tagged. - - For the neutron ones, we rely on them having the kuryr device owner - and not having binding information, thus ensuring they are not - attached to any node. However, to avoid the case where those ports - are being created at the same time of the cleanup process, we don't - delete them unless we have seen them for 2 iterations. - """ - if not self._recovered_pools: - LOG.debug("Kuryr-controller not yet ready to perform nodes" - " cleanup.") - return - os_net = clients.get_network_client() - tags = config.CONF.neutron_defaults.resource_tags - if tags: - subnetpool_id = config.CONF.namespace_subnet.pod_subnet_pool - if subnetpool_id: - subnets = os_net.subnets(tags=tags, - subnetpool_id=subnetpool_id) - subnets_ids = [s.id for s in subnets] - else: - subnets_ids = [config.CONF.neutron_defaults.pod_subnet] - - # NOTE(ltomasbo): Detached subports gets their device_owner unset - detached_subports = os_net.ports(status='DOWN', tags=tags) - for subport in detached_subports: - # FIXME(ltomasbo): Looking for trunk:subport is only needed - # due to a bug in neutron that does not reset the - # device_owner after the port is detached from the trunk - if subport.device_owner not in ['', 'trunk:subport']: - continue - if subport.id not in previous_ports_to_remove: - # FIXME(ltomasbo): Until the above problem is there, - # we need to add protection for recently created ports - # that are still being attached - previous_ports_to_remove.append(subport.id) - continue - # check if port belonged to kuryr and it was a subport - # FIXME(ltomasbo): Assuming single stack - if len(subport.fixed_ips) != 1: - # This should never happen as there is no option to create - # ports without IPs in Neutron, yet we hit it. So adding - # protection from it - continue - if subport.fixed_ips[0].get('subnet_id') not in subnets_ids: - continue - try: - del self._existing_vifs[subport.id] - except KeyError: - LOG.debug('Port %s is not in the ports list.', subport.id) - port_deleted = c_utils.delete_port(subport) - if port_deleted: - previous_ports_to_remove.remove(subport.id) - - # normal ports, or subports not yet attached - existing_ports = os_net.ports( - device_owner=kl_const.DEVICE_OWNER, - status='DOWN', - tags=tags) - else: - # normal ports, or subports not yet attached - existing_ports = os_net.ports( - device_owner=kl_const.DEVICE_OWNER, - status='DOWN') - - for port in existing_ports: - # NOTE(ltomasbo): It may be that the port got just created and it - # is still being attached and/or being tagged. - if port.id not in previous_ports_to_remove: - previous_ports_to_remove.append(port.id) - continue - - if not port.binding_host_id: - try: - del self._existing_vifs[port.id] - except KeyError: - LOG.debug('Port %s is not in the ports list.', port.id) - try: - os_net.delete_port(port.id) - except os_exc.SDKException: - LOG.debug("Problem deleting leftover port %s. " - "Skipping.", port.id) - else: - previous_ports_to_remove.remove(port.id) - - -class NeutronVIFPool(BaseVIFPool): - """Manages VIFs for Bare Metal Kubernetes Pods.""" - - def _get_host_addr(self, pod): - return pod['spec']['nodeName'] - - def _set_port_debug(self, port_id, pod): - os_net = clients.get_network_client() - os_net.update_port(port_id, name=c_utils.get_port_name(pod), - device_id=pod['metadata']['uid']) - - def _return_ports_to_pool(self): - """Recycle ports to be reused by future pods. - - For each port in the recyclable_ports dict it reapplies - security group if they have been changed and it changes the port - name to available_port if the port_debug option is enabled. - Then the port_id is included in the dict with the available_ports. - - If a maximum number of ports per pool is set, the port will be - deleted if the maximum has been already reached. - """ - while True: - eventlet.sleep(oslo_cfg.CONF.vif_pool.ports_pool_update_frequency) - try: - self._trigger_return_to_pool() - except Exception: - LOG.exception( - 'Error while returning ports to pool. ' - 'It will be retried in %s seconds', - oslo_cfg.CONF.vif_pool.ports_pool_update_frequency) - - @lockutils.synchronized('return_to_pool_baremetal') - def _trigger_return_to_pool(self): - if not self._recovered_pools: - LOG.debug("Kuryr-controller not yet ready to return ports to " - "pools.") - return - os_net = clients.get_network_client() - sg_current = {} - if not config.CONF.kubernetes.port_debug: - attrs = {'device_owner': kl_const.DEVICE_OWNER} - tags = config.CONF.neutron_defaults.resource_tags - if tags: - attrs['tags'] = tags - - for port in os_net.ports(**attrs): - if port.id in self._recyclable_ports: - sg_current[port.id] = tuple(sorted( - port.security_group_ids)) - - for port_id, pool_key in list(self._recyclable_ports.items()): - if (not oslo_cfg.CONF.vif_pool.ports_pool_max or - self._get_pool_size(pool_key) < - oslo_cfg.CONF.vif_pool.ports_pool_max): - port_name = (constants.KURYR_PORT_NAME - if config.CONF.kubernetes.port_debug - else '') - if config.CONF.kubernetes.port_debug: - try: - os_net.update_port(port_id, name=port_name, - device_id='') - except os_exc.SDKException: - LOG.warning("Error changing name for port %s to be " - "reused, put back on the cleanable " - "pool.", port_id) - continue - sg = sg_current.get(port_id) - self._available_ports_pools[pool_key].setdefault( - sg, []).append(port_id) - # Move it to the end of ports to update the SG. - self._available_ports_pools[pool_key].move_to_end(sg) - else: - try: - del self._existing_vifs[port_id] - os_net.delete_port(port_id) - except KeyError: - LOG.debug('Port %s is not in the ports list.', port_id) - try: - del self._recyclable_ports[port_id] - except KeyError: - LOG.debug('Port already recycled: %s', port_id) - - def sync_pools(self): - super(NeutronVIFPool, self).sync_pools() - # NOTE(ltomasbo): Ensure previously created ports are recovered into - # their respective pools - self._cleanup_leftover_ports() - self._recover_precreated_ports() - self._recovered_pools = True - - def _recover_precreated_ports(self): - os_net = clients.get_network_client() - attrs = {'device_owner': kl_const.DEVICE_OWNER} - tags = config.CONF.neutron_defaults.resource_tags - if tags: - attrs['tags'] = tags - - if config.CONF.kubernetes.port_debug: - attrs['name'] = constants.KURYR_PORT_NAME - available_ports = os_net.ports(**attrs) - else: - kuryr_ports = os_net.ports(**attrs) - in_use_ports, _ = self._get_in_use_ports_info() - available_ports = [port for port in kuryr_ports - if port.id not in in_use_ports] - - _, available_subports, _ = self._get_trunks_info() - for port in available_ports: - # NOTE(ltomasbo): ensure subports are not considered for - # recovering in the case of multi pools - if available_subports.get(port.id): - continue - if not port.binding_vif_type or not port.binding_host_id: - # NOTE(ltomasbo): kuryr-controller is running without the - # rights to get the needed information to recover the ports. - # Thus, removing the port instead - os_net = clients.get_network_client() - os_net.delete_port(port.id) - continue - subnet_id = port.fixed_ips[0]['subnet_id'] - subnet = { - subnet_id: utils.get_subnet(subnet_id)} - vif = ovu.neutron_to_osvif_vif(port.binding_vif_type, port, subnet) - net_obj = subnet[subnet_id] - pool_key = self._get_pool_key(port.binding_host_id, - port.project_id, - net_obj.id, None) - - self._existing_vifs[port.id] = vif - self._available_ports_pools[pool_key].setdefault( - tuple(sorted(port.security_group_ids)), []).append(port.id) - - LOG.info("PORTS POOL: pools updated with pre-created ports") - self._create_healthcheck_file() - - def delete_network_pools(self, net_id): - if not self._recovered_pools: - LOG.debug("Kuryr-controller not yet ready to delete network " - "pools.") - raise exceptions.ResourceNotReady(net_id) - - epool = eventlet.GreenPool(constants.LEFTOVER_RM_POOL_SIZE) - - # NOTE(ltomasbo): Note the pods should already be deleted, but their - # associated ports may not have been recycled yet, therefore not being - # on the available_ports_pools dict. The next call forces it to be on - # that dict before cleaning it up - self._trigger_return_to_pool() - for pool_key, ports in list(self._available_ports_pools.items()): - if self._get_pool_key_net(pool_key) != net_id: - continue - ports_id = [] - for sg_ports in ports.values(): - ports_id.extend(sg_ports) - for port_id in ports_id: - try: - del self._existing_vifs[port_id] - except KeyError: - LOG.debug('Port %s is not in the ports list.', port_id) - # NOTE(gryf): openstack client doesn't return information, if - # the port deos not exists - - # Delete ports concurrently - for result in epool.imap(c_utils.delete_neutron_port, ports_id): - if result: - LOG.error('During Neutron port deletion an error occured: ' - '%s', result) - raise result - - del self._available_ports_pools[pool_key] - with self._lock: - try: - del self._populate_pool_lock[pool_key] - except KeyError: - pass - - -class NestedVIFPool(BaseVIFPool): - """Manages VIFs for nested Kubernetes Pods. - - In order to handle the pools of ports for nested Pods, an extra dict is - used: - _known_trunk_ids is a dictionary that keeps the trunk port ids associated - to each pool_key to skip calls to neutron to get the trunk information. - """ - _known_trunk_ids = collections.defaultdict(str) - - def __init__(self): - super(NestedVIFPool, self).__init__() - # Start the pool manager so that pools can be populated/freed on - # demand - if config.CONF.kubernetes.enable_manager: - self._pool_manager = pool.PoolManager() - - def set_vif_driver(self, driver): - self._drv_vif = driver - - def release_vif(self, pod, vif, project_id): - if not self._recovered_pools: - LOG.debug("Kuryr-controller not yet ready to remove pods.") - raise exceptions.ResourceNotReady(pod) - try: - host_addr = self._get_host_addr(pod) - except KeyError: - name = pod['metadata']['name'] - LOG.warning("Pod %s does not have status.hostIP field set when " - "getting deleted. This is unusual. Trying to " - "determine the IP by calling Neutron.", - name) - - parent_id = utils.get_parent_port_id(vif) - if not parent_id: - LOG.warning("Port %s not found, ignoring its release request.", - vif.id) - return - - host_addr = utils.get_parent_port_ip(parent_id) - LOG.debug("Determined hostIP for pod %s is %s", name, host_addr) - - super(NestedVIFPool, self).release_vif( - pod, vif, project_id, host_addr=host_addr) - - def _set_port_debug(self, port_id, pod): - os_net = clients.get_network_client() - os_net.update_port(port_id, name=c_utils.get_port_name(pod)) - - def _return_ports_to_pool(self): - """Recycle ports to be reused by future pods. - - For each port in the recyclable_ports dict it reapplies - security group if they have been changed and it changes the port - name to available_port if the port_debug option is enabled. - Then the port_id is included in the dict with the available_ports. - - If a maximum number of ports per pool is set, the port will be - deleted if the maximum has been already reached. - """ - while True: - eventlet.sleep(oslo_cfg.CONF.vif_pool.ports_pool_update_frequency) - try: - self._trigger_return_to_pool() - except Exception: - LOG.exception( - 'Error while returning ports to pool. ' - 'It will be retried in %s seconds', - oslo_cfg.CONF.vif_pool.ports_pool_update_frequency) - - @lockutils.synchronized('return_to_pool_nested') - def _trigger_return_to_pool(self): - if not self._recovered_pools: - LOG.debug("Kuryr-controller not yet ready to return ports to " - "pools.") - return - os_net = clients.get_network_client() - sg_current = {} - if not config.CONF.kubernetes.port_debug: - attrs = {'device_owner': ['trunk:subport', kl_const.DEVICE_OWNER]} - tags = config.CONF.neutron_defaults.resource_tags - if tags: - attrs['tags'] = tags - kuryr_subports = os_net.ports(**attrs) - for subport in kuryr_subports: - if subport.id in self._recyclable_ports: - sg_current[subport.id] = tuple(sorted( - subport.security_group_ids)) - - for port_id, pool_key in list(self._recyclable_ports.items()): - if (not oslo_cfg.CONF.vif_pool.ports_pool_max or - self._get_pool_size(pool_key) < - oslo_cfg.CONF.vif_pool.ports_pool_max): - port_name = (constants.KURYR_PORT_NAME - if config.CONF.kubernetes.port_debug - else '') - if config.CONF.kubernetes.port_debug: - try: - os_net.update_port(port_id, name=port_name) - except os_exc.SDKException: - LOG.warning("Error changing name for port %s to be " - "reused, put back on the cleanable " - "pool.", port_id) - continue - sg = sg_current.get(port_id) - self._available_ports_pools[pool_key].setdefault( - sg, []).append(port_id) - # Move it to the end of ports to update the SG. - self._available_ports_pools[pool_key].move_to_end(sg) - else: - trunk_id = self._get_trunk_id(pool_key) - try: - self._drv_vif._remove_subport(trunk_id, port_id) - self._drv_vif._release_vlan_id( - self._existing_vifs[port_id].vlan_id) - del self._existing_vifs[port_id] - os_net.delete_port(port_id) - except KeyError: - LOG.debug('Port %s is not in the ports list.', port_id) - except (os_exc.SDKException, os_exc.HttpException): - LOG.warning('Error removing the subport %s', port_id) - continue - try: - del self._recyclable_ports[port_id] - except KeyError: - LOG.debug('Port already recycled: %s', port_id) - - def _get_trunk_id(self, pool_key): - trunk_id = self._known_trunk_ids.get(pool_key, None) - if not trunk_id: - p_port = self._drv_vif._get_parent_port_by_host_ip(pool_key[0]) - trunk_id = self._drv_vif._get_trunk_id(p_port) - self._known_trunk_ids[pool_key] = trunk_id - return trunk_id - - def sync_pools(self): - super(NestedVIFPool, self).sync_pools() - # NOTE(ltomasbo): Ensure previously created ports are recovered into - # their respective pools - self._recover_precreated_ports() - self._recovered_pools = True - eventlet.spawn(self._cleanup_leftover_ports) - - def _recover_precreated_ports(self): - self._precreated_ports(action='recover') - LOG.info("PORTS POOL: pools updated with pre-created ports") - self._create_healthcheck_file() - - def _remove_precreated_ports(self, trunk_ips=None): - self._precreated_ports(action='free', trunk_ips=trunk_ips) - - def _precreated_ports(self, action, trunk_ips=None): - """Removes or recovers pre-created subports at given pools - - This function handles the pre-created ports based on the given action: - - If action is `free` it will remove all the subport from the given - trunk ports, or from all the trunk ports if no trunk_ips are passed. - - If action is `recover` it will discover the existing subports in the - given trunk ports (or in all of them if none are passed) and will add - them (and the needed information) to the respective pools. - """ - os_net = clients.get_network_client() - # Note(ltomasbo): ML2/OVS changes the device_owner to trunk:subport - # when a port is attached to a trunk. However, that is not the case - # for other ML2 drivers, such as ODL. So we also need to look for - # compute:kuryr - - parent_ports, available_subports, subnets = self._get_trunks_info() - - if not available_subports: - return - - # FIXME(ltomasbo): Workaround for ports already detached from trunks - # whose status is ACTIVE - trunks_subports = [subport_id['port_id'] - for p_port in parent_ports.values() - for subport_id in p_port['subports']] - port_ids_to_delete = [p_id for p_id in available_subports - if p_id not in trunks_subports] - for port_id in port_ids_to_delete: - LOG.debug("Deleting port with wrong status: %s", port_id) - try: - os_net.delete_port(port_id) - except os_exc.SDKException: - LOG.exception('Error removing the port %s', port_id) - - for trunk_id, parent_port in parent_ports.items(): - host_addr = parent_port.get('ip') - if trunk_ips and host_addr not in trunk_ips: - continue - - for subport in parent_port.get('subports'): - kuryr_subport = available_subports.get(subport['port_id']) - if not kuryr_subport: - continue - - subnet_id = kuryr_subport.fixed_ips[0]['subnet_id'] - subnet = subnets[subnet_id] - net_obj = subnet[subnet_id] - pool_key = self._get_pool_key(host_addr, - kuryr_subport.project_id, - net_obj.id, None) - - if action == 'recover': - vif = ovu.neutron_to_osvif_vif_nested_vlan( - kuryr_subport, subnet, subport['segmentation_id']) - - self._existing_vifs[kuryr_subport.id] = vif - self._available_ports_pools[pool_key].setdefault( - tuple(sorted(kuryr_subport.security_group_ids)), - []).append(kuryr_subport.id) - - elif action == 'free': - try: - self._drv_vif._remove_subport(trunk_id, - kuryr_subport.id) - os_net.delete_port(kuryr_subport.id) - self._drv_vif._release_vlan_id( - subport['segmentation_id']) - del self._existing_vifs[kuryr_subport.id] - self._available_ports_pools[pool_key][ - tuple(sorted(kuryr_subport.security_group_ids - ))].remove(kuryr_subport.id) - except KeyError: - LOG.debug('Port %s is not in the ports list.', - kuryr_subport.id) - except (os_exc.SDKException, os_exc.HttpException): - LOG.warning('Error removing the subport %s', - kuryr_subport.id) - except ValueError: - LOG.debug('Port %s is not in the available ports ' - 'pool.', kuryr_subport.id) - - @lockutils.synchronized('return_to_pool_nested') - def populate_pool(self, trunk_ip, project_id, subnets, security_groups): - if not self._recovered_pools: - LOG.debug("Kuryr-controller not yet ready to populate pools.") - raise exceptions.ResourceNotReady(trunk_ip) - - pool_key = self._get_pool_key(trunk_ip, project_id, None, subnets) - lock = self._get_populate_pool_lock(pool_key) - - if lock.acquire(timeout=POPULATE_POOL_TIMEOUT): - try: - pools = self._available_ports_pools.get(pool_key) - if not pools: - # NOTE(ltomasbo): If the amount of nodes is large the - # repopulation actions may take too long. Using half of the - # batch to prevent the problem - num_ports = int(max(oslo_cfg.CONF.vif_pool - .ports_pool_batch/2, - oslo_cfg.CONF.vif_pool.ports_pool_min)) - self.force_populate_pool(trunk_ip, project_id, subnets, - security_groups, num_ports) - finally: - lock.release() - else: - LOG.debug("Kuryr-controller timed out waiting for it turn to " - "populate pool, retrying.") - raise exceptions.ResourceNotReady(trunk_ip) - - def force_populate_pool(self, trunk_ip, project_id, subnets, - security_groups, num_ports=None): - """Create a given amount of subports at a given trunk port. - - This function creates a given amount of subports and attaches them to - the specified trunk, adding them to the related subports pool - regardless of the amount of subports already available in the pool. - """ - if not num_ports: - num_ports = oslo_cfg.CONF.vif_pool.ports_pool_batch - vifs = self._drv_vif.request_vifs( - pod=[], - project_id=project_id, - subnets=subnets, - security_groups=security_groups, - num_ports=num_ports, - trunk_ip=trunk_ip, - semaphore=self._create_ports_semaphore) - - pool_key = self._get_pool_key(trunk_ip, project_id, None, subnets) - for vif in vifs: - self._existing_vifs[vif.id] = vif - self._available_ports_pools[pool_key].setdefault( - tuple(sorted(security_groups)), []).append(vif.id) - - def free_pool(self, trunk_ips=None): - """Removes subports from the pool and deletes neutron port resource. - - This function empties the pool of available subports and removes the - neutron port resources of the specified trunk port (or all of them if - no trunk is specified). - """ - self._remove_precreated_ports(trunk_ips) - - def delete_network_pools(self, net_id): - if not self._recovered_pools: - LOG.debug("Kuryr-controller not yet ready to delete network " - "pools.") - raise exceptions.ResourceNotReady(net_id) - - epool = eventlet.GreenPool(constants.LEFTOVER_RM_POOL_SIZE) - ports_to_remove = [] - - # NOTE(ltomasbo): Note the pods should already be deleted, but their - # associated ports may not have been recycled yet, therefore not being - # on the available_ports_pools dict. The next call forces it to be on - # that dict before cleaning it up - self._trigger_return_to_pool() - for pool_key, ports in list(self._available_ports_pools.items()): - if self._get_pool_key_net(pool_key) != net_id: - continue - trunk_id = self._get_trunk_id(pool_key) - ports_id = [p_id for sg_ports in ports.values() - for p_id in sg_ports] - try: - self._drv_vif._remove_subports(trunk_id, ports_id) - except os_exc.NotFoundException: - # We don't know which subport was already removed, but we'll - # attempt a manual detach on DELETE error, so just continue. - pass - except (os_exc.SDKException, os_exc.HttpException): - LOG.exception('Error removing subports from trunk: %s', - trunk_id) - raise exceptions.ResourceNotReady(net_id) - - for port_id in ports_id: - try: - self._drv_vif._release_vlan_id( - self._existing_vifs[port_id].vlan_id) - del self._existing_vifs[port_id] - except KeyError: - LOG.debug('Port %s is not in the ports list.', port_id) - ports_to_remove.append(port_id) - - del self._available_ports_pools[pool_key] - with self._lock: - try: - del self._populate_pool_lock[pool_key] - except KeyError: - pass - - # Parallelize Ports deletion. At this point the Ports - # should have been detatched from Trunk and if not operation - # will be retried - for result in epool.imap(c_utils.delete_neutron_port, ports_to_remove): - if result: - LOG.error('During Neutron port deletion an error occured: %s', - result) - raise exceptions.ResourceNotReady(net_id) - - -class MultiVIFPool(base.VIFPoolDriver): - """Manages pools with different VIF types. - - It manages hybrid deployments containing both Bare Metal and Nested - Kubernetes Pods. To do that it creates a pool per node with a different - pool driver depending on the vif driver that the node is using. - - It assumes a label pod_vif is added to each node to inform about the - driver set for that node. If no label is added, it assumes the default pod - vif: the one specified at kuryr.conf - """ - - def set_vif_driver(self): - self._vif_drvs = {} - vif_pool_mapping = self._get_vif_pool_mapping() - - if not vif_pool_mapping: - pod_vif = oslo_cfg.CONF.kubernetes.pod_vif_driver - drv_vif = base.PodVIFDriver.get_instance() - drv_pool = base.VIFPoolDriver.get_instance() - drv_pool.set_vif_driver(drv_vif) - self._vif_drvs[pod_vif] = drv_pool - return - for pod_driver, pool_driver in vif_pool_mapping.items(): - if not utils.check_suitable_multi_pool_driver_opt(pool_driver, - pod_driver): - LOG.error("The pool(%s) and pod(%s) driver selected are not " - "compatible.", pool_driver, pod_driver) - raise exceptions.MultiPodDriverPoolConfigurationNotSupported() - drv_vif = base.PodVIFDriver.get_instance( - specific_driver=pod_driver) - drv_pool = base.VIFPoolDriver.get_instance( - specific_driver=pool_driver, scope='for:{}'.format(pod_driver)) - drv_pool.set_vif_driver(drv_vif) - self._vif_drvs[pod_driver] = drv_pool - - def request_vif(self, pod, project_id, subnets, security_groups): - pod_info = "%s/%s" % (pod['metadata']['namespace'], - pod['metadata']['name']) - try: - pod_vif_type = self._get_pod_vif_type(pod) - except KeyError: - # NOTE(maysams): No nodeName set. Event should be skipped - LOG.warning("Pod %s has no .spec.nodeName set. This is unexpected " - "as it's supposed to be scheduled. Ignoring event.", - pod_info) - return None - return self._vif_drvs[pod_vif_type].request_vif( - pod, project_id, subnets, security_groups) - - def release_vif(self, pod, vif, *argv): - vif_drv_alias = self._get_vif_drv_alias(vif) - self._vif_drvs[vif_drv_alias].release_vif(pod, vif, *argv) - - def activate_vif(self, vif, **kwargs): - vif_drv_alias = self._get_vif_drv_alias(vif) - self._vif_drvs[vif_drv_alias].activate_vif(vif, **kwargs) - - def update_vif_sgs(self, pod, sgs): - pod_vif_type = self._get_pod_vif_type(pod) - self._vif_drvs[pod_vif_type].update_vif_sgs(pod, sgs) - - def remove_sg_from_pools(self, sg_id, net_id): - for vif_drv in self._vif_drvs.values(): - if str(vif_drv) == 'NoopVIFPool': - continue - vif_drv.remove_sg_from_pools(sg_id, net_id) - - def delete_network_pools(self, net_id): - for vif_drv in self._vif_drvs.values(): - if str(vif_drv) == 'NoopVIFPool': - continue - vif_drv.delete_network_pools(net_id) - - def sync_pools(self): - for vif_drv in self._vif_drvs.values(): - vif_drv.sync_pools() - - def _get_pod_vif_type(self, pod): - node_name = pod['spec']['nodeName'] - return self._get_node_vif_driver(node_name) - - @MEMOIZE - def _get_node_vif_driver(self, node_name): - kubernetes = clients.get_kubernetes_client() - node_info = kubernetes.get( - constants.K8S_API_BASE + '/nodes/' + node_name) - - labels = node_info['metadata'].get('labels', None) - if labels: - pod_vif = labels.get('pod_vif', - oslo_cfg.CONF.kubernetes.pod_vif_driver) - return pod_vif - return oslo_cfg.CONF.kubernetes.pod_vif_driver - - def _get_vif_drv_alias(self, vif): - vif_type_name = type(vif).__name__ - return VIF_TYPE_TO_DRIVER_MAPPING[vif_type_name] - - def _get_vif_pool_mapping(self): - vif_pool_mapping = oslo_cfg.CONF.vif_pool.vif_pool_mapping - - if not vif_pool_mapping: - pools_vif_drivers = oslo_cfg.CONF.vif_pool.pools_vif_drivers - - if pools_vif_drivers: - msg = ("Config option vif_pool.pools_vif_drivers is " - "deprecated in favour of vif_pool.vif_pool_mapping, " - "and will be removed in a future release") - versionutils.report_deprecated_feature(LOG, msg) - - for pool_driver, pod_driver in pools_vif_drivers.items(): - vif_pool_mapping[pod_driver] = pool_driver - - return vif_pool_mapping - - def populate_pool(self, node_ip, project_id, subnets, sg_id): - for vif_drv in self._vif_drvs.values(): - if str(vif_drv) == 'NestedVIFPool': - vif_drv.populate_pool(node_ip, project_id, subnets, sg_id) diff --git a/kuryr_kubernetes/controller/handlers/__init__.py b/kuryr_kubernetes/controller/handlers/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/kuryr_kubernetes/controller/handlers/kuryrnetwork.py b/kuryr_kubernetes/controller/handlers/kuryrnetwork.py deleted file mode 100644 index d55884c9e..000000000 --- a/kuryr_kubernetes/controller/handlers/kuryrnetwork.py +++ /dev/null @@ -1,213 +0,0 @@ -# Copyright 2020 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 openstack import exceptions as os_exc -from oslo_config import cfg as oslo_cfg -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.controller.drivers import utils as driver_utils -from kuryr_kubernetes import exceptions as k_exc -from kuryr_kubernetes.handlers import k8s_base -from kuryr_kubernetes import utils - -LOG = logging.getLogger(__name__) - - -class KuryrNetworkHandler(k8s_base.ResourceEventHandler): - """Controller side of KuryrNetwork process for Kubernetes pods. - - `KuryrNetworkHandler` runs on the Kuryr-Kubernetes controller and is - responsible for creating the OpenStack resources associated to the - newly created namespaces, and update the KuryrNetwork CRD status with - them. - """ - OBJECT_KIND = constants.K8S_OBJ_KURYRNETWORK - OBJECT_WATCH_PATH = constants.K8S_API_CRD_KURYRNETWORKS - - def __init__(self): - super(KuryrNetworkHandler, self).__init__() - self._drv_project = drivers.NamespaceProjectDriver.get_instance() - self._drv_subnets = drivers.PodSubnetsDriver.get_instance() - self._drv_sg = drivers.PodSecurityGroupsDriver.get_instance() - self._drv_vif_pool = drivers.VIFPoolDriver.get_instance( - specific_driver='multi_pool') - self._drv_vif_pool.set_vif_driver() - if driver_utils.is_network_policy_enabled(): - self._drv_lbaas = drivers.LBaaSDriver.get_instance() - self._drv_svc_sg = ( - drivers.ServiceSecurityGroupsDriver.get_instance()) - self.k8s = clients.get_kubernetes_client() - - def on_present(self, kuryrnet_crd, *args, **kwargs): - ns_name = kuryrnet_crd['spec']['nsName'] - project_id = kuryrnet_crd['spec']['projectId'] - kns_status = kuryrnet_crd.get('status', {}) - namespace = driver_utils.get_namespace(ns_name) - - crd_creation = False - net_id = kns_status.get('netId') - if not net_id: - try: - net_id = self._drv_subnets.create_network(namespace, - project_id) - except os_exc.SDKException as ex: - self.k8s.add_event(kuryrnet_crd, 'CreateNetworkFailed', - f'Error during creating Neutron network: ' - f'{ex.details}', 'Warning') - raise - status = {'netId': net_id} - self._patch_kuryrnetwork_crd(kuryrnet_crd, status) - self.k8s.add_event(kuryrnet_crd, 'CreateNetworkSucceed', - f'Neutron network {net_id} for namespace') - crd_creation = True - subnet_id = kns_status.get('subnetId') - if not subnet_id or crd_creation: - try: - subnet_id, subnet_cidr = self._drv_subnets.create_subnet( - namespace, project_id, net_id) - except os_exc.ConflictException as ex: - self.k8s.add_event(kuryrnet_crd, 'CreateSubnetFailed', - f'Error during creating Neutron subnet ' - f'for network {net_id}: {ex.details}', - 'Warning') - raise - status = {'subnetId': subnet_id, 'subnetCIDR': subnet_cidr} - self._patch_kuryrnetwork_crd(kuryrnet_crd, status) - self.k8s.add_event(kuryrnet_crd, 'CreateSubnetSucceed', - f'Neutron subnet {subnet_id} for network ' - f'{net_id}') - crd_creation = True - if not kns_status.get('routerId') or crd_creation: - try: - router_id = self._drv_subnets.add_subnet_to_router(subnet_id) - except os_exc.SDKException as ex: - self.k8s.add_event(kuryrnet_crd, 'AddingSubnetToRouterFailed', - f'Error adding Neutron subnet {subnet_id} ' - f'to router: {ex.details}', - 'Warning') - raise - status = {'routerId': router_id, 'populated': False} - self._patch_kuryrnetwork_crd(kuryrnet_crd, status) - self.k8s.add_event(kuryrnet_crd, 'AddingSubnetToRouterSucceed', - f'Neutron subnet {subnet_id} added to router ' - f'{router_id}') - crd_creation = True - - # check labels to create sg rules - ns_labels = kns_status.get('nsLabels', {}) - if (crd_creation or - ns_labels != kuryrnet_crd['spec']['nsLabels']): - # update SG and svc SGs - crd_selectors = self._drv_sg.update_namespace_sg_rules(namespace) - if (driver_utils.is_network_policy_enabled() and crd_selectors and - oslo_cfg.CONF.octavia_defaults.enforce_sg_rules): - services = driver_utils.get_services() - self._update_services(services, crd_selectors, project_id) - # update status - status = {'nsLabels': kuryrnet_crd['spec']['nsLabels']} - self._patch_kuryrnetwork_crd(kuryrnet_crd, status, labels=True) - self.k8s.add_event(kuryrnet_crd, 'SGUpdateTriggered', - 'Neutron security groups update has been ' - 'triggered') - - def on_finalize(self, kuryrnet_crd, *args, **kwargs): - LOG.debug("Deleting kuryrnetwork CRD resources: %s", kuryrnet_crd) - - net_id = kuryrnet_crd.get('status', {}).get('netId') - if net_id: - self._drv_vif_pool.delete_network_pools(net_id) - try: - self._drv_subnets.delete_namespace_subnet(kuryrnet_crd) - except k_exc.ResourceNotReady: - LOG.debug("Subnet is not ready to be removed.") - # TODO(ltomasbo): Once KuryrPort CRDs is supported, we should - # execute a delete network ports method here to remove the - # ports associated to the namespace/subnet, ensuring next - # retry will be successful - raise - else: - # NOTE(gryf): It may happen, that even if KuryrNetworkCRD was not - # updated (when controller crash in between), but the network and - # possibly subnet is there, so it needs to be searched manually. - ns = self.k8s.get(f'{constants.K8S_API_NAMESPACES}/' - f'{kuryrnet_crd["spec"]["nsName"]}') - ns_name = ns['metadata']['name'] - ns_uid = ns['metadata']['uid'] - net_name = driver_utils.get_resource_name(ns_name) - old_net_name = driver_utils.get_resource_name(ns_name, - prefix='ns/', - suffix='-net') - # TODO(gryf): remove old_net_name support in next release. - os_net = clients.get_network_client() - networks = os_net.networks(name=(net_name, old_net_name)) - for net in networks: - if ns_uid == net.description: - LOG.warning('Found Neutron network associated with ' - 'namespace `%s`, while it is not registered ' - 'on KuryrNetwork CRD. Trying to remove.', - ns_name) - self._drv_vif_pool.delete_network_pools(net.id) - - try: - os_net.delete_network(net) - except os_exc.ConflictException: - LOG.warning("One or more ports in use on the network " - "%s. Retrying.", net.id) - raise k_exc.ResourceNotReady(net.id) - - namespace = { - 'metadata': {'name': kuryrnet_crd['spec']['nsName']}} - crd_selectors = self._drv_sg.delete_namespace_sg_rules(namespace) - - if (driver_utils.is_network_policy_enabled() and crd_selectors and - oslo_cfg.CONF.octavia_defaults.enforce_sg_rules): - project_id = kuryrnet_crd['spec']['projectId'] - services = driver_utils.get_services() - self._update_services(services, crd_selectors, project_id) - - LOG.debug('Removing finalizer for KuryrNetwork CRD %s', kuryrnet_crd) - try: - self.k8s.remove_finalizer(kuryrnet_crd, - constants.KURYRNETWORK_FINALIZER) - except k_exc.K8sClientException: - LOG.exception('Error removing KuryrNetwork CRD finalizer for %s', - kuryrnet_crd) - raise - - def _update_services(self, services, crd_selectors, project_id): - for service in services.get('items'): - if not driver_utils.service_matches_affected_pods( - service, crd_selectors): - continue - sgs = self._drv_svc_sg.get_security_groups(service, - project_id) - self._drv_lbaas.update_lbaas_sg(service, sgs) - - def _patch_kuryrnetwork_crd(self, kuryrnet_crd, status, labels=False): - LOG.debug('Patching KuryrNetwork CRD %s', kuryrnet_crd) - try: - if labels: - self.k8s.patch_crd('status', - utils.get_res_link(kuryrnet_crd), status) - else: - self.k8s.patch('status', utils.get_res_link(kuryrnet_crd), - status) - except k_exc.K8sResourceNotFound: - LOG.debug('KuryrNetwork CRD not found %s', kuryrnet_crd) - except k_exc.K8sClientException: - LOG.exception('Error updating kuryrNetwork CRD %s', kuryrnet_crd) - raise diff --git a/kuryr_kubernetes/controller/handlers/kuryrnetwork_population.py b/kuryr_kubernetes/controller/handlers/kuryrnetwork_population.py deleted file mode 100644 index 248452b55..000000000 --- a/kuryr_kubernetes/controller/handlers/kuryrnetwork_population.py +++ /dev/null @@ -1,97 +0,0 @@ -# Copyright 2020 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 KuryrNetworkPopulationHandler(k8s_base.ResourceEventHandler): - """Controller side of KuryrNetwork process for Kubernetes pods. - - `KuryrNetworkPopulationHandler` runs on the Kuryr-Kubernetes controller - and is responsible for populating pools for newly created namespaces. - """ - OBJECT_KIND = constants.K8S_OBJ_KURYRNETWORK - OBJECT_WATCH_PATH = constants.K8S_API_CRD_KURYRNETWORKS - - def __init__(self): - super(KuryrNetworkPopulationHandler, self).__init__() - self._drv_subnets = drivers.PodSubnetsDriver.get_instance() - self._drv_vif_pool = drivers.VIFPoolDriver.get_instance( - specific_driver='multi_pool') - self._drv_vif_pool.set_vif_driver() - self._drv_nodes_subnets = drivers.NodesSubnetsDriver.get_instance() - - def on_present(self, kuryrnet_crd, *args, **kwargs): - subnet_id = kuryrnet_crd.get('status', {}).get('subnetId') - if not subnet_id: - LOG.debug("No Subnet present for KuryrNetwork %s", - kuryrnet_crd['metadata']['name']) - return - - if kuryrnet_crd['status'].get('populated'): - LOG.debug("Subnet %s already populated for Namespace %s", - subnet_id, kuryrnet_crd['metadata']['name']) - return - - namespace = kuryrnet_crd['spec'].get('nsName') - project_id = kuryrnet_crd['spec'].get('projectId') - # NOTE(ltomasbo): using namespace name instead of object as it is not - # required - subnets = self._drv_subnets.get_namespace_subnet(namespace, subnet_id) - - node_subnets = self._drv_nodes_subnets.get_nodes_subnets( - raise_on_empty=True) - nodes = utils.get_nodes_ips(node_subnets) - # NOTE(ltomasbo): Patching the kuryrnet_crd here instead of after - # populate_pool method to ensure initial repopulation is not happening - # twice upon unexpected problems, such as neutron failing to - # transition the ports to ACTIVE or being too slow replying. - # In such case, even though the repopulation actions got triggered, - # the pools will not get the ports loaded (as they are not ACTIVE) - # and new population actions may be triggered if the controller was - # restarted before performing the populated=true patching. - self._patch_kuryrnetwork_crd(kuryrnet_crd, populated=True) - # TODO(ltomasbo): Skip the master node where pods are not usually - # allocated. - for node_ip in nodes: - LOG.debug("Populating subnet pool %s at node %s", subnet_id, - node_ip) - try: - self._drv_vif_pool.populate_pool(node_ip, project_id, subnets, - []) - except exceptions.ResourceNotReady: - # Ensure the repopulation is retriggered if the system was not - # yet ready to perform the repopulation actions - self._patch_kuryrnetwork_crd(kuryrnet_crd, populated=False) - raise - - def _patch_kuryrnetwork_crd(self, kns_crd, populated=True): - kubernetes = clients.get_kubernetes_client() - crd_name = kns_crd['metadata']['name'] - LOG.debug('Patching KuryrNetwork CRD %s' % crd_name) - try: - kubernetes.patch_crd('status', utils.get_res_link(kns_crd), - {'populated': populated}) - except exceptions.K8sClientException: - LOG.exception('Error updating KuryrNetwork CRD %s', crd_name) - raise diff --git a/kuryr_kubernetes/controller/handlers/kuryrnetworkpolicy.py b/kuryr_kubernetes/controller/handlers/kuryrnetworkpolicy.py deleted file mode 100644 index ce7ae274d..000000000 --- a/kuryr_kubernetes/controller/handlers/kuryrnetworkpolicy.py +++ /dev/null @@ -1,319 +0,0 @@ -# Copyright 2019 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 openstack import exceptions as os_exc -from oslo_config import cfg -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.controller.drivers import utils as driver_utils -from kuryr_kubernetes import exceptions -from kuryr_kubernetes.handlers import k8s_base -from kuryr_kubernetes import utils - -LOG = logging.getLogger(__name__) -CONF = cfg.CONF - - -class KuryrNetworkPolicyHandler(k8s_base.ResourceEventHandler): - """Controller side of KuryrNetworkPolicy process for Kubernetes pods. - - `KuryrNetworkPolicyHandler` runs on the kuryr-controller and is - responsible for creating and deleting SG and SG rules for `NetworkPolicy`. - The `KuryrNetworkPolicy` objects are created by `NetworkPolicyHandler`. - """ - OBJECT_KIND = constants.K8S_OBJ_KURYRNETWORKPOLICY - OBJECT_WATCH_PATH = constants.K8S_API_CRD_KURYRNETWORKPOLICIES - - def __init__(self): - super(KuryrNetworkPolicyHandler, self).__init__() - self.os_net = clients.get_network_client() - self.k8s = clients.get_kubernetes_client() - self._drv_project = drivers.NetworkPolicyProjectDriver.get_instance() - self._drv_policy = drivers.NetworkPolicyDriver.get_instance() - self._drv_vif_pool = drivers.VIFPoolDriver.get_instance( - specific_driver='multi_pool') - self._drv_vif_pool.set_vif_driver() - self._drv_pod_sg = drivers.PodSecurityGroupsDriver.get_instance() - self._drv_svc_sg = drivers.ServiceSecurityGroupsDriver.get_instance() - self._drv_lbaas = drivers.LBaaSDriver.get_instance() - - def _patch_kuryrnetworkpolicy_crd(self, knp, field, data, - action='replace'): - name = knp['metadata']['name'] - LOG.debug('Patching KuryrNetwork CRD %s', name) - try: - status = self.k8s.patch_crd(field, utils.get_res_link(knp), - data, action=action) - except exceptions.K8sResourceNotFound: - LOG.debug('KuryrNetworkPolicy CRD not found %s', name) - return None - except exceptions.K8sClientException as exc: - np = utils.get_referenced_object(knp, 'NetworkPolicy') - self.k8s.add_event(np, 'FailedToPatchKuryrNetworkPolicy', - f'Failed to update KuryrNetworkPolicy CRD: ' - f'{exc}', 'Warning') - LOG.exception('Error updating KuryrNetworkPolicy CRD %s', name) - raise - - knp['status'] = status - return knp - - def _get_networkpolicy(self, link): - return self.k8s.get(link) - - def _compare_sgs(self, a, b): - checked_props = ('direction', 'ethertype', 'port_range_max', - 'port_range_min', 'protocol', 'remote_ip_prefix') - - for k in checked_props: - if a.get(k) != b.get(k): - return False - return True - - def _find_sgs(self, a, rules): - for r in rules: - if self._compare_sgs(r, a): - return True - - return False - - def on_present(self, knp, *args, **kwargs): - uniq_name = utils.get_res_unique_name(knp) - LOG.debug('on_present() for NP %s', uniq_name) - project_id = self._drv_project.get_project(knp) - if not knp['status'].get('securityGroupId'): - LOG.debug('Creating SG for NP %s', uniq_name) - # TODO(dulek): Do this right, why do we have a project driver per - # resource?! This one expects policy, not knp, but it - # ignores it anyway! - sg_id = self._drv_policy.create_security_group(knp, project_id) - knp = self._patch_kuryrnetworkpolicy_crd( - knp, 'status', {'securityGroupId': sg_id}) - LOG.debug('Created SG %s for NP %s', sg_id, uniq_name) - else: - # TODO(dulek): Check if it really exists, recreate if not. - sg_id = knp['status'].get('securityGroupId') - - # First update SG rules as we want to apply updated ones - current = knp['status']['securityGroupRules'] - required = knp['spec']['ingressSgRules'] + knp['spec']['egressSgRules'] - required = [r['sgRule'] for r in required] - - # FIXME(dulek): This *might* be prone to race conditions if failure - # happens between SG rule is created/deleted and status - # is annotated. We don't however need to revert on failed - # K8s operations - creation, deletion of SG rules and - # attaching or detaching SG from ports are idempotent - # so we can repeat them. What worries me is losing track - # of an update due to restart. The only way to do it - # would be to periodically check if what's in `status` - # is the reality in OpenStack API. That should be just - # two Neutron API calls + possible resync. - to_add = [] - to_remove = [] - for r in required: - if not self._find_sgs(r, current): - to_add.append(r) - - for i, c in enumerate(current): - if not self._find_sgs(c, required): - to_remove.append((i, c['id'])) - - LOG.debug('SGs to add for NP %s: %s', uniq_name, to_add) - - for sg_rule in to_add: - LOG.debug('Adding SG rule %s for NP %s', sg_rule, uniq_name) - sg_rule['security_group_id'] = sg_id - sgr_id = driver_utils.create_security_group_rule(sg_rule, knp) - sg_rule['id'] = sgr_id - knp = self._patch_kuryrnetworkpolicy_crd( - knp, 'status', {'securityGroupRules/-': sg_rule}, 'add') - - # We need to remove starting from the last one in order to maintain - # indexes. Please note this will start to fail miserably if we start - # to change status from multiple places. - to_remove.reverse() - - LOG.debug('SGs to remove for NP %s: %s', uniq_name, - [x[1] for x in to_remove]) - - for i, sg_rule_id in to_remove: - LOG.debug('Removing SG rule %s as it is no longer part of NP %s', - sg_rule_id, uniq_name) - driver_utils.delete_security_group_rule(sg_rule_id, knp) - knp = self._patch_kuryrnetworkpolicy_crd( - knp, 'status/securityGroupRules', i, 'remove') - - pods_to_update = [] - - previous_sel = knp['status'].get('podSelector', None) - current_sel = knp['spec']['podSelector'] - if previous_sel is None: - # Fresh NetworkPolicy that was never applied. - pods_to_update.extend(self._drv_policy.namespaced_pods(knp)) - elif previous_sel != current_sel or previous_sel == {}: - pods_to_update.extend( - self._drv_policy.affected_pods(knp, previous_sel)) - - matched_pods = self._drv_policy.affected_pods(knp) - pods_to_update.extend(matched_pods) - - for pod in pods_to_update: - if (utils.is_host_network(pod) or - not driver_utils.is_pod_scheduled(pod)): - continue - pod_sgs = self._drv_pod_sg.get_security_groups(pod, project_id) - try: - self._drv_vif_pool.update_vif_sgs(pod, pod_sgs) - except os_exc.NotFoundException: - # Pod got deleted in the meanwhile, should be safe to ignore. - pass - - # FIXME(dulek): We should not need this one day. - policy = self._get_networkpolicy(knp['metadata']['annotations'] - ['networkPolicyLink']) - if (pods_to_update and CONF.octavia_defaults.enforce_sg_rules and - not self._is_egress_only_policy(policy)): - # NOTE(ltomasbo): only need to change services if the pods that - # they point to are updated - services = driver_utils.get_services(knp['metadata']['namespace']) - for service in services.get('items', []): - # TODO(ltomasbo): Skip other services that are not affected - # by the policy - # NOTE(maysams): Network Policy is not enforced on Services - # without selectors for Amphora Octavia provider. - # NOTE(dulek): Skip services being deleted. - if (not service['spec'].get('selector') or - service['metadata'].get('deletionTimestamp') or not - self._is_service_affected(service, pods_to_update)): - continue - sgs = self._drv_svc_sg.get_security_groups(service, project_id) - try: - self._drv_lbaas.update_lbaas_sg(service, sgs) - except exceptions.ResourceNotReady: - # We can ignore LB that's being created - its SGs will get - # handled when members will be getting created. - pass - - self._patch_kuryrnetworkpolicy_crd(knp, 'status', - {'podSelector': current_sel}) - - def _is_service_affected(self, service, affected_pods): - svc_namespace = service['metadata']['namespace'] - svc_selector = service['spec'].get('selector') - svc_pods = driver_utils.get_pods({'selector': svc_selector}, - svc_namespace).get('items') - return any(pod in svc_pods for pod in affected_pods) - - def _is_egress_only_policy(self, policy): - policy_types = policy['spec'].get('policyTypes', []) - return (policy_types == ['Egress'] or - (policy['spec'].get('egress') and - not policy['spec'].get('ingress'))) - - def _get_policy_net_id(self, knp): - policy_ns = knp['metadata']['namespace'] - - try: - path = (f'{constants.K8S_API_CRD_NAMESPACES}/{policy_ns}/' - f'kuryrnetworks/{policy_ns}') - net_crd = self.k8s.get(path) - except exceptions.K8sClientException: - LOG.exception("Kubernetes Client Exception.") - raise - return net_crd['status']['netId'] - - def on_finalize(self, knp, *args, **kwargs): - LOG.debug("Finalizing KuryrNetworkPolicy %s", knp) - project_id = self._drv_project.get_project(knp) - pods_to_update = self._drv_policy.affected_pods(knp) - crd_sg = knp['status'].get('securityGroupId') - try: - policy = self._get_networkpolicy(knp['metadata']['annotations'] - ['networkPolicyLink']) - except exceptions.K8sResourceNotFound: - # NP is already gone, let's just try to clean up. - policy = None - - if crd_sg: - for pod in pods_to_update: - if (utils.is_host_network(pod) - or not driver_utils.is_pod_scheduled(pod)): - continue - pod_sgs = self._drv_pod_sg.get_security_groups(pod, project_id) - if crd_sg in pod_sgs: - pod_sgs.remove(crd_sg) - if not pod_sgs: - pod_sgs = CONF.neutron_defaults.pod_security_groups - if not pod_sgs: - raise cfg.RequiredOptError( - 'pod_security_groups', - cfg.OptGroup('neutron_defaults')) - try: - self._drv_vif_pool.update_vif_sgs(pod, pod_sgs) - except os_exc.NotFoundException: - # Pod got deleted in the meanwhile, safe to ignore. - pass - - # ensure ports at the pool don't have the NP sg associated - try: - net_id = self._get_policy_net_id(knp) - self._drv_vif_pool.remove_sg_from_pools(crd_sg, net_id) - except exceptions.K8sResourceNotFound: - # Probably the network got removed already, we can ignore it. - pass - - try: - self._drv_policy.delete_np_sg(crd_sg) - except os_exc.SDKException as exc: - np = utils.get_referenced_object(knp, 'NetworkPolicy') - if np: - self.k8s.add_event(np, 'FailedToRemoveSecurityGroup', - f'Deleting security group for ' - f'corresponding Network Policy has ' - f'failed: {exc}', 'Warning') - raise - - if (CONF.octavia_defaults.enforce_sg_rules and policy and - not self._is_egress_only_policy(policy)): - services = driver_utils.get_services( - knp['metadata']['namespace']) - for svc in services.get('items'): - if (not svc['spec'].get('selector') or not - self._is_service_affected(svc, pods_to_update)): - continue - - sgs = self._drv_svc_sg.get_security_groups(svc, project_id) - - if crd_sg in sgs: - # Remove our crd_sg out of service groups since we - # don't have it anymore - sgs.remove(crd_sg) - - try: - self._drv_lbaas.update_lbaas_sg(svc, sgs) - except exceptions.ResourceNotReady: - # We can ignore LB that's being created - its SGs will - # get handled when members will be getting created. - pass - - LOG.debug("Removing finalizers from KuryrNetworkPolicy and " - "NetworkPolicy.") - if policy: - self.k8s.remove_finalizer(policy, - constants.NETWORKPOLICY_FINALIZER) - self.k8s.remove_finalizer(knp, constants.NETWORKPOLICY_FINALIZER) diff --git a/kuryr_kubernetes/controller/handlers/kuryrport.py b/kuryr_kubernetes/controller/handlers/kuryrport.py deleted file mode 100644 index a6275c089..000000000 --- a/kuryr_kubernetes/controller/handlers/kuryrport.py +++ /dev/null @@ -1,403 +0,0 @@ -# Copyright 2020 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. - -import datetime - -from openstack import exceptions as os_exc -from os_vif import objects -from oslo_config import cfg as oslo_cfg -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.controller.drivers import nested_vlan_vif -from kuryr_kubernetes.controller.drivers import utils as driver_utils -from kuryr_kubernetes.controller.managers import prometheus_exporter as exp -from kuryr_kubernetes import exceptions as k_exc -from kuryr_kubernetes.handlers import k8s_base -from kuryr_kubernetes import utils - - -LOG = logging.getLogger(__name__) -KURYRPORT_URI = constants.K8S_API_CRD_NAMESPACES + '/{ns}/kuryrports/{crd}' -ACTIVE_TIMEOUT = nested_vlan_vif.ACTIVE_TIMEOUT - - -class KuryrPortHandler(k8s_base.ResourceEventHandler): - """Controller side of KuryrPort process for Kubernetes pods. - - `KuryrPortHandler` runs on the Kuryr-Kubernetes controller and is - responsible for creating/removing the OpenStack resources associated to - the newly created pods, namely ports and update the KuryrPort CRD data. - """ - OBJECT_KIND = constants.K8S_OBJ_KURYRPORT - OBJECT_WATCH_PATH = constants.K8S_API_CRD_KURYRPORTS - - def __init__(self): - super(KuryrPortHandler, self).__init__() - self._drv_project = drivers.PodProjectDriver.get_instance() - self._drv_subnets = drivers.PodSubnetsDriver.get_instance() - self._drv_sg = drivers.PodSecurityGroupsDriver.get_instance() - # REVISIT(ltomasbo): The VIF Handler should not be aware of the pool - # directly. Due to the lack of a mechanism to load and set the - # VIFHandler driver, for now it is aware of the pool driver, but this - # will be reverted as soon as a mechanism is in place. - self._drv_vif_pool = drivers.VIFPoolDriver.get_instance( - specific_driver='multi_pool') - self._drv_vif_pool.set_vif_driver() - self._drv_multi_vif = drivers.MultiVIFDriver.get_enabled_drivers() - if driver_utils.is_network_policy_enabled(): - self._drv_lbaas = drivers.LBaaSDriver.get_instance() - self._drv_svc_sg = (drivers.ServiceSecurityGroupsDriver - .get_instance()) - self.k8s = clients.get_kubernetes_client() - - def on_present(self, kuryrport_crd, *args, **kwargs): - if not kuryrport_crd['status']['vifs']: - # Get vifs - if not self.get_vifs(kuryrport_crd): - # Ignore this event, according to one of the cases logged in - # get_vifs method. - return - - retry_info = kwargs.get('retry_info') - - vifs = {ifname: {'default': data['default'], - 'vif': objects.base.VersionedObject - .obj_from_primitive(data['vif'])} - for ifname, data in kuryrport_crd['status']['vifs'].items()} - - if all([v['vif'].active for v in vifs.values()]): - return - - changed = False - pod = self._get_pod(kuryrport_crd) - - try: - for ifname, data in vifs.items(): - if not data['vif'].active: - try: - self._drv_vif_pool.activate_vif(data['vif'], pod=pod, - retry_info=retry_info) - changed = True - except k_exc.ResourceNotReady: - if retry_info and retry_info.get('elapsed', - 0) > ACTIVE_TIMEOUT: - self.k8s.add_event(pod, 'ActivatePortFailed', - 'Activating Neutron port has ' - 'timed out', 'Warning') - raise - except os_exc.ResourceNotFound: - self.k8s.add_event(pod, 'ActivatePortFailed', - 'Activating Neutron port has ' - 'failed, possibly deleted', - 'Warning') - LOG.debug("Port not found, possibly already deleted. " - "No need to activate it") - finally: - if changed: - project_id = self._drv_project.get_project(pod) - - try: - kp_name = kuryrport_crd['metadata']['name'] - self._update_kuryrport_crd(kuryrport_crd, vifs) - self.k8s.add_event(pod, 'KuryrPortUpdatedWithActiveVIFs', - f'KuryrPort CRD: {kp_name} updated with' - f' active VIFs') - except k_exc.K8sResourceNotFound as ex: - LOG.exception("Failed to update KuryrPort CRD: %s", ex) - security_groups = self._drv_sg.get_security_groups( - pod, project_id) - for ifname, data in vifs.items(): - self._drv_vif_pool.release_vif(pod, data['vif'], - project_id, - security_groups) - self.k8s.add_event(pod, 'UpdateKuryrPortCRDFailed', - f'Marking ports as ACTIVE in the ' - f'KuryrPort failed: {ex}', 'Warning') - except k_exc.K8sClientException: - raise k_exc.ResourceNotReady(pod['metadata']['name']) - try: - self._record_pod_creation_metric(pod) - except Exception: - LOG.debug("Failed to record metric for pod %s", - pod['metadata']['name']) - - if driver_utils.is_network_policy_enabled(): - crd_pod_selectors = self._drv_sg.create_sg_rules(pod) - if oslo_cfg.CONF.octavia_defaults.enforce_sg_rules: - services = driver_utils.get_services() - self._update_services(services, crd_pod_selectors, - project_id) - - def _mock_cleanup_pod(self, kuryrport_crd): - """Mock Pod that doesn't exist anymore for cleanup purposes""" - pod = { - 'apiVersion': 'v1', - 'kind': 'Pod', - 'metadata': kuryrport_crd['metadata'].copy(), - } - # No need to try to delete the finalizer from the pod later, as - # pod's gone. - del pod['metadata']['finalizers'] - - main_vif = objects.base.VersionedObject.obj_from_primitive( - kuryrport_crd['status']['vifs'][constants.DEFAULT_IFNAME] - ['vif']) - - # Let's try to get the node's address using nodeName saved in CRD - host_ip = None - try: - node = self.k8s.get(f"{constants.K8S_API_BASE}/nodes/" - f"{kuryrport_crd['spec']['podNodeName']}") - for address in node['status']['addresses']: - if address['type'] == constants.K8S_NODE_ADDRESS_INTERNAL: - host_ip = address['address'] - break - except k_exc.K8sClientException: - LOG.warning('Could not find node %s when cleaning up port.', - kuryrport_crd['spec']['podNodeName']) - - if not host_ip: - # We can still try to use OpenStack API if this is nested VIF - port_id = utils.get_parent_port_id(main_vif) - if port_id: - host_ip = utils.get_parent_port_ip(port_id) - - if host_ip: - pod['status'] = {'hostIP': host_ip} - - # If we failed to find host_ip we still allow cleanup to follow, we - # catch all exceptions from release_vif anyway. - return pod - - def on_finalize(self, kuryrport_crd, *args, **kwargs): - name = kuryrport_crd['metadata']['name'] - namespace = kuryrport_crd['metadata']['namespace'] - cleanup = False # If we're doing a cleanup, raise no error. - try: - pod = self.k8s.get(f"{constants.K8S_API_NAMESPACES}" - f"/{namespace}/pods/{name}") - if pod['metadata']['uid'] != kuryrport_crd['spec']['podUid']: - # Seems like this is KuryrPort created for an old Pod instance, - # with the same name. Cleaning it up instead of regular delete. - raise k_exc.K8sResourceNotFound( - 'Pod %s' % pod['metadata']['uid']) - except k_exc.K8sResourceNotFound: - LOG.warning('Pod for KuryrPort %s was forcibly deleted. ' - 'Attempting a cleanup before releasing KuryrPort.', - utils.get_res_unique_name(kuryrport_crd)) - self.k8s.add_event(kuryrport_crd, 'MissingPod', - 'Pod does not exist anymore, attempting to ' - 'cleanup orphaned KuryrPort', 'Warning') - if kuryrport_crd['status']['vifs']: - pod = self._mock_cleanup_pod(kuryrport_crd) - cleanup = True # Make sure we don't raise on release_vif() - else: - # Remove the finalizer, most likely ports never got created. - self.k8s.remove_finalizer(kuryrport_crd, - constants.KURYRPORT_FINALIZER) - return - - if ('deletionTimestamp' not in pod['metadata'] and - not utils.is_pod_completed(pod)): - # NOTE(gryf): Ignore deleting KuryrPort, since most likely it was - # removed manually, while we need vifs for corresponding pod - # object which apparently is still running. - LOG.warning('Manually triggered KuryrPort %s removal. This ' - 'action should be avoided, since KuryrPort CRDs are ' - 'internal to Kuryr.', name) - self.k8s.add_event(pod, 'NoKuryrPort', 'KuryrPort was not found, ' - 'most probably it was manually removed.', - 'Warning') - return - - project_id = self._drv_project.get_project(pod) - try: - crd_pod_selectors = self._drv_sg.delete_sg_rules(pod) - except k_exc.ResourceNotReady: - # NOTE(ltomasbo): If the pod is being deleted before - # kuryr-controller annotated any information about the port - # associated, there is no need for deleting sg rules associated to - # it. So this exception could be safely ignored for the current - # sg drivers. Only the NP driver associates rules to the pods ips, - # and that waits for annotations to start. - # - # NOTE(gryf): perhaps we don't need to handle this case, since - # during CRD creation all the things, including security groups - # rules would be created too. - LOG.debug("Skipping SG rules deletion associated to the pod %s", - pod) - self.k8s.add_event(pod, 'SkipingSGDeletion', 'Skipping SG rules ' - 'deletion') - crd_pod_selectors = [] - - for data in kuryrport_crd['status']['vifs'].values(): - vif = objects.base.VersionedObject.obj_from_primitive(data['vif']) - try: - self._drv_vif_pool.release_vif(pod, vif, project_id) - except Exception: - if not cleanup: - raise - LOG.warning('Error when cleaning up VIF %s, ignoring.', - utils.get_res_unique_name(kuryrport_crd)) - if (driver_utils.is_network_policy_enabled() and crd_pod_selectors and - oslo_cfg.CONF.octavia_defaults.enforce_sg_rules): - services = driver_utils.get_services() - self._update_services(services, crd_pod_selectors, project_id) - - # Remove finalizer out of pod. - self.k8s.remove_finalizer(pod, constants.POD_FINALIZER) - - # Finally, remove finalizer from KuryrPort CRD - self.k8s.remove_finalizer(kuryrport_crd, constants.KURYRPORT_FINALIZER) - - def get_vifs(self, kuryrport_crd): - try: - pod = self.k8s.get(f"{constants.K8S_API_NAMESPACES}" - f"/{kuryrport_crd['metadata']['namespace']}" - f"/pods" - f"/{kuryrport_crd['metadata']['name']}") - if pod['metadata']['uid'] != kuryrport_crd['spec']['podUid']: - # Seems like this is KuryrPort created for an old Pod, deleting - # it anyway. - raise k_exc.K8sResourceNotFound( - 'Pod %s' % pod['metadata']['uid']) - except k_exc.K8sResourceNotFound: - LOG.warning('Pod for KuryrPort %s was forcibly deleted. Deleting' - 'the KuryrPort too to attempt resource cleanup.', - utils.get_res_unique_name(kuryrport_crd)) - self.k8s.add_event(kuryrport_crd, 'MissingPod', - 'Pod does not exist anymore, attempting to ' - 'delete orphaned KuryrPort', 'Warning') - try: - self.k8s.delete(utils.get_res_link(kuryrport_crd)) - except k_exc.K8sResourceNotFound: - pass - except k_exc.K8sClientException: - LOG.exception('Error when trying to delete KuryrPort %s. Will ' - 'retry later.', - utils.get_res_unique_name(kuryrport_crd)) - return False - - project_id = self._drv_project.get_project(pod) - security_groups = self._drv_sg.get_security_groups(pod, project_id) - try: - subnets = self._drv_subnets.get_subnets(pod, project_id) - except (os_exc.ResourceNotFound, k_exc.K8sResourceNotFound): - LOG.warning("Subnet does not exists. If namespace driver is " - "used, probably the namespace for the pod is " - "already deleted. So this pod does not need to " - "get a port as it will be deleted too. If the " - "default subnet driver is used, then you must " - "select an existing subnet to be used by Kuryr.") - self.k8s.add_event(pod, 'NoPodSubnetFound', 'Pod subnet not ' - 'found. Namespace for this pod was probably ' - 'deleted. For default subnet driver it must ' - 'be existing subnet configured for Kuryr', - 'Warning') - return False - - # Request the default interface of pod - try: - main_vif = self._drv_vif_pool.request_vif(pod, project_id, - subnets, - security_groups) - except os_exc.ResourceNotFound: - # NOTE(gryf): It might happen, that between getting security - # groups above and requesting VIF, network policy is deleted, - # hence we will get 404 from OpenStackSDK. Let's retry, to refresh - # information regarding SG. - LOG.warning("SG not found during VIF requesting. Retrying.") - raise k_exc.ResourceNotReady(pod['metadata']['name']) - - pod_name = pod['metadata']['name'] - if not main_vif: - LOG.warning("Ignoring event due to pod %s not being " - "scheduled yet.", pod_name) - self.k8s.add_event(pod, 'KuryrIgnoringPodEvent', - f'Ignoring event: Pod not scheduled ' - f'{pod_name}') - return False - - vifs = {constants.DEFAULT_IFNAME: {'default': True, 'vif': main_vif}} - - # Request the additional interfaces from multiple drivers - index = 0 - for driver in self._drv_multi_vif: - additional_vifs = driver.request_additional_vifs(pod, project_id, - security_groups) - for index, vif in enumerate(additional_vifs, start=index+1): - ifname = (oslo_cfg.CONF.kubernetes.additional_ifname_prefix + - str(index)) - vifs[ifname] = {'default': False, 'vif': vif} - - try: - self._update_kuryrport_crd(kuryrport_crd, vifs) - self.k8s.add_event(pod, 'KuryrPortUpdatedWithVIFs', - f'KuryrPort CRD: {pod_name} updated with VIFs') - except k_exc.K8sClientException as ex: - LOG.exception("Kubernetes Client Exception creating " - "KuryrPort CRD: %s", ex) - for ifname, data in vifs.items(): - self._drv_vif_pool.release_vif(pod, data['vif'], project_id) - self.k8s.add_event(pod, 'ExceptionOnKPUpdate', f'There was k8s ' - f'client exception on updating corresponding ' - f'KuryrPort CRD: {ex}', 'Warning') - return True - - def _update_kuryrport_crd(self, kuryrport_crd, vifs): - LOG.debug('Updating CRD %s', kuryrport_crd["metadata"]["name"]) - vif_dict = {} - for ifname, data in vifs.items(): - data['vif'].obj_reset_changes(recursive=True) - vif_dict[ifname] = {'default': data['default'], - 'vif': data['vif'].obj_to_primitive()} - - self.k8s.patch_crd('status', utils.get_res_link(kuryrport_crd), - {'vifs': vif_dict}) - - def _update_services(self, services, crd_pod_selectors, project_id): - for service in services.get('items'): - if not driver_utils.service_matches_affected_pods( - service, crd_pod_selectors): - continue - sgs = self._drv_svc_sg.get_security_groups(service, - project_id) - self._drv_lbaas.update_lbaas_sg(service, sgs) - - def _record_pod_creation_metric(self, pod): - exporter = exp.ControllerPrometheusExporter.get_instance() - for condition in pod['status'].get('conditions'): - if condition['type'] == 'PodScheduled' and condition['status']: - f_str = "%Y-%m-%dT%H:%M:%SZ" - time_obj = datetime.datetime.strptime( - condition['lastTransitionTime'], f_str) - pod_creation_time = datetime.datetime.now() - time_obj - pod_creation_sec = (pod_creation_time).total_seconds() - exporter.record_pod_creation_metric(pod_creation_sec) - - def _get_pod(self, kuryrport_crd): - try: - name = kuryrport_crd['metadata']['name'] - namespace = kuryrport_crd['metadata']['namespace'] - return self.k8s.get(f"{constants.K8S_API_NAMESPACES}" - f"/{namespace}/pods/{name}") - except k_exc.K8sResourceNotFound as ex: - self.k8s.add_event(kuryrport_crd, 'KuryrFailedGettingPod' - f'Failed to get corresponding pod: {ex}', - 'Warning') - LOG.exception("Failed to get pod: %s", ex) - raise diff --git a/kuryr_kubernetes/controller/handlers/lbaas.py b/kuryr_kubernetes/controller/handlers/lbaas.py deleted file mode 100644 index e0a4e5795..000000000 --- a/kuryr_kubernetes/controller/handlers/lbaas.py +++ /dev/null @@ -1,447 +0,0 @@ -# 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 netaddr - -from kuryr.lib._i18n import _ -from oslo_log import log as logging - -from kuryr_kubernetes import clients -from kuryr_kubernetes import config -from kuryr_kubernetes import constants as k_const -from kuryr_kubernetes.controller.drivers import base as drv_base -from kuryr_kubernetes.controller.drivers import utils as driver_utils -from kuryr_kubernetes import exceptions as k_exc -from kuryr_kubernetes.handlers import k8s_base -from kuryr_kubernetes import utils - -LOG = logging.getLogger(__name__) -CONF = config.CONF - -SUPPORTED_SERVICE_TYPES = ('ClusterIP', 'LoadBalancer') - - -class ServiceHandler(k8s_base.ResourceEventHandler): - """ServiceHandler handles K8s Service events. - - ServiceHandler handles K8s Service events and updates related Endpoints - with LBaaSServiceSpec when necessary. - """ - - OBJECT_KIND = k_const.K8S_OBJ_SERVICE - OBJECT_WATCH_PATH = "%s/%s" % (k_const.K8S_API_BASE, "services") - - def __init__(self): - super(ServiceHandler, self).__init__() - self._drv_project = drv_base.ServiceProjectDriver.get_instance() - self._drv_subnets = drv_base.ServiceSubnetsDriver.get_instance() - self._drv_sg = drv_base.ServiceSecurityGroupsDriver.get_instance() - self._drv_lbaas = drv_base.LBaaSDriver.get_instance() - self.k8s = clients.get_kubernetes_client() - - self._lb_provider = None - if self._drv_lbaas.providers_supported(): - self._lb_provider = 'amphora' - config_provider = CONF.kubernetes.endpoints_driver_octavia_provider - if config_provider != 'default': - self._lb_provider = config_provider - - def _bump_network_policies(self, svc): - if driver_utils.is_network_policy_enabled(): - driver_utils.bump_networkpolicies(svc['metadata']['namespace']) - - def on_present(self, service, *args, **kwargs): - reason = self._should_ignore(service) - if reason: - reason %= utils.get_res_unique_name(service) - LOG.debug(reason) - self.k8s.add_event(service, 'KuryrServiceSkipped', reason) - return - - loadbalancer_crd = self.k8s.get_loadbalancer_crd(service) - try: - if not self._patch_service_finalizer(service): - return - except k_exc.K8sClientException as ex: - msg = (f'K8s API error when adding finalizer to Service ' - f'{utils.get_res_unique_name(service)}') - LOG.exception(msg) - self.k8s.add_event(service, 'KuryrAddServiceFinalizerError', - f'{msg}: {ex}', 'Warning') - raise - - if loadbalancer_crd is None: - try: - # Bump all the NPs in the namespace to force SG rules - # recalculation. - self._bump_network_policies(service) - self.create_crd_spec(service) - except k_exc.K8sNamespaceTerminating: - LOG.debug('Namespace %s is being terminated, ignoring ' - 'Service %s in that namespace.', - service['metadata']['namespace'], - service['metadata']['name']) - return - elif self._has_lbaas_spec_changes(service, loadbalancer_crd): - self._update_crd_spec(loadbalancer_crd, service) - - def _is_supported_type(self, service): - spec = service['spec'] - return spec.get('type') in SUPPORTED_SERVICE_TYPES - - def _has_spec_annotation(self, service): - return (k_const.K8S_ANNOTATION_LBAAS_SPEC in - service['metadata'].get('annotations', {})) - - def _get_service_ip(self, service): - if self._is_supported_type(service): - return self._strip_funny_ip(service['spec'].get('clusterIP')) - return None - - def _should_ignore(self, service): - if not self._has_clusterip(service): - return 'Skipping headless Service %s.' - if not self._is_supported_type(service): - return 'Skipping service %s of unsupported type.' - if self._has_spec_annotation(service): - return ('Skipping annotated service %s, waiting for it to be ' - 'converted to KuryrLoadBalancer object and annotation ' - 'removed.') - if utils.is_kubernetes_default_resource(service): - # Avoid to handle default Kubernetes service as requires https. - return 'Skipping default service %s.' - return None - - def _patch_service_finalizer(self, service): - return self.k8s.add_finalizer(service, k_const.SERVICE_FINALIZER) - - def on_finalize(self, service, *args, **kwargs): - klb_crd_path = utils.get_klb_crd_path(service) - # Bump all the NPs in the namespace to force SG rules - # recalculation. - self._bump_network_policies(service) - try: - self.k8s.delete(klb_crd_path) - except k_exc.K8sResourceNotFound: - self.k8s.remove_finalizer(service, k_const.SERVICE_FINALIZER) - - def _has_clusterip(self, service): - # ignore headless service, clusterIP is None - return service['spec'].get('clusterIP') != 'None' - - def _get_subnet_id(self, service, project_id, ip): - subnets_mapping = self._drv_subnets.get_subnets(service, project_id) - subnet_ids = { - subnet_id - for subnet_id, network in subnets_mapping.items() - for subnet in network.subnets.objects - if ip in subnet.cidr} - - if len(subnet_ids) != 1: - raise k_exc.IntegrityError(_( - "Found %(num)s subnets for service %(link)s IP %(ip)s") % { - 'link': utils.get_res_link(service), - 'ip': ip, - 'num': len(subnet_ids)}) - - return subnet_ids.pop() - - def create_crd_spec(self, service): - svc_name = service['metadata']['name'] - svc_namespace = service['metadata']['namespace'] - kubernetes = clients.get_kubernetes_client() - spec = self._build_kuryrloadbalancer_spec(service) - - owner_reference = { - 'apiVersion': service['apiVersion'], - 'kind': service['kind'], - 'name': service['metadata']['name'], - 'uid': service['metadata']['uid'], - } - - loadbalancer_crd = { - 'apiVersion': 'openstack.org/v1', - 'kind': 'KuryrLoadBalancer', - 'metadata': { - 'name': svc_name, - 'finalizers': [k_const.KURYRLB_FINALIZER], - 'ownerReferences': [owner_reference], - }, - 'spec': spec, - 'status': {}, - } - - try: - kubernetes.post('{}/{}/kuryrloadbalancers'.format( - k_const.K8S_API_CRD_NAMESPACES, svc_namespace), - loadbalancer_crd) - except k_exc.K8sConflict: - raise k_exc.ResourceNotReady(svc_name) - except k_exc.K8sNamespaceTerminating: - raise - except k_exc.K8sClientException as e: - LOG.exception("Exception when creating KuryrLoadBalancer CRD.") - self.k8s.add_event( - service, 'CreateKLBFailed', - 'Error when creating KuryrLoadBalancer object: %s' % e, - 'Warning') - raise - - def _update_crd_spec(self, loadbalancer_crd, service): - svc_name = service['metadata']['name'] - kubernetes = clients.get_kubernetes_client() - spec = self._build_kuryrloadbalancer_spec(service) - LOG.debug('Patching KuryrLoadBalancer CRD %s', loadbalancer_crd) - try: - kubernetes.patch_crd('spec', utils.get_res_link(loadbalancer_crd), - spec) - except k_exc.K8sResourceNotFound: - LOG.debug('KuryrLoadBalancer CRD not found %s', loadbalancer_crd) - except k_exc.K8sConflict: - raise k_exc.ResourceNotReady(svc_name) - except k_exc.K8sClientException as e: - LOG.exception('Error updating KuryrNetwork CRD %s', - loadbalancer_crd) - self.k8s.add_event( - service, 'UpdateKLBFailed', - 'Error when updating KuryrLoadBalancer object: %s' % e, - 'Warning') - raise - - def _get_data_timeout_annotation(self, service): - default_timeout_cli = CONF.octavia_defaults.timeout_client_data - default_timeout_mem = CONF.octavia_defaults.timeout_member_data - try: - annotations = service['metadata']['annotations'] - except KeyError: - return default_timeout_cli, default_timeout_mem - try: - timeout_cli = annotations[k_const.K8S_ANNOTATION_CLIENT_TIMEOUT] - data_timeout_cli = int(timeout_cli) - except KeyError: - data_timeout_cli = default_timeout_cli - try: - timeout_mem = annotations[k_const.K8S_ANNOTATION_MEMBER_TIMEOUT] - data_timeout_mem = int(timeout_mem) - except KeyError: - data_timeout_mem = default_timeout_mem - return data_timeout_cli, data_timeout_mem - - def _build_kuryrloadbalancer_spec(self, service): - svc_ip = self._get_service_ip(service) - spec_lb_ip = service['spec'].get('loadBalancerIP') - ports = service['spec'].get('ports') - for port in ports: - if type(port['targetPort']) == int: - port['targetPort'] = str(port['targetPort']) - project_id = self._drv_project.get_project(service) - sg_ids = self._drv_sg.get_security_groups(service, project_id) - subnet_id = self._get_subnet_id(service, project_id, svc_ip) - spec_type = service['spec'].get('type') - spec = { - 'ip': svc_ip, - 'ports': ports, - 'project_id': project_id, - 'security_groups_ids': sg_ids, - 'subnet_id': subnet_id, - 'type': spec_type, - } - - if self._lb_provider: - spec['provider'] = self._lb_provider - - if spec_lb_ip is not None: - spec['lb_ip'] = self._strip_funny_ip(spec_lb_ip) - timeout_cli, timeout_mem = self._get_data_timeout_annotation(service) - spec['timeout_client_data'] = timeout_cli - spec['timeout_member_data'] = timeout_mem - return spec - - def _has_lbaas_spec_changes(self, service, loadbalancer_crd): - return (self._has_ip_changes(service, loadbalancer_crd) or - utils.has_port_changes(service, loadbalancer_crd) or - self._has_timeout_changes(service, loadbalancer_crd) or - self._has_provider_changes(loadbalancer_crd)) - - def _has_provider_changes(self, loadbalancer_crd): - return (self._lb_provider and - loadbalancer_crd['spec'].get('provider') != self._lb_provider) - - def _has_ip_changes(self, service, loadbalancer_crd): - link = utils.get_res_link(service) - svc_ip = self._get_service_ip(service) - - if loadbalancer_crd['spec'].get('ip') is None: - if svc_ip is None: - return False - return True - - elif str(loadbalancer_crd['spec'].get('ip')) != svc_ip: - LOG.debug("LBaaS spec IP %(spec_ip)s != %(svc_ip)s for %(link)s" - % {'spec_ip': loadbalancer_crd['spec']['ip'], - 'svc_ip': svc_ip, - 'link': link}) - return True - - return False - - def _has_timeout_changes(self, service, loadbalancer_crd): - link = utils.get_res_link(service) - cli_timeout, mem_timeout = self._get_data_timeout_annotation(service) - - for spec_value, current_value in [(loadbalancer_crd['spec'].get( - 'timeout_client_data'), cli_timeout), (loadbalancer_crd[ - 'spec'].get('timeout_member_data'), mem_timeout)]: - if not spec_value and not current_value: - continue - elif spec_value != current_value: - LOG.debug("LBaaS spec listener timeout {} != {} for {}".format( - spec_value, current_value, link)) - return True - - return False - - def _strip_funny_ip(self, ip): - return str(netaddr.IPAddress(ip, flags=netaddr.core.ZEROFILL)) - - -class EndpointsHandler(k8s_base.ResourceEventHandler): - """EndpointsHandler handles K8s Endpoints events. - - EndpointsHandler handles K8s Endpoints events and tracks changes in - LBaaSServiceSpec to update Neutron LBaaS accordingly and to reflect its' - actual state in LBaaSState. - """ - - OBJECT_KIND = k_const.K8S_OBJ_ENDPOINTS - OBJECT_WATCH_PATH = "%s/%s" % (k_const.K8S_API_BASE, "endpoints") - - def __init__(self): - super(EndpointsHandler, self).__init__() - self.k8s = clients.get_kubernetes_client() - - def on_present(self, endpoints, *args, **kwargs): - ep_name = endpoints['metadata']['name'] - ep_namespace = endpoints['metadata']['namespace'] - - loadbalancer_crd = self.k8s.get_loadbalancer_crd(endpoints) - - if (not (self._has_pods(endpoints) or (loadbalancer_crd and - loadbalancer_crd.get('status'))) - or k_const.K8S_ANNOTATION_HEADLESS_SERVICE - in endpoints['metadata'].get('labels', []) or - utils.is_kubernetes_default_resource(endpoints)): - LOG.debug("Ignoring Kubernetes endpoints %s", - endpoints['metadata']['name']) - return - - if loadbalancer_crd is None: - raise k_exc.KuryrLoadBalancerNotCreated(endpoints) - else: - try: - self._update_crd_spec(loadbalancer_crd, endpoints) - except k_exc.K8sNamespaceTerminating: - LOG.debug('Namespace %s is being terminated, ignoring ' - 'Endpoints %s in that namespace.', - ep_namespace, ep_name) - - def on_deleted(self, endpoints, *args, **kwargs): - self._remove_endpoints(endpoints) - - def _has_pods(self, endpoints): - ep_subsets = endpoints.get('subsets', []) - if not ep_subsets: - return False - return any(True - for subset in ep_subsets - if subset.get('addresses', [])) - - def _convert_subsets_to_endpointslice(self, endpoints_obj): - endpointslices = [] - endpoints = [] - subsets = endpoints_obj.get('subsets', []) - for subset in subsets: - addresses = subset.get('addresses', []) - ports = subset.get('ports', []) - for address in addresses: - ip = address.get('ip') - targetRef = address.get('targetRef') - endpoint = { - 'addresses': [ip], - 'conditions': { - 'ready': True - }, - } - if targetRef: - endpoint['targetRef'] = targetRef - endpoints.append(endpoint) - endpointslices.append({ - 'endpoints': endpoints, - 'ports': ports, - }) - - return endpointslices - - def _add_event(self, endpoints, reason, message, type_=None): - """_add_event adds an event for the corresponding Service.""" - try: - service = self.k8s.get(utils.get_service_link(endpoints)) - except k_exc.K8sClientException: - LOG.debug('Error when fetching Service to add an event %s, ' - 'ignoring', utils.get_res_unique_name(endpoints)) - return - kwargs = {'type_': type_} if type_ else {} - self.k8s.add_event(service, reason, message, **kwargs) - - def _update_crd_spec(self, loadbalancer_crd, endpoints): - # TODO(maysams): Remove the conversion once we start handling - # EndpointSlices. - epslices = self._convert_subsets_to_endpointslice(endpoints) - try: - self.k8s.patch_crd('spec', utils.get_res_link(loadbalancer_crd), - {'endpointSlices': epslices}) - except k_exc.K8sResourceNotFound: - LOG.debug('KuryrLoadbalancer CRD not found %s', loadbalancer_crd) - except k_exc.K8sConflict: - raise k_exc.ResourceNotReady(loadbalancer_crd) - except k_exc.K8sClientException as e: - LOG.exception('Error updating KuryrLoadbalancer CRD %s', - loadbalancer_crd) - self._add_event( - endpoints, 'UpdateKLBFailed', - 'Error when updating KuryrLoadBalancer object: %s' % e, - 'Warning') - raise - - return True - - def _remove_endpoints(self, endpoints): - lb_name = utils.get_res_unique_name(endpoints) - try: - self.k8s.patch_crd('spec', utils.get_klb_crd_path(endpoints), - 'endpointSlices', action='remove') - except k_exc.K8sResourceNotFound: - LOG.debug('KuryrLoadBalancer CRD not found %s', lb_name) - except k_exc.K8sUnprocessableEntity: - # This happens when endpointSlices doesn't exist on the KLB, - # safe to ignore, the resources is in the state we want already. - pass - except k_exc.K8sClientException as e: - LOG.exception('Error updating KuryrLoadBalancer CRD %s', lb_name) - self._add_event( - endpoints, 'UpdateKLBFailed', - 'Error when updating KuryrLoadBalancer object: %s' % e, - 'Warning') - raise diff --git a/kuryr_kubernetes/controller/handlers/loadbalancer.py b/kuryr_kubernetes/controller/handlers/loadbalancer.py deleted file mode 100755 index 04dd00ed8..000000000 --- a/kuryr_kubernetes/controller/handlers/loadbalancer.py +++ /dev/null @@ -1,916 +0,0 @@ -# Copyright (c) 2020 Red Hat, 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 time - -from oslo_log import log as logging - -from kuryr_kubernetes import clients -from kuryr_kubernetes import config -from kuryr_kubernetes import constants as k_const -from kuryr_kubernetes.controller.drivers import base as drv_base -from kuryr_kubernetes.controller.drivers import utils as driver_utils -from kuryr_kubernetes import exceptions as k_exc -from kuryr_kubernetes.handlers import k8s_base -from kuryr_kubernetes import utils - -LOG = logging.getLogger(__name__) -CONF = config.CONF - -OCTAVIA_DEFAULT_PROVIDERS = ['octavia', 'amphora'] -CRD_RECONCILIATION_FREQUENCY = 600 # seconds - - -class KuryrLoadBalancerHandler(k8s_base.ResourceEventHandler): - """LoadBalancerStatusHandler handles K8s Endpoints events. - - LBStatusHandler handles K8s Endpoints events and tracks changes in - LBaaSServiceSpec to update Neutron LBaaS accordingly and to reflect its' - actual state in LBaaSState. - """ - - OBJECT_KIND = k_const.K8S_OBJ_KURYRLOADBALANCER - OBJECT_WATCH_PATH = k_const.K8S_API_CRD_KURYRLOADBALANCERS - - def __init__(self): - super(KuryrLoadBalancerHandler, self).__init__() - self._drv_lbaas = drv_base.LBaaSDriver.get_instance() - self._drv_pod_project = drv_base.PodProjectDriver.get_instance() - self._drv_pod_subnets = drv_base.PodSubnetsDriver.get_instance() - self._drv_service_pub_ip = drv_base.ServicePubIpDriver.get_instance() - self._drv_svc_project = drv_base.ServiceProjectDriver.get_instance() - self._drv_sg = drv_base.ServiceSecurityGroupsDriver.get_instance() - self._drv_nodes_subnets = drv_base.NodesSubnetsDriver.get_instance() - self.k8s = clients.get_kubernetes_client() - - def _get_nodes_subnets(self): - return utils.get_subnets_id_cidrs( - self._drv_nodes_subnets.get_nodes_subnets()) - - def _add_event(self, klb, reason, message, type_=None): - """_add_event adds an event for the corresponding Service.""" - klb_meta = klb['metadata'] - for ref in klb_meta.get('ownerReferences', []): - # "mock" a Service based on ownerReference to it. - if ref['kind'] == 'Service' and ref['name'] == klb_meta['name']: - service = { - 'apiVersion': ref['apiVersion'], - 'kind': ref['kind'], - 'metadata': { - 'name': ref['name'], - 'uid': ref['uid'], - 'namespace': klb_meta['namespace'], # ref shares ns - }, - } - break - else: - # No reference, just fetch the service from the API. - try: - service = self.k8s.get( - f"{k_const.K8S_API_NAMESPACES}/{klb_meta['namespace']}" - f"/services/{klb_meta['name']}") - except k_exc.K8sClientException: - LOG.debug('Error when fetching Service to add an event %s, ' - 'ignoring', utils.get_res_unique_name(klb)) - return - kwargs = {'type_': type_} if type_ else {} - self.k8s.add_event(service, reason, message, **kwargs) - - def on_present(self, loadbalancer_crd, *args, **kwargs): - if loadbalancer_crd.get('status', None) is None: - try: - self.k8s.patch_crd('status', - utils.get_res_link(loadbalancer_crd), {}) - except k_exc.K8sResourceNotFound: - LOG.debug('KuryrLoadbalancer CRD not found %s', - utils.get_res_unique_name(loadbalancer_crd)) - return - - reason = self._should_ignore(loadbalancer_crd) - if reason: - reason %= utils.get_res_unique_name(loadbalancer_crd) - LOG.debug(reason) - self._add_event(loadbalancer_crd, 'KuryrServiceSkipped', reason) - return - - crd_lb = loadbalancer_crd['status'].get('loadbalancer') - if crd_lb: - lb_provider = crd_lb.get('provider') - spec_lb_provider = loadbalancer_crd['spec'].get('provider') - # amphora to ovn upgrade - if not lb_provider or lb_provider in OCTAVIA_DEFAULT_PROVIDERS: - if (spec_lb_provider and - spec_lb_provider not in OCTAVIA_DEFAULT_PROVIDERS): - self._add_event(loadbalancer_crd, 'KuryrUpdateProvider', - 'Deleting Amphora load balancer to ' - 'recreate it with OVN provider') - self._ensure_release_lbaas(loadbalancer_crd) - - # ovn to amphora downgrade - elif lb_provider and lb_provider not in OCTAVIA_DEFAULT_PROVIDERS: - if (not spec_lb_provider or - spec_lb_provider in OCTAVIA_DEFAULT_PROVIDERS): - self._add_event(loadbalancer_crd, 'KuryrUpdateProvider', - 'Deleting OVN load balancer to ' - 'recreate it with Amphora provider') - self._ensure_release_lbaas(loadbalancer_crd) - - if not crd_lb: - self._add_event(loadbalancer_crd, 'KuryrEnsureLB', - 'Provisioning a load balancer') - try: - changed = self._sync_lbaas_members(loadbalancer_crd) - except Exception as e: - self._add_event( - loadbalancer_crd, 'KuryrEnsureLBError', - f'Error when provisioning load balancer: {e}', 'Warning') - raise - - if changed: - self._add_event(loadbalancer_crd, 'KuryrEnsuredLB', - 'Load balancer provisioned') - # Note(yboaron) For LoadBalancer services, we should allocate FIP, - # associate it to LB VIP and update K8S service status - lb_ip = loadbalancer_crd['spec'].get('lb_ip') - pub_info = loadbalancer_crd['status'].get( - 'service_pub_ip_info') - if pub_info is None and loadbalancer_crd['spec'].get('type'): - service_pub_ip_info = ( - self._drv_service_pub_ip.acquire_service_pub_ip_info( - loadbalancer_crd['spec']['type'], - lb_ip, - loadbalancer_crd['spec']['project_id'], - loadbalancer_crd['status']['loadbalancer'][ - 'port_id'])) - if service_pub_ip_info: - self._add_event( - loadbalancer_crd, 'KuryrEnsureFIP', - 'Associating floating IP to the load balancer') - self._drv_service_pub_ip.associate_pub_ip( - service_pub_ip_info, loadbalancer_crd['status'][ - 'loadbalancer']['port_id']) - loadbalancer_crd['status'][ - 'service_pub_ip_info'] = service_pub_ip_info - self._update_lb_status(loadbalancer_crd) - self._patch_status(loadbalancer_crd) - - def reconcile(self): - loadbalancer_crds = [] - try: - loadbalancer_crds = driver_utils.get_kuryrloadbalancer_crds() - except k_exc.K8sClientException: - LOG.warning("Error retriving KuryrLoadBalanders CRDs") - try: - self._trigger_reconciliation(loadbalancer_crds) - except Exception: - LOG.exception('Error while running loadbalancers reconciliation.') - - def _trigger_reconciliation(self, loadbalancer_crds): - LOG.debug("Reconciling the KuryrLoadBalancer CRDs") - lbaas = clients.get_loadbalancer_client() - resources_fn = {'loadbalancer': lbaas.load_balancers, - 'listener': lbaas.listeners, - 'pool': lbaas.pools, 'member': lbaas.members} - resources = {'loadbalancer': [], 'listener': [], 'pool': [], - 'member': []} - - for klb in loadbalancer_crds: - if klb['metadata'].get('deletionTimestamp'): - continue - - selflink = utils.get_res_link(klb) - lb_id = klb.get('status', {}).get('loadbalancer', {}).get('id') - - if lb_id: - resources['loadbalancer'].append({'id': lb_id, - 'selflink': selflink, - 'klb': klb}) - - for lbl in klb.get('status', {}).get('listeners', []): - resources['listener'].append({'id': lbl['id'], - 'selflink': selflink, - 'lklb': klb}) - for pl in klb.get('status', {}).get('pools', []): - resources['pool'].append({'id': pl['id'], - 'selflink': selflink, - 'pklb': klb}) - for lbm in klb.get('status', {}).get('members', []): - resources['member'].append({'id': lbm['id'], - 'selflink': selflink, - 'mklb': klb, - 'pool_id': lbm['pool_id']}) - - resources_already_triggered = [] - # let's reconcile load balancers first, listeners, pools - # and then members - resource_types = ('loadbalancer', 'listener', 'pool', 'member') - for resource_type in resource_types: - filters = {} - self._drv_lbaas.add_tags(resource_type, filters) - os_list = resources_fn[resource_type] - if resource_type == 'member': - pool_ids = [cr_member['pool_id'] for cr_member in - resources[resource_type]] - pool_ids = list(set(pool_ids)) - os_resources_id = [] - for pl_id in pool_ids: - os_resources = os_list(pl_id, **filters) - os_resources_id.extend([rsrc['id'] for rsrc in - os_resources]) - else: - os_resources = os_list(**filters) - os_resources_id = [rsrc['id'] for rsrc in os_resources] - for data in resources[resource_type]: - if data['selflink'] in resources_already_triggered: - continue - if data['id'] not in os_resources_id: - resources_already_triggered.append(data['selflink']) - LOG.debug("Reconciling KuryrLoadBalancer CRD: %s", - data['selflink']) - self._reconcile_lb(data) - - def _reconcile_lb(self, data): - kubernetes = clients.get_kubernetes_client() - try: - if data.get('klb'): - self._add_event(data['klb'], 'LoadBalancerMissing', - 'Load balancer for the Service does not ' - 'exist anymore. Recreating it.', 'Warning') - if data.get('lklb'): - self._add_event(data['lklb'], 'LoadBalancerListenerMissing', - 'Load Balancer listener does not exist ' - 'anymore. Recreating it.', 'Warning') - if data.get('pklb'): - self._add_event(data['pklb'], 'LoadBalancerPoolMissing', - 'Load Balancer pool does not exist anymore. ' - 'Recreating it.', 'Warning') - if data.get('mklb'): - self._add_event(data['mklb'], 'LoadBalancerMemberMissing', - 'Load Balancer member does not exist anymore. ' - 'Recreating it.', 'Warning') - - kubernetes.patch_crd('status', data['selflink'], {}) - except k_exc.K8sResourceNotFound: - LOG.debug('Unable to reconcile the KuryLoadBalancer CRD %s', - data['selflink']) - except k_exc.K8sClientException: - LOG.warning('Unable to patch the KuryLoadBalancer CRD %s', - data['selflink']) - - def _should_ignore(self, loadbalancer_crd): - if not(self._has_endpoints(loadbalancer_crd) or - loadbalancer_crd.get('status')): - return 'Skipping Service %s without Endpoints' - elif not loadbalancer_crd['spec'].get('ip'): - return 'Skipping Service %s without IP set yet' - return False - - def _has_endpoints(self, loadbalancer_crd): - ep_slices = loadbalancer_crd['spec'].get('endpointSlices', []) - if not ep_slices: - return False - return True - - def on_finalize(self, loadbalancer_crd, *args, **kwargs): - LOG.debug("Deleting the loadbalancer CRD") - - if loadbalancer_crd['status'] != {}: - self._add_event(loadbalancer_crd, 'KuryrReleaseLB', - 'Releasing the load balancer') - try: - # NOTE(ivc): deleting pool deletes its members - self._drv_lbaas.release_loadbalancer( - loadbalancer_crd['status'].get('loadbalancer')) - except Exception as e: - # FIXME(dulek): It seems like if loadbalancer will be stuck in - # PENDING_DELETE we'll just silently time out - # waiting for it to be deleted. Is that expected? - self._add_event( - loadbalancer_crd, 'KuryrReleaseLBError', - f'Error when releasing load balancer: {e}', 'Warning') - raise - - try: - pub_info = loadbalancer_crd['status']['service_pub_ip_info'] - except KeyError: - pub_info = None - - if pub_info: - self._add_event( - loadbalancer_crd, 'KuryrReleaseFIP', - 'Dissociating floating IP from the load balancer') - self._drv_service_pub_ip.release_pub_ip( - loadbalancer_crd['status']['service_pub_ip_info']) - - LOG.debug('Removing finalizer from KuryrLoadBalancer CRD %s', - loadbalancer_crd) - try: - self.k8s.remove_finalizer(loadbalancer_crd, - k_const.KURYRLB_FINALIZER) - except k_exc.K8sClientException as e: - msg = (f'K8s API error when removing finalizer from ' - f'KuryrLoadBalancer of Service ' - f'{utils.get_res_unique_name(loadbalancer_crd)}') - LOG.exception(msg) - self._add_event(loadbalancer_crd, 'KuryrRemoveLBFinalizerError', - f'{msg}: {e}', 'Warning') - raise - - namespace = loadbalancer_crd['metadata']['namespace'] - name = loadbalancer_crd['metadata']['name'] - try: - service = self.k8s.get(f"{k_const.K8S_API_NAMESPACES}/{namespace}" - f"/services/{name}") - except k_exc.K8sResourceNotFound: - LOG.warning('Service %s not found. This is unexpected.', - utils.get_res_unique_name(loadbalancer_crd)) - return - - LOG.debug('Removing finalizer from Service %s', - utils.get_res_unique_name(service)) - try: - self.k8s.remove_finalizer(service, k_const.SERVICE_FINALIZER) - except k_exc.K8sClientException as e: - msg = (f'K8s API error when removing finalizer from Service ' - f'{utils.get_res_unique_name(service)}') - LOG.exception(msg) - self._add_event( - loadbalancer_crd, 'KuryrRemoveServiceFinalizerError', - f'{msg}: {e}', 'Warning') - raise - - def _patch_status(self, loadbalancer_crd): - try: - self.k8s.patch_crd('status', utils.get_res_link(loadbalancer_crd), - loadbalancer_crd['status']) - except k_exc.K8sResourceNotFound: - LOG.debug('KuryrLoadBalancer CRD not found %s', loadbalancer_crd) - return False - except k_exc.K8sUnprocessableEntity: - LOG.warning('KuryrLoadBalancer %s modified, retrying later.', - utils.get_res_unique_name(loadbalancer_crd)) - return False - except k_exc.K8sClientException as e: - msg = (f'K8s API error when updating status of ' - f'{utils.get_res_unique_name(loadbalancer_crd)} Service ' - f'load balancer') - LOG.exception(msg) - self._add_event(loadbalancer_crd, 'KuryrUpdateLBStatusError', - f'{msg}: {e}', 'Warning') - raise - return True - - def _sync_lbaas_members(self, loadbalancer_crd): - changed = False - - if self._remove_unused_members(loadbalancer_crd): - changed = True - - if self._sync_lbaas_pools(loadbalancer_crd): - changed = True - - if (self._has_endpoints(loadbalancer_crd) and - self._add_new_members(loadbalancer_crd)): - changed = True - - return changed - - def _sync_lbaas_sgs(self, klb_crd): - lb = klb_crd['status'].get('loadbalancer') - svc_name = klb_crd['metadata']['name'] - svc_namespace = klb_crd['metadata']['namespace'] - try: - service = self.k8s.get( - f'{k_const.K8S_API_NAMESPACES}/{svc_namespace}/' - f'services/{svc_name}') - except k_exc.K8sResourceNotFound: - LOG.debug('Service %s not found.', svc_name) - return - except k_exc.K8sClientException: - LOG.exception('Error retrieving Service %s.', svc_name) - raise - - project_id = self._drv_svc_project.get_project(service) - lb_sgs = self._drv_sg.get_security_groups(service, project_id) - lb['security_groups'] = lb_sgs - - try: - self.k8s.patch_crd('status/loadbalancer', - utils.get_res_link(klb_crd), - {'security_groups': lb_sgs}) - except k_exc.K8sResourceNotFound: - LOG.debug('KuryrLoadBalancer %s not found', svc_name) - return None - except k_exc.K8sUnprocessableEntity: - LOG.debug('KuryrLoadBalancer entity not processable ' - 'due to missing loadbalancer field.') - return None - except k_exc.K8sClientException as e: - msg = (f'K8s API error when updating SGs status of ' - f'{utils.get_res_unique_name(klb_crd)} Service load ' - f'balancer') - LOG.exception(msg) - self._add_event(klb_crd, 'KuryrUpdateLBStatusError', - f'{msg}: {e}', 'Warning') - raise - return klb_crd - - def _add_new_members(self, loadbalancer_crd): - changed = False - - if loadbalancer_crd['status'].get('loadbalancer'): - loadbalancer_crd = self._sync_lbaas_sgs(loadbalancer_crd) - if not loadbalancer_crd: - return changed - - lsnr_by_id = {l['id']: l for l in loadbalancer_crd['status'].get( - 'listeners', [])} - pool_by_lsnr_port = {(lsnr_by_id[p['listener_id']]['protocol'], - lsnr_by_id[p['listener_id']]['port']): p - for p in loadbalancer_crd['status'].get( - 'pools', [])} - - # NOTE(yboaron): Since LBaaSv2 doesn't support UDP load balancing, - # the LBaaS driver will return 'None' in case of UDP port - # listener creation. - # we should consider the case in which - # 'pool_by_lsnr_port[p.protocol, p.port]' is missing - pool_by_tgt_name = {} - for p in loadbalancer_crd['spec'].get('ports', []): - try: - pool_by_tgt_name[p['name']] = pool_by_lsnr_port[p['protocol'], - p['port']] - except KeyError: - continue - - current_targets = [(str(m['ip']), m['port'], m['pool_id']) - for m in loadbalancer_crd['status'].get( - 'members', [])] - - for ep_slice in loadbalancer_crd['spec']['endpointSlices']: - ep_slices_ports = ep_slice.get('ports', []) - for endpoint in ep_slice.get('endpoints', []): - try: - target_ip = endpoint['addresses'][0] - target_ref = endpoint.get('targetRef') - target_namespace = None - if target_ref: - target_namespace = target_ref['namespace'] - # Avoid to point to a Pod on hostNetwork - # that isn't the one to be added as Member. - if not target_ref and utils.get_subnet_by_ip( - self._get_nodes_subnets(), - target_ip): - target_pod = {} - else: - target_pod = utils.get_pod_by_ip( - target_ip, target_namespace) - except KeyError: - continue - if not pool_by_tgt_name: - continue - for ep_slice_port in ep_slices_ports: - target_port = ep_slice_port['port'] - port_name = ep_slice_port.get('name') - try: - pool = pool_by_tgt_name[port_name] - except KeyError: - LOG.debug("No pool found for port: %r", port_name) - continue - - if (target_ip, target_port, pool['id']) in current_targets: - continue - - member_subnet_id = self._get_subnet_by_octavia_mode( - target_pod, target_ip, loadbalancer_crd) - - if not member_subnet_id: - msg = ( - f'Unable to determine ID of the subnet of member ' - f'{target_ip} for service ' - f'{utils.get_res_unique_name(loadbalancer_crd)}. ' - f'Skipping its creation') - self._add_event(loadbalancer_crd, 'KuryrSkipMember', - msg, 'Warning') - LOG.warning(msg) - continue - - target_name, target_namespace = self._get_target_info( - target_ref, loadbalancer_crd) - - first_member_of_the_pool = True - for member in loadbalancer_crd['status'].get( - 'members', []): - if pool['id'] == member['pool_id']: - first_member_of_the_pool = False - break - if first_member_of_the_pool: - listener_port = lsnr_by_id[pool['listener_id']][ - 'port'] - else: - listener_port = None - loadbalancer = loadbalancer_crd['status']['loadbalancer'] - member = self._drv_lbaas.ensure_member( - loadbalancer=loadbalancer, - pool=pool, - subnet_id=member_subnet_id, - ip=target_ip, - port=target_port, - target_ref_namespace=target_namespace, - target_ref_name=target_name, - listener_port=listener_port) - if not member: - continue - members = loadbalancer_crd['status'].get('members', []) - if members: - loadbalancer_crd['status'].get('members', []).append( - member) - else: - loadbalancer_crd['status']['members'] = [] - loadbalancer_crd['status'].get('members', []).append( - member) - if not self._patch_status(loadbalancer_crd): - return False - changed = True - return changed - - def _get_target_info(self, target_ref, loadbalancer_crd): - if target_ref: - target_namespace = target_ref['namespace'] - target_name = target_ref['name'] - else: - target_namespace = loadbalancer_crd['metadata']['namespace'] - target_name = loadbalancer_crd['metadata']['name'] - return target_name, target_namespace - - def _get_subnet_by_octavia_mode(self, target_pod, target_ip, lb_crd): - # TODO(apuimedo): Do not pass subnet_id at all when in - # L3 mode once old neutron-lbaasv2 is not supported, as - # octavia does not require it - subnet_id = None - if (CONF.octavia_defaults.member_mode == - k_const.OCTAVIA_L2_MEMBER_MODE): - if target_pod: - subnet_id = self._get_pod_subnet(target_pod, target_ip) - else: - subnet = utils.get_subnet_by_ip( - self._get_nodes_subnets(), target_ip) - if subnet: - subnet_id = subnet[0] - else: - # We use the service subnet id so that the connectivity - # from VIP to pods happens in layer 3 mode, i.e., - # routed. - subnet_id = lb_crd['status']['loadbalancer']['subnet_id'] - return subnet_id - - def _get_pod_subnet(self, pod, ip): - project_id = self._drv_pod_project.get_project(pod) - - subnet_ids = [] - if not utils.is_host_network(pod): - subnets_map = self._drv_pod_subnets.get_subnets(pod, project_id) - subnet_ids = [subnet_id - for subnet_id, network in subnets_map.items() - for subnet in network.subnets.objects - if ip in subnet.cidr] - if subnet_ids: - return subnet_ids[0] - else: - # NOTE(ltomasbo): We are assuming that if IP is not on the - # pod subnet it's because the member is using hostNetworking. In - # this case we look for the IP in worker_nodes_subnets. - subnet = utils.get_subnet_by_ip(self._get_nodes_subnets(), ip) - if subnet: - return subnet[0] - else: - # This shouldn't ever happen but let's return just the first - # worker_nodes_subnet id. - return self._get_nodes_subnets()[0][0] - - def _get_port_in_pool(self, pool, loadbalancer_crd): - - for l in loadbalancer_crd['status']['listeners']: - if l['id'] != pool['listener_id']: - continue - for port in loadbalancer_crd['spec'].get('ports', []): - if l.get('port') == port.get( - 'port') and l.get('protocol') == port.get('protocol'): - return port - return None - - def _remove_unused_members(self, loadbalancer_crd): - lb_crd_name = loadbalancer_crd['metadata']['name'] - spec_ports = {} - pools = loadbalancer_crd['status'].get('pools', []) - for pool in pools: - port = self._get_port_in_pool(pool, loadbalancer_crd) - if port: - if not port.get('name'): - port['name'] = None - spec_ports[port['name']] = pool['id'] - - ep_slices = loadbalancer_crd['spec'].get('endpointSlices', []) - current_targets = [utils.get_current_endpoints_target( - ep, p, spec_ports, lb_crd_name) - for ep_slice in ep_slices - for ep in ep_slice['endpoints'] - for p in ep_slice['ports'] - if p.get('name') in spec_ports] - - removed_ids = set() - for member in loadbalancer_crd['status'].get('members', []): - member_name = member.get('name', '') - try: - # NOTE: The member name is compose of: - # NAMESPACE_NAME/POD_NAME:PROTOCOL_PORT - pod_name = member_name.split('/')[1].split(':')[0] - except AttributeError: - pod_name = "" - - if ((str(member['ip']), pod_name, member['port'], member[ - 'pool_id']) in current_targets): - continue - - self._drv_lbaas.release_member(loadbalancer_crd['status'][ - 'loadbalancer'], member) - removed_ids.add(member['id']) - - if removed_ids: - members = [m for m in loadbalancer_crd['status'].get('members', []) - if m['id'] not in removed_ids] - loadbalancer_crd['status']['members'] = members - - if not self._patch_status(loadbalancer_crd): - return False - return bool(removed_ids) - - def _sync_lbaas_pools(self, loadbalancer_crd): - changed = False - - if self._remove_unused_pools(loadbalancer_crd): - changed = True - - if self._sync_lbaas_listeners(loadbalancer_crd): - changed = True - - if self._add_new_pools(loadbalancer_crd): - changed = True - - return changed - - def _add_new_pools(self, loadbalancer_crd): - changed = False - - current_listeners_ids = {pool['listener_id'] - for pool in loadbalancer_crd['status'].get( - 'pools', [])} - for listener in loadbalancer_crd['status'].get('listeners', []): - if listener['id'] in current_listeners_ids: - continue - pool = self._drv_lbaas.ensure_pool(loadbalancer_crd['status'][ - 'loadbalancer'], listener) - if not pool: - continue - pools = loadbalancer_crd['status'].get('pools', []) - if pools: - loadbalancer_crd['status'].get('pools', []).append( - pool) - else: - loadbalancer_crd['status']['pools'] = [] - loadbalancer_crd['status'].get('pools', []).append( - pool) - - if not self._patch_status(loadbalancer_crd): - return False - changed = True - return changed - - def _is_pool_in_spec(self, pool, loadbalancer_crd): - # NOTE(yboaron): in order to check if a specific pool is in lbaas_spec - # we should: - # 1. get the listener that pool is attached to - # 2. check if listener's attributes appear in lbaas_spec. - for l in loadbalancer_crd['status']['listeners']: - if l['id'] != pool['listener_id']: - continue - for port in loadbalancer_crd['spec'].get('ports', []): - if l['port'] == port['port'] and l['protocol'] == port[ - 'protocol']: - return True - return False - - def _remove_unused_pools(self, loadbalancer_crd): - removed_ids = set() - - for pool in loadbalancer_crd['status'].get('pools', []): - if self._is_pool_in_spec(pool, loadbalancer_crd): - continue - self._drv_lbaas.release_pool(loadbalancer_crd['status'][ - 'loadbalancer'], pool) - removed_ids.add(pool['id']) - if removed_ids: - loadbalancer_crd['status']['pools'] = [p for p in loadbalancer_crd[ - 'status'].get('pools', []) if p['id'] not in removed_ids] - loadbalancer_crd['status']['members'] = [m for m in - loadbalancer_crd[ - 'status'].get( - 'members', []) - if m['pool_id'] not in - removed_ids] - - if not self._patch_status(loadbalancer_crd): - return False - return bool(removed_ids) - - def _sync_lbaas_listeners(self, loadbalancer_crd): - changed = False - - if self._remove_unused_listeners(loadbalancer_crd): - changed = True - - if self._sync_lbaas_loadbalancer(loadbalancer_crd): - changed = True - - if self._add_new_listeners(loadbalancer_crd): - changed = True - - return changed - - def _add_new_listeners(self, loadbalancer_crd): - changed = False - lb_crd_spec_ports = loadbalancer_crd['spec'].get('ports') - spec_t_cli = loadbalancer_crd['spec'].get('timeout_client_data', 0) - spec_t_mb = loadbalancer_crd['spec'].get('timeout_member_data', 0) - if not lb_crd_spec_ports: - return changed - lbaas_spec_ports = sorted(lb_crd_spec_ports, - key=lambda x: x['protocol']) - for port_spec in lbaas_spec_ports: - protocol = port_spec['protocol'] - port = port_spec['port'] - - listener = [] - for l in loadbalancer_crd['status'].get('listeners', []): - timeout_cli = l.get('timeout_client_data', 0) - timeout_mb = l.get('timeout_member_data', 0) - if l['port'] == port and l['protocol'] == protocol: - if timeout_cli == spec_t_cli and timeout_mb == spec_t_mb: - listener.append(l) - - if listener: - continue - # FIXME (maysams): Due to a bug in Octavia, which does - # not allows listeners with same port but different - # protocols to co-exist, we need to skip the creation of - # listeners that have the same port as an existing one. - listener = [l for l in loadbalancer_crd['status'].get( - 'listeners', []) if l['port'] == port] - - if listener and not self._drv_lbaas.double_listeners_supported(): - msg = ( - f'Octavia does not support multiple listeners listening ' - f'on the same port. Skipping creation of listener ' - f'{protocol}:{port} because {listener["protocol"]}:' - f'{listener["port"]} already exists for Service ' - f'{utils.get_res_unique_name(loadbalancer_crd)}') - self._add_event(loadbalancer_crd, 'KuryrSkipListener', msg, - 'Warning') - LOG.warning(msg) - continue - if protocol == "SCTP" and not self._drv_lbaas.sctp_supported(): - msg = ( - f'Skipping listener {protocol}:{port} creation as Octavia ' - f'does not support {protocol} protocol.') - self._add_event(loadbalancer_crd, 'KuryrSkipListener', msg, - 'Warning') - LOG.warning(msg) - continue - listener = self._drv_lbaas.ensure_listener( - loadbalancer=loadbalancer_crd['status'].get('loadbalancer'), - protocol=protocol, - port=port, - service_type=loadbalancer_crd['spec'].get('type'), - timeout_client_data=spec_t_cli, - timeout_member_data=spec_t_mb) - - if listener is not None: - listeners = loadbalancer_crd['status'].get('listeners', []) - if listeners: - for pre_listener in listeners: - if pre_listener['id'] == listener['id']: - listeners.remove(pre_listener) - listeners.append(listener) - else: - loadbalancer_crd['status']['listeners'] = [] - loadbalancer_crd['status'].get('listeners', []).append( - listener) - - if not self._patch_status(loadbalancer_crd): - return False - changed = True - return changed - - def _remove_unused_listeners(self, loadbalancer_crd): - current_listeners = {p['listener_id'] for p in loadbalancer_crd[ - 'status'].get('pools', [])} - removed_ids = set() - for listener in loadbalancer_crd['status'].get('listeners', []): - if listener['id'] in current_listeners: - continue - self._drv_lbaas.release_listener(loadbalancer_crd['status'][ - 'loadbalancer'], listener) - removed_ids.add(listener['id']) - if removed_ids: - loadbalancer_crd['status']['listeners'] = [ - l for l in loadbalancer_crd['status'].get('listeners', - []) if l['id'] - not in removed_ids] - - if not self._patch_status(loadbalancer_crd): - return False - return bool(removed_ids) - - def _update_lb_status(self, lb_crd): - lb_crd_status = lb_crd['status'] - lb_ip_address = lb_crd_status['service_pub_ip_info']['ip_addr'] - name = lb_crd['metadata']['name'] - ns = lb_crd['metadata']['namespace'] - status_data = {"loadBalancer": { - "ingress": [{"ip": lb_ip_address.format()}]}} - try: - self.k8s.patch("status", f"{k_const.K8S_API_NAMESPACES}" - f"/{ns}/services/{name}/status", - status_data) - except k_exc.K8sConflict: - raise k_exc.ResourceNotReady(name) - except k_exc.K8sClientException as e: - msg = (f'K8s API error when updating external FIP data of Service ' - f'{utils.get_res_unique_name(lb_crd)}') - LOG.exception(msg) - self._add_event(lb_crd, 'KuryrUpdateServiceStatusError', - f'{msg}: {e}', 'Warning') - raise - - def _sync_lbaas_loadbalancer(self, loadbalancer_crd): - lb = loadbalancer_crd['status'].get('loadbalancer') - - if lb and lb['ip'] != loadbalancer_crd['spec'].get('ip'): - # if loadbalancerIP was associated to lbaas VIP, disassociate it. - - try: - pub_info = loadbalancer_crd['status']['service_pub_ip_info'] - except KeyError: - pub_info = None - - if pub_info: - self._drv_service_pub_ip.disassociate_pub_ip( - loadbalancer_crd['status']['service_pub_ip_info']) - self._drv_service_pub_ip.release_pub_ip( - loadbalancer_crd['status']['service_pub_ip_info']) - - self._drv_lbaas.release_loadbalancer( - loadbalancer=lb) - - lb = {} - loadbalancer_crd['status'] = {} - - if not lb: - if loadbalancer_crd['spec'].get('ip'): - lb_name = self._drv_lbaas.get_service_loadbalancer_name( - loadbalancer_crd['metadata']['namespace'], - loadbalancer_crd['metadata']['name']) - lb = self._drv_lbaas.ensure_loadbalancer( - name=lb_name, - project_id=loadbalancer_crd['spec'].get('project_id'), - subnet_id=loadbalancer_crd['spec'].get('subnet_id'), - ip=loadbalancer_crd['spec'].get('ip'), - security_groups_ids=loadbalancer_crd['spec'].get( - 'security_groups_ids'), - service_type=loadbalancer_crd['spec'].get('type'), - provider=loadbalancer_crd['spec'].get('provider')) - loadbalancer_crd['status']['loadbalancer'] = lb - - return self._patch_status(loadbalancer_crd) - return False - - def _ensure_release_lbaas(self, loadbalancer_crd): - self._drv_lbaas.release_loadbalancer( - loadbalancer_crd['status'].get('loadbalancer')) - utils.clean_lb_crd_status( - utils.get_res_unique_name(loadbalancer_crd)) - # NOTE(ltomasbo): give some extra time to ensure the Load - # Balancer VIP is also released - time.sleep(1) diff --git a/kuryr_kubernetes/controller/handlers/machine.py b/kuryr_kubernetes/controller/handlers/machine.py deleted file mode 100644 index 1c4ffa9d1..000000000 --- a/kuryr_kubernetes/controller/handlers/machine.py +++ /dev/null @@ -1,67 +0,0 @@ -# Copyright 2020 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. - -import uuid - -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 - -LOG = logging.getLogger(__name__) - - -class MachineHandler(k8s_base.ResourceEventHandler): - """MachineHandler gathers info about OpenShift nodes needed by Kuryr. - - At the moment that's the subnets of all the worker nodes. - """ - OBJECT_KIND = constants.OPENSHIFT_OBJ_MACHINE - OBJECT_WATCH_PATH = constants.OPENSHIFT_API_CRD_MACHINES - - def __init__(self): - super(MachineHandler, self).__init__() - self.node_subnets_driver = drivers.NodesSubnetsDriver.get_instance() - - def _bump_nps(self): - """Bump NetworkPolicy objects to have the SG rules recalculated.""" - k8s = clients.get_kubernetes_client() - # NOTE(dulek): Listing KuryrNetworkPolicies instead of NetworkPolicies, - # as we only care about NPs already handled. - knps = k8s.get(constants.K8S_API_CRD_KURYRNETWORKPOLICIES) - for knp in knps.get('items', []): - try: - k8s.annotate( - knp['metadata']['annotations']['networkPolicyLink'], - {constants.K8S_ANNOTATION_POLICY: str(uuid.uuid4())}) - except exceptions.K8sResourceNotFound: - # Had to be deleted in the meanwhile. - pass - - def on_present(self, machine, *args, **kwargs): - effect = self.node_subnets_driver.add_node(machine) - if effect: - # If the change was meaningful we need to make sure all the NPs - # are recalculated to get the new SG rules added. - self._bump_nps() - - def on_deleted(self, machine, *args, **kwargs): - effect = self.node_subnets_driver.delete_node(machine) - if effect: - # If the change was meaningful we need to make sure all the NPs - # are recalculated to get the old SG rule deleted. - self._bump_nps() diff --git a/kuryr_kubernetes/controller/handlers/namespace.py b/kuryr_kubernetes/controller/handlers/namespace.py deleted file mode 100644 index 455cda5f7..000000000 --- a/kuryr_kubernetes/controller/handlers/namespace.py +++ /dev/null @@ -1,147 +0,0 @@ -# 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() - - def on_present(self, namespace, *args, **kwargs): - 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 - - if not self._handle_namespace(ns_name): - LOG.debug("Namespace %s has no Pods that should be handled. " - "Skipping event.", ns_name) - return - - try: - self._add_kuryrnetwork_crd(namespace, ns_labels) - except exceptions.K8sClientException: - LOG.exception("Kuryrnetwork CRD creation failed.") - raise exceptions.ResourceNotReady(namespace) - - def _handle_namespace(self, namespace): - """Evaluate if the Namespace should be handled - - Fetches all the Pods in the Namespace and check - if there is any Pod in that Namespace on Pods Network. - - :param namespace: Namespace name - :returns: True if the Namespace resources should be - created, False if otherwise. - """ - kubernetes = clients.get_kubernetes_client() - pods = kubernetes.get( - '{}/namespaces/{}/pods'.format( - constants.K8S_API_BASE, namespace)) - return any(not utils.is_host_network(pod) - for pod in pods.get('items', [])) - - 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): - ns_name = namespace['metadata']['name'] - project_id = self._drv_project.get_project(namespace) - kubernetes = clients.get_kubernetes_client() - - kns_crd = { - 'apiVersion': 'openstack.org/v1', - 'kind': 'KuryrNetwork', - 'metadata': { - 'name': ns_name, - 'finalizers': [constants.KURYRNETWORK_FINALIZER], - }, - 'spec': { - 'nsName': ns_name, - 'projectId': project_id, - 'nsLabels': ns_labels, - } - } - try: - kubernetes.post('{}/{}/kuryrnetworks'.format( - constants.K8S_API_CRD_NAMESPACES, ns_name), 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_KURYRNETWORKS) 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 diff --git a/kuryr_kubernetes/controller/handlers/pipeline.py b/kuryr_kubernetes/controller/handlers/pipeline.py deleted file mode 100644 index e9cebf9b0..000000000 --- a/kuryr_kubernetes/controller/handlers/pipeline.py +++ /dev/null @@ -1,72 +0,0 @@ -# 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. - -from requests import exceptions as requests_exc - -from keystoneauth1 import exceptions as key_exc - -from kuryr_kubernetes import exceptions -from kuryr_kubernetes.handlers import asynchronous as h_async -from kuryr_kubernetes.handlers import dispatch as h_dis -from kuryr_kubernetes.handlers import k8s_base as h_k8s -from kuryr_kubernetes.handlers import logging as h_log -from kuryr_kubernetes.handlers import retry as h_retry - - -class ControllerPipeline(h_dis.EventPipeline): - """Serves as an entry point for controller Kubernetes events. - - `ControllerPipeline` is an entry point handler for the Kuryr-Kubernetes - controller. `ControllerPipeline` allows registering - :class:`kuryr_kubernetes.handlers.k8s_base.ResourceEventHandler`s and - ensures the proper handler is called for each event that is passed to the - `ControllerPipeline`. Also it ensures the following behavior: - - - multiple `ResourceEventHandler`s can be registered for the same - resource type (`OBJECT_KIND`) - - - failing handlers (i.e. ones that raise `Exception`s) are retried - until either the handler succeeds or a finite amount of time passes, - in which case the most recent exception is logged - - - in case there are multiple handlers registered for the same resource - type, all such handlers are considered independent (i.e. if one - handler fails, other handlers will still be called regardless; and the - order in which such handlers are called is not determined) - - - events for different Kubernetes objects can be handled concurrently - - - events for the same Kubernetes object are handled sequentially in - the order of arrival - """ - - def __init__(self, thread_group): - self._tg = thread_group - super(ControllerPipeline, self).__init__() - - def _wrap_consumer(self, consumer): - # TODO(ivc): tune retry interval/timeout - return h_log.LogExceptions( - h_retry.Retry( - consumer, - exceptions=(exceptions.ResourceNotReady, - key_exc.connection.ConnectFailure, - requests_exc.ConnectionError)), - ignore_exceptions=(exceptions.KuryrLoadBalancerNotCreated,)) - - def _wrap_dispatcher(self, dispatcher): - return h_log.LogExceptions(h_async.Async(dispatcher, self._tg, - h_k8s.object_uid, - h_k8s.object_info)) diff --git a/kuryr_kubernetes/controller/handlers/pod_label.py b/kuryr_kubernetes/controller/handlers/pod_label.py deleted file mode 100644 index b2d012e8c..000000000 --- a/kuryr_kubernetes/controller/handlers/pod_label.py +++ /dev/null @@ -1,124 +0,0 @@ -# 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_config import cfg as oslo_cfg -from oslo_log import log as logging -from oslo_serialization import jsonutils - -from kuryr_kubernetes import clients -from kuryr_kubernetes import constants -from kuryr_kubernetes.controller.drivers import base as drivers -from kuryr_kubernetes.controller.drivers import utils as driver_utils -from kuryr_kubernetes import exceptions as k_exc -from kuryr_kubernetes.handlers import k8s_base -from kuryr_kubernetes import utils - -LOG = logging.getLogger(__name__) - - -class PodLabelHandler(k8s_base.ResourceEventHandler): - """Controller side of Pod Label process for Kubernetes pods. - - `PodLabelHandler` runs on the Kuryr-Kubernetes controller and is - responsible for triggering the vif port updates upon pod labels changes. - """ - - OBJECT_KIND = constants.K8S_OBJ_POD - OBJECT_WATCH_PATH = "%s/%s" % (constants.K8S_API_BASE, "pods") - - def __init__(self): - super(PodLabelHandler, self).__init__() - self._drv_project = drivers.PodProjectDriver.get_instance() - self._drv_sg = drivers.PodSecurityGroupsDriver.get_instance() - self._drv_svc_sg = drivers.ServiceSecurityGroupsDriver.get_instance() - self._drv_vif_pool = drivers.VIFPoolDriver.get_instance( - specific_driver='multi_pool') - self._drv_vif_pool.set_vif_driver() - self._drv_lbaas = drivers.LBaaSDriver.get_instance() - - def on_present(self, pod, *args, **kwargs): - if utils.is_host_network(pod) or not self._has_vifs(pod): - # NOTE(ltomasbo): The event will be retried once the vif handler - # annotates the pod with the pod state. - return - - current_pod_info = (pod['metadata'].get('labels'), - pod['status'].get('podIP')) - previous_pod_info = self._get_pod_info(pod) - LOG.debug("Got previous pod info from annotation: %r", - previous_pod_info) - - if current_pod_info == previous_pod_info: - return - - # FIXME(dulek): We should be able to just do create if only podIP - # changed, right? - crd_pod_selectors = self._drv_sg.update_sg_rules(pod) - - project_id = self._drv_project.get_project(pod) - security_groups = self._drv_sg.get_security_groups(pod, project_id) - self._drv_vif_pool.update_vif_sgs(pod, security_groups) - try: - self._set_pod_info(pod, current_pod_info) - except k_exc.K8sResourceNotFound: - LOG.debug("Pod already deleted, no need to retry.") - return - - if oslo_cfg.CONF.octavia_defaults.enforce_sg_rules: - services = driver_utils.get_services() - self._update_services(services, crd_pod_selectors, project_id) - - def _get_pod_info(self, pod): - try: - annotations = pod['metadata']['annotations'] - pod_labels_annotation = annotations[constants.K8S_ANNOTATION_LABEL] - pod_ip_annotation = annotations[constants.K8S_ANNOTATION_IP] - except KeyError: - return None, None - pod_labels = jsonutils.loads(pod_labels_annotation) - return pod_labels, pod_ip_annotation - - def _set_pod_info(self, pod, info): - if not info[0]: - LOG.debug("Removing info annotations: %r", info) - annotation = None, info[1] - else: - annotation = jsonutils.dumps(info[0], sort_keys=True), info[1] - LOG.debug("Setting info annotations: %r", annotation) - - k8s = clients.get_kubernetes_client() - k8s.annotate(utils.get_res_link(pod), - { - constants.K8S_ANNOTATION_LABEL: annotation[0], - constants.K8S_ANNOTATION_IP: annotation[1] - }, - resource_version=pod['metadata']['resourceVersion']) - - def _has_vifs(self, pod): - try: - kp = driver_utils.get_kuryrport(pod) - cr_vifs = driver_utils.get_vifs(kp) - vifs = cr_vifs['status']['vifs'] - LOG.debug("Pod have associated KuryrPort with vifs: %s", vifs) - except KeyError: - return False - return True - - def _update_services(self, services, crd_pod_selectors, project_id): - for service in services.get('items'): - if not driver_utils.service_matches_affected_pods( - service, crd_pod_selectors): - continue - sgs = self._drv_svc_sg.get_security_groups(service, project_id) - self._drv_lbaas.update_lbaas_sg(service, sgs) diff --git a/kuryr_kubernetes/controller/handlers/policy.py b/kuryr_kubernetes/controller/handlers/policy.py deleted file mode 100644 index d26ab3ff2..000000000 --- a/kuryr_kubernetes/controller/handlers/policy.py +++ /dev/null @@ -1,61 +0,0 @@ -# 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 as k_const -from kuryr_kubernetes.controller.drivers import base as drivers -from kuryr_kubernetes.handlers import k8s_base -from kuryr_kubernetes import utils - -LOG = logging.getLogger(__name__) - - -class NetworkPolicyHandler(k8s_base.ResourceEventHandler): - """NetworkPolicyHandler handles k8s Network Policies events""" - - OBJECT_KIND = k_const.K8S_OBJ_POLICY - OBJECT_WATCH_PATH = k_const.K8S_API_POLICIES - - def __init__(self): - super(NetworkPolicyHandler, self).__init__() - self._drv_policy = drivers.NetworkPolicyDriver.get_instance() - self.k8s = clients.get_kubernetes_client() - - def on_present(self, policy, *args, **kwargs): - LOG.debug("Created or updated: %s", policy) - - self._drv_policy.ensure_network_policy(policy) - - # Put finalizer in if it's not there already. - self.k8s.add_finalizer(policy, k_const.NETWORKPOLICY_FINALIZER) - - def on_finalize(self, policy, *args, **kwargs): - LOG.debug("Finalizing policy %s", policy) - if not self._drv_policy.release_network_policy(policy): - # KNP was not found, so we need to finalize on our own. - self.k8s.remove_finalizer(policy, k_const.NETWORKPOLICY_FINALIZER) - - def is_ready(self, quota): - if not (utils.has_kuryr_crd(k_const.K8S_API_CRD_KURYRNETWORKPOLICIES) - and self._check_quota(quota)): - LOG.error("Marking NetworkPolicyHandler as not ready.") - return False - return True - - def _check_quota(self, quota): - if utils.has_limit(quota.security_groups): - return utils.is_available('security_groups', quota.security_groups) - return True diff --git a/kuryr_kubernetes/controller/handlers/vif.py b/kuryr_kubernetes/controller/handlers/vif.py deleted file mode 100644 index 3f5705cb4..000000000 --- a/kuryr_kubernetes/controller/handlers/vif.py +++ /dev/null @@ -1,217 +0,0 @@ -# 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 uuid - -from oslo_config import cfg -from oslo_log import log as logging - -from kuryr_kubernetes import clients -from kuryr_kubernetes import constants -from kuryr_kubernetes.controller.drivers import utils as driver_utils -from kuryr_kubernetes import exceptions as k_exc -from kuryr_kubernetes.handlers import k8s_base -from kuryr_kubernetes import utils - -CONF = cfg.CONF -LOG = logging.getLogger(__name__) -KURYRPORT_URI = constants.K8S_API_CRD_NAMESPACES + '/{ns}/kuryrports/{crd}' - - -class VIFHandler(k8s_base.ResourceEventHandler): - """Controller side of VIF binding process for Kubernetes pods. - - `VIFHandler` runs on the Kuryr-Kubernetes controller and together with - the CNI driver (that runs on 'kubelet' nodes) is responsible for providing - networking to Kubernetes pods. `VIFHandler` relies on a set of drivers - (which are responsible for managing Neutron resources) to define the VIF - objects and pass them to the CNI driver in form of the Kubernetes pod - annotation. - """ - - OBJECT_KIND = constants.K8S_OBJ_POD - OBJECT_WATCH_PATH = "%s/%s" % (constants.K8S_API_BASE, "pods") - - def __init__(self): - super(VIFHandler).__init__() - self.k8s = clients.get_kubernetes_client() - - def on_present(self, pod, *args, **kwargs): - if utils.is_host_network(pod): - return - - pod_name = pod['metadata']['name'] - if utils.is_pod_completed(pod): - LOG.debug("Pod %s has completed execution, " - "removing the vifs", pod_name) - self.on_finalize(pod) - return - - if not self._is_pod_scheduled(pod): - # REVISIT(ivc): consider an additional configurable check that - # would allow skipping pods to enable heterogeneous environments - # where certain pods/namespaces/nodes can be managed by other - # networking solutions/CNI drivers. - return - - namespace = pod['metadata']['namespace'] - kuryrnetwork_path = '{}/{}/kuryrnetworks/{}'.format( - constants.K8S_API_CRD_NAMESPACES, namespace, - namespace) - kuryrnetwork = driver_utils.get_k8s_resource(kuryrnetwork_path) - kuryrnetwork_status = kuryrnetwork.get('status', {}) - if (CONF.kubernetes.pod_subnets_driver == 'namespace' and - (not kuryrnetwork or not kuryrnetwork_status.get('routerId'))): - namespace_path = '{}/{}'.format( - constants.K8S_API_NAMESPACES, namespace) - LOG.debug("Triggering Namespace Handling %s", namespace_path) - try: - self.k8s.annotate(namespace_path, - {'KuryrTrigger': str(uuid.uuid4())}) - except k_exc.K8sResourceNotFound: - LOG.warning('Ignoring Pod handling, no Namespace %s.', - namespace) - return - raise k_exc.ResourceNotReady(pod) - - # NOTE(gryf): Set the finalizer as soon, as we have pod created. On - # subsequent updates of the pod, add_finalizer will ignore this if - # finalizer exist. - try: - if not self.k8s.add_finalizer(pod, constants.POD_FINALIZER): - # NOTE(gryf) It might happen that pod will be deleted even - # before we got here. - return - except k_exc.K8sClientException as ex: - self.k8s.add_event(pod, 'FailedToAddFinalizerToPod', - f'Adding finalizer to pod has failed: {ex}', - 'Warning') - LOG.exception("Failed to add finalizer to pod object: %s", ex) - raise - - kp = driver_utils.get_kuryrport(pod) - LOG.debug("Got KuryrPort: %r", kp) - if not kp: - try: - self._add_kuryrport_crd(pod) - except k_exc.K8sNamespaceTerminating: - # The underlying namespace is being terminated, we can - # ignore this and let `on_finalize` handle this now. - LOG.debug('Namespace %s is being terminated, ignoring Pod ' - '%s in that namespace.', - pod['metadata']['namespace'], pod_name) - return - except k_exc.K8sClientException as ex: - self.k8s.add_event(pod, 'FailedToCreateKuryrPortCRD', - f'Creating corresponding KuryrPort CRD has ' - f'failed: {ex}', 'Warning') - LOG.exception("Kubernetes Client Exception creating " - "KuryrPort CRD: %s", ex) - raise k_exc.ResourceNotReady(pod) - - def on_finalize(self, pod, *args, **kwargs): - - try: - kp = self.k8s.get(KURYRPORT_URI.format( - ns=pod["metadata"]["namespace"], crd=pod["metadata"]["name"])) - except k_exc.K8sResourceNotFound: - try: - self.k8s.remove_finalizer(pod, constants.POD_FINALIZER) - except k_exc.K8sClientException as ex: - self.k8s.add_event(pod, 'FailedRemovingFinalizerFromPod', - f'Removing finalizer from pod has failed: ' - f'{ex}', 'Warning') - LOG.exception('Failed to remove finalizer from pod: %s', ex) - raise - return - - if 'deletionTimestamp' in kp['metadata']: - # NOTE(gryf): Seems like KP was manually removed. By using - # annotations, force an emition of event to trigger on_finalize - # method on the KuryrPort. - try: - self.k8s.annotate(utils.get_res_link(kp), - {'KuryrTrigger': str(uuid.uuid4())}) - except k_exc.K8sResourceNotFound: - self.k8s.remove_finalizer(pod, constants.POD_FINALIZER) - except k_exc.K8sClientException as ex: - self.k8s.add_event(pod, 'FailedRemovingPodFinalzier', - f'Failed removing finalizer from pod: {ex}', - 'Warning') - raise k_exc.ResourceNotReady(pod['metadata']['name']) - else: - try: - self.k8s.delete(KURYRPORT_URI - .format(ns=pod["metadata"]["namespace"], - crd=pod["metadata"]["name"])) - except k_exc.K8sResourceNotFound: - self.k8s.remove_finalizer(pod, constants.POD_FINALIZER) - - except k_exc.K8sClientException as ex: - self.k8s.add_event(pod, 'FailedRemovingKuryrPortCRD', - f'Failed removing corresponding KuryrPort ' - f'CRD: {ex}', 'Warning') - LOG.exception("Could not remove KuryrPort CRD for pod %s.", - pod['metadata']['name']) - raise k_exc.ResourceNotReady(pod['metadata']['name']) - - def is_ready(self, quota): - if (utils.has_limit(quota.ports) and - not utils.is_available('ports', quota.ports)): - LOG.error('Marking VIFHandler as not ready.') - return False - return True - - @staticmethod - def _is_pod_scheduled(pod): - """Checks if Pod is in PENDING status and has node assigned.""" - try: - return (pod['spec']['nodeName'] and - pod['status']['phase'] == constants.K8S_POD_STATUS_PENDING) - except KeyError: - return False - - def _add_kuryrport_crd(self, pod): - LOG.debug('Adding CRD %s', pod["metadata"]["name"]) - - vifs = {} - - owner_reference = {'apiVersion': pod['apiVersion'], - 'kind': pod['kind'], - 'name': pod['metadata']['name'], - 'uid': pod['metadata']['uid']} - - kuryr_port = { - 'apiVersion': constants.K8S_API_CRD_VERSION, - 'kind': constants.K8S_OBJ_KURYRPORT, - 'metadata': { - 'name': pod['metadata']['name'], - 'finalizers': [constants.KURYRPORT_FINALIZER], - 'labels': { - constants.KURYRPORT_LABEL: pod['spec']['nodeName'] - }, - 'ownerReferences': [owner_reference] - }, - 'spec': { - 'podUid': pod['metadata']['uid'], - 'podNodeName': pod['spec']['nodeName'], - 'podStatic': utils.is_pod_static(pod) - }, - 'status': { - 'vifs': vifs - } - } - - self.k8s.post(KURYRPORT_URI.format(ns=pod["metadata"]["namespace"], - crd=''), kuryr_port) diff --git a/kuryr_kubernetes/controller/managers/__init__.py b/kuryr_kubernetes/controller/managers/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/kuryr_kubernetes/controller/managers/health.py b/kuryr_kubernetes/controller/managers/health.py deleted file mode 100644 index e8cb79ea3..000000000 --- a/kuryr_kubernetes/controller/managers/health.py +++ /dev/null @@ -1,114 +0,0 @@ -# Copyright 2018 Maysa de Macedo Souza. -# -# 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 http import client as httplib -import os - -from oslo_config import cfg -from oslo_log import log as logging - -from kuryr.lib._i18n import _ -from kuryr.lib import config as kuryr_config -from kuryr.lib import utils -from kuryr_kubernetes import clients -from kuryr_kubernetes import config -from kuryr_kubernetes.handlers import health as h_health -from kuryr_kubernetes import health as base_server - -LOG = logging.getLogger(__name__) -CONF = cfg.CONF - -health_server_opts = [ - cfg.IntOpt('port', - help=_('port for Health HTTP Server.'), - default=8082), -] - -CONF.register_opts(health_server_opts, "health_server") - - -class HealthServer(base_server.BaseHealthServer): - """Proxy server used by readiness and liveness probes to manage health checks. - - Allows to verify connectivity with Kubernetes API, Keystone and Neutron. - If pool ports functionality is enabled it is verified whether - the precreated ports are loaded into the pools. Also, checks handlers - states. - """ - - def __init__(self): - super().__init__('controller-health', CONF.health_server.port) - self._registry = h_health.HealthRegister.get_instance().registry - - def _components_ready(self): - os_net = clients.get_network_client() - project_id = config.CONF.neutron_defaults.project - quota = os_net.get_quota(quota=project_id, details=True) - - for component in self._registry: - if not component.is_ready(quota): - LOG.debug('Controller component not ready: %s.' % component) - return False - return True - - def readiness_status(self): - if CONF.kubernetes.vif_pool_driver != 'noop': - if not os.path.exists('/tmp/pools_loaded'): - error_message = 'Ports not loaded into the pools.' - LOG.error(error_message) - return error_message, httplib.NOT_FOUND, {} - - k8s_conn = self.verify_k8s_connection() - if not k8s_conn: - error_message = 'Error when processing k8s healthz request.' - LOG.error(error_message) - return error_message, httplib.INTERNAL_SERVER_ERROR, {} - try: - self.verify_keystone_connection() - except Exception as ex: - error_message = ('Error when creating a Keystone session and ' - 'getting a token: %s.' % ex) - LOG.exception(error_message) - return error_message, httplib.INTERNAL_SERVER_ERROR, {} - - try: - if not self._components_ready(): - return '', httplib.INTERNAL_SERVER_ERROR, {} - except Exception as ex: - error_message = ('Error when processing neutron request %s' % ex) - LOG.exception(error_message) - return error_message, httplib.INTERNAL_SERVER_ERROR, {} - - return 'ok', httplib.OK, {} - - def liveness_status(self): - for component in self._registry: - if not component.is_alive(): - exc = component.get_last_exception() - if not exc: - msg = f'Component {component.__class__.__name__} is dead.' - LOG.error(msg) - else: - msg = (f'Component {component.__class__.__name__} is dead.' - f' Last caught exception below') - LOG.exception(msg, exc_info=exc) - return msg, httplib.INTERNAL_SERVER_ERROR, {} - return 'ok', httplib.OK, {} - - def verify_keystone_connection(self): - # Obtain a new token to ensure connectivity with keystone - conf_group = kuryr_config.neutron_group.name - auth_plugin = utils.get_auth_plugin(conf_group) - sess = utils.get_keystone_session(conf_group, auth_plugin) - sess.get_token() diff --git a/kuryr_kubernetes/controller/managers/pool.py b/kuryr_kubernetes/controller/managers/pool.py deleted file mode 100644 index 8ea64e1cf..000000000 --- a/kuryr_kubernetes/controller/managers/pool.py +++ /dev/null @@ -1,255 +0,0 @@ -# Copyright 2017 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 http import server -import os -import socketserver -import threading - - -from openstack import exceptions as os_exc -from oslo_config import cfg as oslo_cfg -from oslo_log import log as logging -from oslo_serialization import jsonutils - -from kuryr.lib._i18n import _ - -from kuryr_kubernetes import constants -from kuryr_kubernetes.controller.drivers import base as drivers - -LOG = logging.getLogger(__name__) - -pool_manager_opts = [ - oslo_cfg.StrOpt('sock_file', - help=_("Absolute path to socket file that " - "will be used for communication with " - "the Pool Manager daemon"), - default='/run/kuryr/kuryr_manage.sock'), -] - -oslo_cfg.CONF.register_opts(pool_manager_opts, "pool_manager") - - -class UnixDomainHttpServer(socketserver.ThreadingUnixStreamServer): - pass - - -class RequestHandler(server.BaseHTTPRequestHandler): - protocol = "HTTP/1.0" - - def do_POST(self): - content_length = int(self.headers.get('Content-Length', 0)) - - body = self.rfile.read(content_length) - params = dict(jsonutils.loads(body)) - - if self.path.endswith(constants.VIF_POOL_POPULATE): - trunk_ips = params.get('trunks', None) - num_ports = params.get('num_ports', 1) - if trunk_ips: - try: - self._create_subports(num_ports, trunk_ips) - except Exception: - response = ('Error while populating pool {0} with {1} ' - 'ports.'.format(trunk_ips, num_ports)) - else: - response = ('Ports pool at {0} was populated with {1} ' - 'ports.'.format(trunk_ips, num_ports)) - - self.send_header('Content-Length', len(response)) - self.end_headers() - self.wfile.write(response.encode()) - else: - response = 'Trunk port IP(s) missing.' - self.send_header('Content-Length', len(response)) - self.end_headers() - self.wfile.write(response.encode()) - - elif self.path.endswith(constants.VIF_POOL_FREE): - trunk_ips = params.get('trunks', None) - if not trunk_ips: - pool = "all" - else: - pool = trunk_ips - - try: - self._delete_subports(trunk_ips) - except Exception: - response = 'Error freeing ports pool: {0}.'.format(pool) - else: - response = 'Ports pool belonging to {0} was freed.'.format( - pool) - - self.send_header('Content-Length', len(response)) - self.end_headers() - self.wfile.write(response.encode()) - - else: - response = 'Method not allowed.' - self.send_header('Content-Length', len(response)) - self.end_headers() - self.wfile.write(response.encode()) - - def do_GET(self): - content_length = int(self.headers.get('Content-Length', 0)) - - body = self.rfile.read(content_length) - params = dict(jsonutils.loads(body)) - - if self.path.endswith(constants.VIF_POOL_LIST): - try: - pools_info = self._list_pools() - except Exception: - response = 'Error listing the pools.' - else: - response = 'Pools:\n{0}'.format(pools_info) - - self.send_header('Content-Length', len(response)) - self.end_headers() - self.wfile.write(response.encode()) - - elif self.path.endswith(constants.VIF_POOL_SHOW): - raw_key = params.get('pool_key', None) - if len(raw_key) != 3: - response = ('Invalid pool key. Proper format is:\n' - '[trunk_ip, project_id, [security_groups]]\n') - else: - pool_key = (raw_key[0], raw_key[1], tuple(sorted(raw_key[2]))) - - try: - pool_info = self._show_pool(pool_key) - except Exception: - response = 'Error showing pool: {0}.'.format(pool_key) - else: - response = 'Pool {0} ports are:\n{1}'.format(pool_key, - pool_info) - - self.send_header('Content-Length', len(response)) - self.end_headers() - self.wfile.write(response.encode()) - - else: - response = 'Method not allowed.' - self.send_header('Content-Length', len(response)) - self.end_headers() - self.wfile.write(response.encode()) - - def _create_subports(self, num_ports, trunk_ips): - try: - drv_project = drivers.PodProjectDriver.get_instance() - drv_subnets = drivers.PodSubnetsDriver.get_instance() - drv_sg = drivers.PodSecurityGroupsDriver.get_instance() - drv_vif = drivers.PodVIFDriver.get_instance() - drv_vif_pool = drivers.VIFPoolDriver.get_instance() - drv_vif_pool.set_vif_driver(drv_vif) - project_id = drv_project.get_project({}) - security_groups = drv_sg.get_security_groups({}, project_id) - subnets = drv_subnets.get_subnets([], project_id) - except TypeError: - LOG.error("Invalid driver type") - raise - - for trunk_ip in trunk_ips: - try: - drv_vif_pool.force_populate_pool( - trunk_ip, project_id, subnets, security_groups, num_ports) - except os_exc.ConflictException: - LOG.error("VLAN Id conflict (already in use) at trunk %s", - trunk_ip) - raise - except os_exc.SDKException: - LOG.exception("Error happened during subports addition at " - "trunk: %s", trunk_ip) - raise - - def _delete_subports(self, trunk_ips): - try: - drv_vif = drivers.PodVIFDriver.get_instance() - drv_vif_pool = drivers.VIFPoolDriver.get_instance() - drv_vif_pool.set_vif_driver(drv_vif) - - drv_vif_pool.free_pool(trunk_ips) - except TypeError: - LOG.error("Invalid driver type") - raise - - def _list_pools(self): - try: - drv_vif = drivers.PodVIFDriver.get_instance() - drv_vif_pool = drivers.VIFPoolDriver.get_instance() - drv_vif_pool.set_vif_driver(drv_vif) - - available_pools = drv_vif_pool.list_pools() - except TypeError: - LOG.error("Invalid driver type") - raise - - pools_info = "" - for pool_key, pool_items in available_pools.items(): - pools_info += (jsonutils.dumps(pool_key) + " has " - + str(len(pool_items)) + " ports\n") - if pools_info: - return pools_info - return "There are no pools" - - def _show_pool(self, pool_key): - try: - drv_vif = drivers.PodVIFDriver.get_instance() - drv_vif_pool = drivers.VIFPoolDriver.get_instance() - drv_vif_pool.set_vif_driver(drv_vif) - - pool = drv_vif_pool.show_pool(pool_key) - except TypeError: - LOG.error("Invalid driver type") - raise - - if pool: - pool_info = "" - for pool_id in pool: - pool_info += str(pool_id) + "\n" - return pool_info - else: - return "Empty pool" - - -class PoolManager(object): - """Manages the ports pool enabling population and free actions. - - `PoolManager` runs on the Kuryr-kubernetes controller and allows to - populate specific pools with a given amount of ports. In addition, it also - allows to remove all the (unused) ports in the given pool(s), or from all - of the pool if none of them is specified. - """ - - def __init__(self): - pool_manager = threading.Thread(target=self._start_kuryr_manage_daemon) - pool_manager.setDaemon(True) - pool_manager.start() - - def _start_kuryr_manage_daemon(self): - LOG.info("Pool manager started") - server_address = oslo_cfg.CONF.pool_manager.sock_file - try: - os.unlink(server_address) - except OSError: - if os.path.exists(server_address): - raise - try: - httpd = UnixDomainHttpServer(server_address, RequestHandler) - httpd.serve_forever() - except KeyboardInterrupt: - pass - except Exception: - LOG.exception('Failed to start Pool Manager.') - httpd.socket.close() diff --git a/kuryr_kubernetes/controller/managers/prometheus_exporter.py b/kuryr_kubernetes/controller/managers/prometheus_exporter.py deleted file mode 100644 index 32a4a3bdd..000000000 --- a/kuryr_kubernetes/controller/managers/prometheus_exporter.py +++ /dev/null @@ -1,192 +0,0 @@ -# Copyright 2020 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. - -import flask -import netaddr -import prometheus_client -from prometheus_client.exposition import generate_latest - -from oslo_config import cfg -from oslo_log import log as logging - -from kuryr_kubernetes import clients -from kuryr_kubernetes import config -from kuryr_kubernetes import utils - -LOG = logging.getLogger(__name__) -CONF = cfg.CONF -RESOURCES = ('ports', 'subnets', 'networks', 'security_groups', - 'security_group_rules') -_NO_QUOTA = 0 -_INF = float("inf") -_NO_LIMIT = -1 - - -class ControllerPrometheusExporter(object): - """Provides metrics to Prometheus""" - - instance = None - - def __init__(self): - self.application = flask.Flask('prometheus-exporter') - self.ctx = None - self.application.add_url_rule( - '/metrics', methods=['GET'], view_func=self.metrics) - self.headers = {'Connection': 'close'} - self._os_net = clients.get_network_client() - self._os_lb = clients.get_loadbalancer_client() - self._project_id = config.CONF.neutron_defaults.project - self._create_metrics() - - def metrics(self): - """Provides the registered metrics""" - self._record_quota_free_count_metric() - self._record_ports_quota_per_subnet_metric() - self._record_lbs_metrics() - - collected_metric = generate_latest(self.registry) - return flask.Response(collected_metric, mimetype='text/plain') - - def record_pod_creation_metric(self, duration): - """Records pod creation duration to the registry""" - self.pod_creation_latency.observe(duration) - - def record_lb_failure(self): - """Increase failure count for Load Balancer readiness""" - self.load_balancer_readiness.inc() - - def record_port_failure(self): - """Increase failure count to Port readiness""" - self.port_readiness.inc() - - @classmethod - def get_instance(cls): - if not ControllerPrometheusExporter.instance: - ControllerPrometheusExporter.instance = cls() - return ControllerPrometheusExporter.instance - - def run(self): - # Disable obtrusive werkzeug logs. - logging.getLogger('werkzeug').setLevel(logging.WARNING) - - address = '::' - try: - LOG.info('Starting Prometheus exporter') - self.application.run( - address, CONF.prometheus_exporter.controller_exporter_port) - except Exception: - LOG.exception('Failed to start Prometheus exporter') - raise - - def _record_quota_free_count_metric(self): - """Records Network resources availability to the registry""" - quota = self._os_net.get_quota(quota=self._project_id, details=True) - for resource in RESOURCES: - resource_quota = quota[resource] - labels = {'resource': resource} - quota_limit = resource_quota['limit'] - if quota_limit == _NO_LIMIT: - self.quota_free_count.labels(**labels).set(quota_limit) - continue - quota_used = resource_quota['used'] - availability = quota_limit - quota_used - if availability >= _NO_QUOTA: - self.quota_free_count.labels(**labels).set(availability) - - def _record_ports_quota_per_subnet_metric(self): - """Records the ports quota per subnet to the registry""" - subnets = self._os_net.subnets(project_id=self._project_id) - namespace_prefix = 'ns/' - for subnet in subnets: - if namespace_prefix not in subnet.name: - continue - total_num_addresses = 0 - ports_availability = 0 - for allocation in subnet.allocation_pools: - total_num_addresses += netaddr.IPRange( - netaddr.IPAddress(allocation['start']), - netaddr.IPAddress(allocation['end'])).size - ports_count = len(list(self._os_net.ports( - fixed_ips=[f'subnet_id={subnet.id}'], - project_id=self._project_id))) - # NOTE(maysams): As the allocation pools range does not take - # into account the Gateway IP, that port IP shouldn't - # be include when counting the used ports. - ports_count = ports_count - 1 - labels = {'subnet_id': subnet.id, 'subnet_name': subnet.name} - ports_availability = total_num_addresses-ports_count - self.port_quota_per_subnet.labels(**labels).set(ports_availability) - - def _record_lbs_metrics(self): - """Records the number of members available per LB and the LB state""" - critical_lbs = [ - ('dns-default', 'openshift-dns'), - ('kubernetes', 'default')] - for name, namespace in critical_lbs: - klb = utils.get_kuryrloadbalancer(name, namespace) - lb = klb.get('status', {}).get('loadbalancer', {}) - lb_id = lb.get('id') - if not lb_id: - continue - lb = self._os_lb.find_load_balancer(lb_id) - labels = {'lb_name': namespace + '/' + name} - if not lb: - self.lbs_state.labels(**labels).state('DELETED') - continue - self.lbs_state.labels(**labels).state(lb.provisioning_status) - pools = self._os_lb.pools(loadbalancer_id=lb.id) - for pool in pools: - labels = {'lb_name': lb.name, 'lb_pool_name': pool.name} - self.lbs_members_count.labels(**labels).set(len(pool.members)) - - def _create_metrics(self): - """Creates a registry and records metrics""" - self.registry = prometheus_client.CollectorRegistry() - self.quota_free_count = prometheus_client.Gauge( - 'kuryr_quota_free_count', 'Amount of quota available' - ' for the network resource', labelnames={'resource'}, - registry=self.registry) - - self.port_quota_per_subnet = prometheus_client.Gauge( - 'kuryr_port_quota_per_subnet', 'Amount of ports available' - ' on Subnet', labelnames={'subnet_id', 'subnet_name'}, - registry=self.registry) - - self.lbs_members_count = prometheus_client.Gauge( - 'kuryr_critical_lb_members_count', 'Amount of members per ' - 'critical Load Balancer pool', - labelnames={'lb_name', 'lb_pool_name'}, - registry=self.registry) - - self.lbs_state = prometheus_client.Enum( - 'kuryr_critical_lb_state', 'Critical Load Balancer State', - labelnames={'lb_name'}, - states=['ERROR', 'ACTIVE', 'DELETED', 'PENDING_CREATE', - 'PENDING_UPDATE', 'PENDING_DELETE'], - registry=self.registry) - - buckets = (10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, _INF) - self.pod_creation_latency = prometheus_client.Histogram( - 'kuryr_pod_creation_latency', 'Time taken for a pod to have' - ' Kuryr annotations set', buckets=buckets, registry=self.registry) - - self.load_balancer_readiness = prometheus_client.Counter( - 'kuryr_load_balancer_readiness', 'This counter is increased when ' - 'Kuryr notices that an Octavia load balancer is stuck in an ' - 'unexpected state', registry=self.registry) - - self.port_readiness = prometheus_client.Counter( - 'kuryr_port_readiness', 'This counter is increased when Kuryr ' - 'times out waiting for Neutron to move port to ACTIVE', - registry=self.registry) diff --git a/kuryr_kubernetes/controller/service.py b/kuryr_kubernetes/controller/service.py deleted file mode 100644 index d062e148b..000000000 --- a/kuryr_kubernetes/controller/service.py +++ /dev/null @@ -1,188 +0,0 @@ -# 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 functools -import sys - -import urllib3 - -import os_vif -from oslo_config import cfg -from oslo_log import log as logging -from oslo_service import periodic_task -from oslo_service import service -from stevedore.named import NamedExtensionManager - -from kuryr_kubernetes import clients -from kuryr_kubernetes import config -from kuryr_kubernetes.controller.drivers import base as drivers -from kuryr_kubernetes.controller.handlers import pipeline as h_pipeline -from kuryr_kubernetes.controller.managers import health -from kuryr_kubernetes.controller.managers import prometheus_exporter as exp -from kuryr_kubernetes import objects -from kuryr_kubernetes import utils -from kuryr_kubernetes import watcher - - -CONF = cfg.CONF -LOG = logging.getLogger(__name__) - - -def _handler_not_found(names): - LOG.exception('Handlers "%s" were not found.', names) - LOG.critical('Handlers "%s" were not found.', names) - raise SystemExit() - - -def _handler_not_loaded(manager, entrypoint, exception): - LOG.exception('Exception when loading handlers %s.', entrypoint) - LOG.critical('Handlers entrypoint "%s" failed to load due to %s.', - entrypoint, exception) - raise SystemExit() - - -def _load_kuryr_ctrlr_handlers(): - configured_handlers = CONF.kubernetes.enabled_handlers - LOG.info('Configured handlers: %s', configured_handlers) - handlers = NamedExtensionManager( - 'kuryr_kubernetes.controller.handlers', - configured_handlers, - invoke_on_load=True, - on_missing_entrypoints_callback=_handler_not_found, - on_load_failure_callback=_handler_not_loaded) - LOG.info('Loaded handlers: %s', handlers.names()) - ctrlr_handlers = [] - for handler in handlers.extensions: - ctrlr_handlers.append(handler.obj) - return ctrlr_handlers - - -class KuryrK8sServiceMeta(type(service.Service), - type(periodic_task.PeriodicTasks)): - pass - - -class KuryrK8sService(service.Service, periodic_task.PeriodicTasks, - metaclass=KuryrK8sServiceMeta): - """Kuryr-Kubernetes controller Service.""" - - def __init__(self): - super(KuryrK8sService, self).__init__() - periodic_task.PeriodicTasks.__init__(self, CONF) - - objects.register_locally_defined_vifs() - pipeline = h_pipeline.ControllerPipeline(self.tg) - self.watcher = watcher.Watcher(pipeline, self.tg) - self.health_manager = health.HealthServer() - self.exporter = exp.ControllerPrometheusExporter.get_instance() - self.current_leader = None - self.node_name = utils.get_node_name() - - self.handlers = _load_kuryr_ctrlr_handlers() - for handler in self.handlers: - self.watcher.add(handler.get_watch_path()) - pipeline.register(handler) - self.pool_driver = drivers.VIFPoolDriver.get_instance( - specific_driver='multi_pool') - self.pool_driver.set_vif_driver() - - def is_leader(self): - return self.current_leader == self.node_name - - def start(self): - LOG.info("Service '%s' starting", self.__class__.__name__) - super(KuryrK8sService, self).start() - - if not CONF.kubernetes.controller_ha: - LOG.info('Running in non-HA mode, starting watcher immediately.') - self.watcher.start() - self.pool_driver.sync_pools() - else: - LOG.info('Running in HA mode, watcher will be started later.') - f = functools.partial(self.run_periodic_tasks, None) - self.tg.add_timer(1, f) - - self.tg.add_thread(self.exporter.run) - self.tg.add_thread(self.health_manager.run) - LOG.info("Service '%s' started", self.__class__.__name__) - - @periodic_task.periodic_task(spacing=5, run_immediately=True) - def monitor_leader(self, context): - if not CONF.kubernetes.controller_ha: - return - leader = utils.get_leader_name() - if leader is None: - # Error when fetching current leader. We're paranoid, so just to - # make sure we won't break anything we'll try to step down. - self.on_revoke_leader() - elif leader != self.current_leader and leader == self.node_name: - # I'm becoming the leader. - self.on_become_leader() - elif leader != self.current_leader and self.is_leader(): - # I'm revoked from being the leader. - self.on_revoke_leader() - elif leader == self.current_leader and self.is_leader(): - # I continue to be the leader - self.on_continue_leader() - - self.current_leader = leader - - def on_become_leader(self): - LOG.info('Controller %s becomes the leader, starting watcher.', - self.node_name) - self.watcher.start() - self.pool_driver.sync_pools() - - def on_revoke_leader(self): - LOG.info('Controller %s stops being the leader, stopping watcher.', - self.node_name) - if self.watcher.is_running(): - self.watcher.stop() - - def on_continue_leader(self): - # Just make sure my watcher is running. - if not self.watcher.is_running(): - LOG.warning('Controller %s is the leader, but has watcher ' - 'stopped. Restarting it.', self.node_name) - self.watcher.start() - - def wait(self): - super(KuryrK8sService, self).wait() - LOG.info("Service '%s' stopped", self.__class__.__name__) - - def stop(self, graceful=False): - LOG.info("Service '%s' stopping", self.__class__.__name__) - self.watcher.stop() - super(KuryrK8sService, self).stop(graceful) - - @periodic_task.periodic_task(spacing=600, run_immediately=False) - def reconcile_loadbalancers(self, context): - LOG.debug("Checking for Kubernetes resources reconciliations") - for handler in self.handlers: - handler.reconcile() - - @periodic_task.periodic_task(spacing=90, run_immediately=False) - def cleanup_dead_resources(self, context): - utils.cleanup_dead_ports() - utils.cleanup_dead_networks() - - -def start(): - urllib3.disable_warnings() - config.init(sys.argv[1:]) - config.setup_logging() - clients.setup_clients() - os_vif.initialize() - kuryrk8s_launcher = service.launch(config.CONF, KuryrK8sService()) - kuryrk8s_launcher.wait() diff --git a/kuryr_kubernetes/exceptions.py b/kuryr_kubernetes/exceptions.py deleted file mode 100644 index 0da90b495..000000000 --- a/kuryr_kubernetes/exceptions.py +++ /dev/null @@ -1,197 +0,0 @@ -# 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. - -from kuryr_kubernetes import utils - - -class K8sClientException(Exception): - pass - - -class IntegrityError(RuntimeError): - pass - - -class InvalidKuryrConfiguration(RuntimeError): - pass - - -class ResourceNotReady(Exception): - def __init__(self, resource): - msg = resource - if type(resource) == dict: - if resource.get('metadata', {}).get('name', None): - res_name = utils.get_res_unique_name(resource) - kind = resource.get('kind') - if kind: - msg = f'{kind} {res_name}' - else: - msg = res_name - self.message = "Resource not ready: %r" % msg - super(ResourceNotReady, self).__init__(self.message) - - -class KuryrLoadBalancerNotCreated(Exception): - def __init__(self, res): - name = utils.get_res_unique_name(res) - super().__init__( - 'KuryrLoadBalancer not created yet for the Service %s' % name) - - -class LoadBalancerNotReady(ResourceNotReady): - def __init__(self, loadbalancer_id, status): - super().__init__( - 'Loadbalancer %s is stuck in %s status for several minutes. This ' - 'is unexpected and indicates problem with OpenStack Octavia. ' - 'Please contact your OpenStack administrator.' % ( - loadbalancer_id, status)) - - -class PortNotReady(ResourceNotReady): - def __init__(self, port_id, status): - super().__init__( - 'Port %s is stuck in %s status for several minutes. This ' - 'is unexpected and indicates problem with OpenStack Neutron. ' - 'Please contact your OpenStack administrator.' % (port_id, status)) - - -class K8sResourceNotFound(K8sClientException): - def __init__(self, resource): - super(K8sResourceNotFound, self).__init__("Resource not " - "found: %r" % resource) - - -class K8sConflict(K8sClientException): - def __init__(self, message): - super(K8sConflict, self).__init__("Conflict: %r" % message) - - -class K8sForbidden(K8sClientException): - def __init__(self, message): - super(K8sForbidden, self).__init__("Forbidden: %r" % message) - - -class K8sNamespaceTerminating(K8sForbidden): - # This is raised when K8s complains about operation failing because - # namespace is being terminated. - def __init__(self, message): - super(K8sNamespaceTerminating, self).__init__( - "Namespace already terminated: %r" % message) - - -class K8sUnprocessableEntity(K8sClientException): - def __init__(self, message): - super(K8sUnprocessableEntity, self).__init__( - "Unprocessable: %r" % message) - - -class K8sFieldValueForbidden(K8sUnprocessableEntity): - pass - - -class InvalidKuryrNetworkAnnotation(Exception): - pass - - -class CNIError(Exception): - pass - - -def format_msg(exception): - return "%s: %s" % (exception.__class__.__name__, exception) - - -class K8sNodeTrunkPortFailure(Exception): - """Exception represents that error is related to K8s node trunk port - - This exception is thrown when Neutron port is not associated to a Neutron - vlan trunk. - """ - - -class AllowedAddressAlreadyPresent(Exception): - """Exception indicates an already present 'allowed address pair' on port - - This exception is raised when an attempt to add an already inserted - 'allowed address pair' on a port is made. Such a condition likely indicates - a bad program state or a programming bug. - """ - - -class MultiPodDriverPoolConfigurationNotSupported(Exception): - """Exception indicates a wrong configuration of the multi pod driver pool - - This exception is raised when the multi pod driver pool is not properly - configured. This could be due to three different reasons: - 1. One of the pool drivers is not supported - 2. One of the pod drivers is not supported - 3. One of the pod drivers is not supported by its selected pool driver - """ - - -class CNITimeout(Exception): - """Exception groups various timeouts happening in the CNI """ - - -class CNIKuryrPortTimeout(CNITimeout): - """Excepton raised on timeout waiting for KuryrPort to be created""" - def __init__(self, name): - super().__init__( - f'Timed out waiting for KuryrPort to be created for pod {name}. ' - f'kuryr-controller is responsible for that, check logs there.') - - -class CNINeutronPortActivationTimeout(CNITimeout): - """Excepton raised on time out waiting for Neutron ports to be ACITVE""" - def __init__(self, name, vifs): - inactive = ', '.join(vif.id for vif in vifs.values() if not vif.active) - super().__init__( - f'Timed out waiting for Neutron port(s) {inactive} to be marked ' - f'as ACTIVE after being bound to a Pod {name}. Most likely this ' - f'indicates an issue with OpenStack Neutron. You can also check ' - f'logs of kuryr-controller to confirm.') - - -class CNIBindingFailure(Exception): - """Exception indicates a binding/unbinding VIF failure in CNI""" - def __init__(self, message): - super(CNIBindingFailure, self).__init__(message) - - -class CNIPodUidMismatch(Exception): - """Excepton raised on a mismatch of CNI request's pod UID and KuryrPort""" - def __init__(self, name, expected, observed): - super().__init__( - f'uid {observed} of the pod {name} does not match the uid ' - f'{expected} requested by the CNI. Dropping CNI request to prevent' - f' race conditions.') - - -class CNIPodGone(Exception): - """Excepton raised when Pod got deleted while processing a CNI request""" - def __init__(self, name): - super().__init__( - f'Pod {name} got deleted while processing the CNI ADD request.') - - -class UnreachableOctavia(Exception): - """Exception indicates Octavia API failure and can not be reached - - This exception is raised when Kuryr can not reach Octavia. The Octavia - API call returns 'None' on the version field and we need to properly log - a message informing the user - """ - def __init__(self, message): - super(UnreachableOctavia, self).__init__(message) diff --git a/kuryr_kubernetes/handlers/__init__.py b/kuryr_kubernetes/handlers/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/kuryr_kubernetes/handlers/asynchronous.py b/kuryr_kubernetes/handlers/asynchronous.py deleted file mode 100755 index 6204974b2..000000000 --- a/kuryr_kubernetes/handlers/asynchronous.py +++ /dev/null @@ -1,123 +0,0 @@ -# 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 itertools -import queue as py_queue -import time - -from oslo_concurrency import lockutils -from oslo_log import log as logging - - -from kuryr_kubernetes.handlers import base - -LOG = logging.getLogger(__name__) - -DEFAULT_QUEUE_DEPTH = 100 -DEFAULT_GRACE_PERIOD = 5 -STALE_PERIOD = 0.5 - - -class Async(base.EventHandler): - """Handles events asynchronously. - - `Async` can be used to decorate another `handler` to be run asynchronously - using the specified `thread_group`. `Async` distinguishes *related* and - *unrelated* events (based on the result of `group_by`(`event`) function) - and handles *unrelated* events concurrently while *related* events are - handled serially and in the same order they arrived to `Async`. - """ - - def __init__(self, handler, thread_group, group_by, info_func, - queue_depth=DEFAULT_QUEUE_DEPTH, - grace_period=DEFAULT_GRACE_PERIOD): - self._handler = handler - self._thread_group = thread_group - self._group_by = group_by - self._info_func = info_func - self._queue_depth = queue_depth - self._grace_period = grace_period - self._queues = {} - - def __call__(self, event, *args, **kwargs): - group = self._group_by(event) - with lockutils.lock(group): - try: - queue = self._queues[group] - # NOTE(dulek): We don't want to risk injecting an outdated - # state if events for that resource are in queue. - if kwargs.get('injected', False): - return - except KeyError: - queue = py_queue.Queue(self._queue_depth) - self._queues[group] = queue - info = self._info_func(event) - thread = self._thread_group.add_thread(self._run, group, queue, - info) - thread.link(self._done, group, info) - queue.put((event, args, kwargs)) - - def _run(self, group, queue, info): - LOG.trace("Asynchronous handler started processing %s (%s)", group, - info) - for _ in itertools.count(): - # NOTE(ivc): this is a mock-friendly replacement for 'while True' - # to allow more controlled environment for unit-tests (e.g. to - # avoid tests getting stuck in infinite loops) - try: - event, args, kwargs = queue.get(timeout=self._grace_period) - except py_queue.Empty: - break - # FIXME(ivc): temporary workaround to skip stale events - # If K8s updates resource while the handler is processing it, - # when the handler finishes its work it can fail to update an - # annotation due to the 'resourceVersion' conflict. K8sClient - # was updated to allow *new* annotations to be set ignoring - # 'resourceVersion', but it leads to another problem as the - # Handler will receive old events (i.e. before annotation is set) - # and will start processing the event 'from scratch'. - # It has negative effect on handlers' performance (VIFHandler - # creates ports only to later delete them and LBaaS handler also - # produces some excess requests to Neutron, although with lesser - # impact). - # Possible solutions (can be combined): - # - use K8s ThirdPartyResources to store data/annotations instead - # of native K8s resources (assuming Kuryr-K8s will own those - # resources and no one else would update them) - # - use the resulting 'resourceVersion' received from K8sClient's - # 'annotate' to provide feedback to Async to skip all events - # until that version - # - stick to the 'get-or-create' behaviour in handlers and - # also introduce cache for long operations - time.sleep(STALE_PERIOD) - while not queue.empty(): - event, args, kwargs = queue.get() - if queue.empty(): - time.sleep(STALE_PERIOD) - self._handler(event, *args, **kwargs) - - def _done(self, thread, group, info): - LOG.trace("Asynchronous handler stopped processing group %s (%s)", - group, info) - queue = self._queues.pop(group) - - if not queue.empty(): - LOG.critical( - "Asynchronous handler thread terminated abnormally; %(count)s " - "events dropped for %(group)s (%(info)s)", - {'count': queue.qsize(), 'group': group, 'info': info}) - - if not self._queues: - LOG.trace("Asynchronous handler is idle") diff --git a/kuryr_kubernetes/handlers/base.py b/kuryr_kubernetes/handlers/base.py deleted file mode 100644 index 438625d47..000000000 --- a/kuryr_kubernetes/handlers/base.py +++ /dev/null @@ -1,28 +0,0 @@ -# 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 - - -class EventHandler(object, metaclass=abc.ABCMeta): - """Base class for event handlers.""" - - @abc.abstractmethod - def __call__(self, event, *args, **kwargs): - """Handle the event.""" - raise NotImplementedError() - - def __str__(self): - return self.__class__.__name__ diff --git a/kuryr_kubernetes/handlers/dispatch.py b/kuryr_kubernetes/handlers/dispatch.py deleted file mode 100644 index 56585bfcd..000000000 --- a/kuryr_kubernetes/handlers/dispatch.py +++ /dev/null @@ -1,126 +0,0 @@ -# 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 - -from oslo_log import log as logging - -from kuryr_kubernetes.handlers import base as h_base - -LOG = logging.getLogger(__name__) - - -class Dispatcher(h_base.EventHandler): - """Dispatches events to registered handlers. - - Dispatcher serves as both multiplexer and filter for dispatching events - to multiple registered handlers based on the event content and - predicates provided during the handler registration. - """ - - def __init__(self): - self._registry = {} - - def register(self, key_fn, key, handler): - """Adds handler to the registry. - - `key_fn` and `key` constitute the `key_fn(event) == key` predicate - that determines if the `handler` should be called for a given `event`. - - :param key_fn: function that will be called for each event to - determine the event `key` - :param key: value to match against the result of `key_fn` function - that determines if the `handler` should be called for an - event - :param handler: `callable` object that would be called if the - conditions specified by `key_fn` and `key` are met - """ - key_group = self._registry.setdefault(key_fn, {}) - handlers = key_group.setdefault(key, []) - handlers.append(handler) - - def __call__(self, event, *args, **kwargs): - handlers = set() - - for key_fn, key_group in self._registry.items(): - key = key_fn(event) - handlers.update(key_group.get(key, ())) - - obj = event.get('object', {}) - obj_meta = obj.get('metadata', {}) - - LOG.trace("%d handler(s) available for event %s %s:%s/%s (uid: %s)", - len(handlers), event.get('type'), obj.get('kind'), - obj_meta.get('namespace'), obj_meta.get('name'), - obj_meta.get('uid')) - - for handler in handlers: - handler(event, *args, **kwargs) - - -class EventConsumer(h_base.EventHandler, metaclass=abc.ABCMeta): - """Consumes events matching specified predicates. - - EventConsumer is an interface for all event handlers that are to be - registered by the `EventPipeline`. - """ - - def __init__(self): - super(EventConsumer, self).__init__() - - @property - @abc.abstractmethod - def consumes(self): - """Predicates determining events supported by this handler. - - :return: `dict` object containing {key_fn: key} predicates to be - used by `Dispatcher.register` - """ - raise NotImplementedError() - - -class EventPipeline(h_base.EventHandler, metaclass=abc.ABCMeta): - """Serves as an entry-point for event handling. - - Implementing subclasses should override `_wrap_dispatcher` and/or - `_wrap_consumer` methods to sanitize the consumers passed to `register` - (i.e. to satisfy the `Watcher` requirement that the event handler does - not raise exceptions) and to add features like asynchronous event - processing or retry-on-failure functionality. - """ - - def __init__(self): - self._dispatcher = Dispatcher() - self._handler = self._wrap_dispatcher(self._dispatcher) - - def register(self, consumer): - """Adds handler to the registry. - - :param consumer: `EventConsumer`-type object - """ - handler = self._wrap_consumer(consumer) - for key_fn, key in consumer.consumes.items(): - self._dispatcher.register(key_fn, key, handler) - - def __call__(self, event, *args, **kwargs): - self._handler(event, *args, **kwargs) - - @abc.abstractmethod - def _wrap_dispatcher(self, dispatcher): - raise NotImplementedError() - - @abc.abstractmethod - def _wrap_consumer(self, consumer): - raise NotImplementedError() diff --git a/kuryr_kubernetes/handlers/health.py b/kuryr_kubernetes/handlers/health.py deleted file mode 100644 index 2fe30cbd9..000000000 --- a/kuryr_kubernetes/handlers/health.py +++ /dev/null @@ -1,57 +0,0 @@ -# Copyright 2018 Maysa de Macedo Souza. -# -# 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. - - -class HealthRegister(object): - instance = None - - def __init__(self): - self.registry = [] - - def register(self, elem): - self.registry.append(elem) - - @classmethod - def get_instance(cls): - if not HealthRegister.instance: - HealthRegister.instance = cls() - return HealthRegister.instance - - -class HealthHandler(object): - """Base class for health handlers.""" - def __init__(self): - super(HealthHandler, self).__init__() - self._alive = True - self._ready = True - self._manager = HealthRegister.get_instance() - self._manager.register(self) - self._last_exception = None - - def set_liveness(self, alive, exc=None): - if exc: - self._last_exception = exc - self._alive = alive - - def set_readiness(self, ready): - self._ready = ready - - def is_alive(self): - return self._alive - - def is_ready(self, *args): - return self._ready - - def get_last_exception(self): - return self._last_exception diff --git a/kuryr_kubernetes/handlers/k8s_base.py b/kuryr_kubernetes/handlers/k8s_base.py deleted file mode 100755 index 78e479c2a..000000000 --- a/kuryr_kubernetes/handlers/k8s_base.py +++ /dev/null @@ -1,121 +0,0 @@ -# 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. - -from kuryr_kubernetes.handlers import dispatch -from kuryr_kubernetes.handlers import health - - -def object_kind(event): - try: - return event['object']['kind'] - except KeyError: - return None - - -def object_uid(event): - try: - return event['object']['metadata']['uid'] - except KeyError: - return None - - -def object_info(event): - try: - resource = event['object'] - try: - return "%(kind)s %(namespace)s/%(name)s" % resource['metadata'] - except KeyError: - return "%(kind)s: %(name)s" % resource['metadata'] - except KeyError: - return None - - -class ResourceEventHandler(dispatch.EventConsumer, health.HealthHandler): - """Base class for K8s event handlers. - - Implementing classes should override both `OBJECT_KIND` and - 'OBJECT_WATCH_PATH' attributes. - The `OBJECT_KIND` should be set to a valid Kubernetes object type - name (e.g. 'Pod' or 'Namespace'; see [1] for more details). - - The `OBJECT_WATCH_PATH` should point to object's watched path, - (e.g. for the 'Pod' case the OBJECT_WATCH_PATH should be '/api/v1/pods'). - - Implementing classes are expected to override any or all of the - `on_added`, `on_present`, `on_modified`, `on_deleted` methods that would - be called depending on the type of the event (with K8s object as a single - argument). - - [1] https://github.com/kubernetes/kubernetes/blob/release-1.4/docs/devel\ - /api-conventions.md#types-kinds - """ - - OBJECT_KIND = None - OBJECT_WATCH_PATH = None - - def __init__(self): - super(ResourceEventHandler, self).__init__() - - def get_watch_path(self): - return self.OBJECT_WATCH_PATH - - @property - def consumes(self): - return {object_kind: self.OBJECT_KIND} - - def _check_finalize(self, obj): - deletion_timestamp = None - try: - deletion_timestamp = obj['metadata']['deletionTimestamp'] - except (KeyError, TypeError): - pass - - return deletion_timestamp - - def __call__(self, event, *args, **kwargs): - event_type = event.get('type') - obj = event.get('object') - if 'MODIFIED' == event_type: - if self._check_finalize(obj): - self.on_finalize(obj, *args, **kwargs) - return - self.on_modified(obj, *args, **kwargs) - self.on_present(obj, *args, **kwargs) - elif 'ADDED' == event_type: - if self._check_finalize(obj): - self.on_finalize(obj, *args, **kwargs) - return - self.on_added(obj, *args, **kwargs) - self.on_present(obj, *args, **kwargs) - elif 'DELETED' == event_type: - self.on_deleted(obj, *args, **kwargs) - - def on_added(self, obj, *args, **kwargs): - pass - - def on_present(self, obj, *args, **kwargs): - pass - - def on_modified(self, obj, *args, **kwargs): - pass - - def on_deleted(self, obj, *args, **kwargs): - pass - - def on_finalize(self, obj, *args, **kwargs): - pass - - def reconcile(self): - pass diff --git a/kuryr_kubernetes/handlers/logging.py b/kuryr_kubernetes/handlers/logging.py deleted file mode 100644 index 4c11c9524..000000000 --- a/kuryr_kubernetes/handlers/logging.py +++ /dev/null @@ -1,49 +0,0 @@ -# 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. - -from oslo_log import log as logging - -from kuryr_kubernetes.handlers import base - -LOG = logging.getLogger(__name__) - - -class LogExceptions(base.EventHandler): - """Suppresses exceptions and sends them to log. - - LogExceptions wraps `handler` passed as an initialization parameter by - suppressing `exceptions` it raises and sending them to logging facility - instead. - """ - - def __init__(self, handler, exceptions=Exception, ignore_exceptions=None): - self._handler = handler - self._exceptions = exceptions - self._ignore_exceptions = ignore_exceptions or () - - def __call__(self, event, *args, **kwargs): - try: - self._handler(event, *args, **kwargs) - except self._ignore_exceptions: - pass - except self._exceptions as ex: - # If exception comes from OpenStack SDK and contains - # 'request_id' then print this 'request_id' along the Exception. - # This 'request_id' can be then used to search the OpenStack - # service logs. - req_id = '' - if hasattr(ex, 'request_id'): - req_id = f' [{ex.request_id}]' - LOG.exception("Failed to handle event%s: %s", req_id, event) diff --git a/kuryr_kubernetes/handlers/retry.py b/kuryr_kubernetes/handlers/retry.py deleted file mode 100644 index 33ea37bc4..000000000 --- a/kuryr_kubernetes/handlers/retry.py +++ /dev/null @@ -1,137 +0,0 @@ -# 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 itertools -import time - -import requests - -from openstack import exceptions as os_exc -from oslo_log import log as logging -from oslo_utils import excutils - -from kuryr_kubernetes import clients -from kuryr_kubernetes.controller.managers import prometheus_exporter -from kuryr_kubernetes import exceptions -from kuryr_kubernetes.handlers import base -from kuryr_kubernetes import utils - -LOG = logging.getLogger(__name__) - - -class Retry(base.EventHandler): - """Retries handler on failure. - - `Retry` can be used to decorate another `handler` to be retried whenever - it raises any of the specified `exceptions`. If the `handler` does not - succeed within the time limit specified by `timeout`, `Retry` will - raise the exception risen by `handler`. `Retry` does not interrupt the - `handler`, so the actual time spent within a single call to `Retry` may - exceed the `timeout` depending on responsiveness of the `handler`. - - `handler` is retried for the same `event` (expected backoff E(c) = - interval * 2 ** c / 2). - """ - - def __init__(self, handler, exceptions=Exception, - timeout=utils.DEFAULT_TIMEOUT, - interval=utils.DEFAULT_INTERVAL): - self._handler = handler - self._exceptions = exceptions - self._timeout = timeout - self._interval = interval - self._k8s = clients.get_kubernetes_client() - - def __call__(self, event, *args, **kwargs): - start_time = time.time() - deadline = time.time() + self._timeout - for attempt in itertools.count(1): - if event.get('type') in ['MODIFIED', 'ADDED']: - obj = event.get('object') - if obj: - try: - obj_link = utils.get_res_link(obj) - except KeyError: - LOG.debug("Unknown object, skipping: %s", obj) - else: - try: - self._k8s.get(obj_link) - except exceptions.K8sResourceNotFound: - LOG.debug("There is no need to process the " - "retry as the object %s has already " - "been deleted.", obj_link) - return - except (exceptions.K8sClientException, - requests.ConnectionError): - LOG.debug("Kubernetes client error getting the " - "object. Continuing with handler " - "execution.") - try: - info = { - 'elapsed': time.time() - start_time - } - self._handler(event, *args, retry_info=info, **kwargs) - break - except (exceptions.LoadBalancerNotReady, - exceptions.PortNotReady) as exc: - cls_map = {'LoadBalancerNotReady': 'record_lb_failure', - 'PortNotReady': 'record_port_failure'} - with excutils.save_and_reraise_exception() as ex: - if self._sleep(deadline, attempt, ex.value): - ex.reraise = False - else: - exporter = (prometheus_exporter - .ControllerPrometheusExporter - .get_instance()) - method = getattr(exporter, cls_map[type(exc).__name__]) - method() - except exceptions.KuryrLoadBalancerNotCreated: - with excutils.save_and_reraise_exception() as ex: - if self._sleep(deadline, attempt, ex.value): - ex.reraise = False - except os_exc.ConflictException: - with excutils.save_and_reraise_exception() as ex: - error_type = clients.get_neutron_error_type(ex.value) - if error_type == 'OverQuota': - if self._sleep(deadline, attempt, ex.value): - ex.reraise = False - except self._exceptions: - with excutils.save_and_reraise_exception() as ex: - if self._sleep(deadline, attempt, ex.value): - ex.reraise = False - else: - LOG.exception('Report handler unhealthy %s', - self._handler) - self._handler.set_liveness(alive=False, exc=ex.value) - except Exception as ex: - LOG.exception('Report handler unhealthy %s', self._handler) - self._handler.set_liveness(alive=False, exc=ex) - raise - - def _sleep(self, deadline, attempt, exception): - LOG.debug("Handler %s failed (attempt %s; %s)", - self._handler, attempt, exceptions.format_msg(exception)) - interval = utils.exponential_sleep(deadline, attempt, - self._interval) - if not interval: - LOG.debug("Handler %s failed (attempt %s; %s), " - "timeout exceeded (%s seconds)", - self._handler, attempt, exceptions.format_msg(exception), - self._timeout) - return 0 - - LOG.debug("Resumed after %s seconds. Retry handler %s", interval, - self._handler) - return interval diff --git a/kuryr_kubernetes/health.py b/kuryr_kubernetes/health.py deleted file mode 100644 index d45d80017..000000000 --- a/kuryr_kubernetes/health.py +++ /dev/null @@ -1,76 +0,0 @@ -# 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 - -from flask import Flask -from oslo_config import cfg -from oslo_log import log as logging - -from kuryr_kubernetes import clients - -LOG = logging.getLogger(__name__) -CONF = cfg.CONF - - -class BaseHealthServer(abc.ABC): - """Base class of server used to provide readiness and liveness probes.""" - - def __init__(self, app_name, port): - self.app_name = app_name - self.port = port - self.ctx = None - self.application = Flask(app_name) - self.application.add_url_rule( - '/ready', methods=['GET'], view_func=self.readiness_status) - self.application.add_url_rule( - '/alive', methods=['GET'], view_func=self.liveness_status) - - def apply_conn_close(response): - response.headers['Connection'] = 'close' - return response - - self.application.after_request(apply_conn_close) - - @abc.abstractmethod - def readiness_status(self): - raise NotImplementedError() - - @abc.abstractmethod - def liveness_status(self): - raise NotImplementedError() - - def run(self): - # Disable obtrusive werkzeug logs. - logging.getLogger('werkzeug').setLevel(logging.WARNING) - - address = '::' - LOG.info('Starting %s health check server on %s:%d.', self.app_name, - address, self.port) - try: - self.application.run(address, self.port) - except Exception: - LOG.exception('Failed to start %s health check server.', - self.app_name) - raise - - def verify_k8s_connection(self): - k8s = clients.get_kubernetes_client() - try: - k8s.get('/healthz', json=False, headers={'Connection': 'close'}) - except Exception as e: - # Not LOG.exception to make sure long message from K8s API is not - # repeated. - LOG.error('Exception when trying to reach Kubernetes API: %s.', e) - return False - - return True diff --git a/kuryr_kubernetes/k8s_client.py b/kuryr_kubernetes/k8s_client.py deleted file mode 100644 index 25d4d8602..000000000 --- a/kuryr_kubernetes/k8s_client.py +++ /dev/null @@ -1,466 +0,0 @@ -# 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 contextlib -import datetime -import functools -import itertools -import os -import ssl -import time -from urllib import parse -import urllib3 - -from oslo_log import log as logging -from oslo_serialization import jsonutils -import requests -from requests import adapters - -from kuryr.lib._i18n import _ -from kuryr_kubernetes import config -from kuryr_kubernetes import constants -from kuryr_kubernetes import exceptions as exc -from kuryr_kubernetes import utils - -CONF = config.CONF -LOG = logging.getLogger(__name__) - - -class K8sClient(object): - # REVISIT(ivc): replace with python-k8sclient if it could be extended - # with 'WATCH' support - - def __init__(self, base_url): - self._base_url = base_url - cert_file = config.CONF.kubernetes.ssl_client_crt_file - key_file = config.CONF.kubernetes.ssl_client_key_file - ca_crt_file = config.CONF.kubernetes.ssl_ca_crt_file - self.verify_server = config.CONF.kubernetes.ssl_verify_server_crt - token_file = config.CONF.kubernetes.token_file - self.token = None - self.cert = (None, None) - self.are_events_enabled = config.CONF.kubernetes.use_events - - # Setting higher numbers regarding connection pools as we're running - # with max of 1000 green threads. - self.session = requests.Session() - prefix = '%s://' % parse.urlparse(base_url).scheme - self.session.mount(prefix, adapters.HTTPAdapter(pool_maxsize=1000)) - if token_file: - if os.path.exists(token_file): - with open(token_file, 'r') as f: - self.token = f.readline().rstrip('\n') - else: - raise RuntimeError( - _("Unable to find token_file : %s") % token_file) - else: - if cert_file and not os.path.exists(cert_file): - raise RuntimeError( - _("Unable to find ssl cert_file : %s") % cert_file) - if key_file and not os.path.exists(key_file): - raise RuntimeError( - _("Unable to find ssl key_file : %s") % key_file) - self.cert = (cert_file, key_file) - if self.verify_server: - if not ca_crt_file: - raise RuntimeError( - _("ssl_ca_crt_file cannot be None")) - elif not os.path.exists(ca_crt_file): - raise RuntimeError( - _("Unable to find ca cert_file : %s") % ca_crt_file) - else: - self.verify_server = ca_crt_file - - # Let's setup defaults for our Session. - self.session.cert = self.cert - self.session.verify = self.verify_server - if self.token: - self.session.headers['Authorization'] = f'Bearer {self.token}' - # NOTE(dulek): Seems like this is the only way to set is globally. - self.session.request = functools.partial( - self.session.request, timeout=( - CONF.kubernetes.watch_connection_timeout, - CONF.kubernetes.watch_read_timeout)) - - def _raise_from_response(self, response): - if response.status_code == requests.codes.not_found: - raise exc.K8sResourceNotFound(response.text) - if response.status_code == requests.codes.conflict: - raise exc.K8sConflict(response.text) - if response.status_code == requests.codes.forbidden: - if 'because it is being terminated' in response.json()['message']: - raise exc.K8sNamespaceTerminating(response.text) - raise exc.K8sForbidden(response.text) - if response.status_code == requests.codes.unprocessable_entity: - # NOTE(gryf): on k8s API code 422 is also Forbidden, but specified - # to FieldValueForbidden. Perhaps there are other usages for - # throwing unprocessable entity errors in different cases. - if ('FieldValueForbidden' in response.text and - 'Forbidden' in response.json()['message']): - raise exc.K8sFieldValueForbidden(response.text) - raise exc.K8sUnprocessableEntity(response.text) - if not response.ok: - raise exc.K8sClientException(response.text) - - def get(self, path, json=True, headers=None): - LOG.debug("Get %(path)s", {'path': path}) - url = self._base_url + path - response = self.session.get(url, headers=headers) - self._raise_from_response(response) - - if json: - result = response.json() - kind = result['kind'] - - api_version = result.get('apiVersion') - if not api_version: - api_version = utils.get_api_ver(path) - - # Strip List from e.g. PodList. For some reason `.items` of a list - # returned from API doesn't have `kind` set. - # NOTE(gryf): Also, for the sake of calculating selfLink - # equivalent, we need to have both: kind and apiVersion, while the - # latter is not present on items list for core resources, while - # for custom resources there are both kind and apiVersion.. - if kind.endswith('List'): - kind = kind[:-4] - - # NOTE(gryf): In case we get null/None for items from the API, - # we need to convert it to the empty list, otherwise it might - # be propagated to the consumers of this method and sent back - # to the Kubernetes as is, and fail as a result. - if result['items'] is None: - result['items'] = [] - - for item in result['items']: - if not item.get('kind'): - item['kind'] = kind - if not item.get('apiVersion'): - item['apiVersion'] = api_version - - if not result.get('apiVersion'): - result['apiVersion'] = api_version - else: - result = response.text - - return result - - def _get_url_and_header(self, path, content_type): - url = self._base_url + path - header = {'Content-Type': content_type, - 'Accept': 'application/json'} - - return url, header - - def patch(self, field, path, data): - LOG.debug("Patch %(path)s: %(data)s", {'path': path, 'data': data}) - content_type = 'application/merge-patch+json' - url, header = self._get_url_and_header(path, content_type) - response = self.session.patch(url, json={field: data}, headers=header) - self._raise_from_response(response) - return response.json().get('status') - - def patch_crd(self, field, path, data, action='replace'): - content_type = 'application/json-patch+json' - url, header = self._get_url_and_header(path, content_type) - - if action == 'remove': - data = [{'op': action, - 'path': f'/{field}/{data}'}] - else: - if data: - data = [{'op': action, - 'path': f'/{field}/{crd_field}', - 'value': value} - for crd_field, value in data.items()] - else: - data = [{'op': action, - 'path': f'/{field}', - 'value': data}] - - LOG.debug("Patch %(path)s: %(data)s", { - 'path': path, 'data': data}) - - response = self.session.patch(url, data=jsonutils.dumps(data), - headers=header) - self._raise_from_response(response) - return response.json().get('status') - - def post(self, path, body): - LOG.debug("Post %(path)s: %(body)s", {'path': path, 'body': body}) - url = self._base_url + path - header = {'Content-Type': 'application/json'} - - response = self.session.post(url, json=body, headers=header) - self._raise_from_response(response) - return response.json() - - def delete(self, path): - LOG.debug("Delete %(path)s", {'path': path}) - url = self._base_url + path - header = {'Content-Type': 'application/json'} - - response = self.session.delete(url, headers=header) - self._raise_from_response(response) - return response.json() - - # TODO(dulek): add_finalizer() and remove_finalizer() have some code - # duplication, but I don't see a nice way to avoid it. - def add_finalizer(self, obj, finalizer): - if finalizer in obj['metadata'].get('finalizers', []): - return True - - path = utils.get_res_link(obj) - LOG.debug(f"Add finalizer {finalizer} to {path}") - url, headers = self._get_url_and_header( - path, 'application/merge-patch+json') - - for i in range(3): # Let's make sure it's not infinite loop - finalizers = obj['metadata'].get('finalizers', []).copy() - finalizers.append(finalizer) - - data = { - 'metadata': { - 'finalizers': finalizers, - 'resourceVersion': obj['metadata']['resourceVersion'], - }, - } - - response = self.session.patch(url, json=data, headers=headers) - - if response.ok: - return True - - try: - self._raise_from_response(response) - except (exc.K8sFieldValueForbidden, exc.K8sResourceNotFound): - # Object is being deleting or gone. Return. - return False - except exc.K8sConflict: - try: - obj = self.get(path) - except exc.K8sResourceNotFound: - # Object got removed before finalizer was set - return False - if finalizer in obj['metadata'].get('finalizers', []): - # Finalizer is there, return. - return True - - # If after 3 iterations there's still conflict, just raise. - self._raise_from_response(response) - - def remove_finalizer(self, obj, finalizer): - path = utils.get_res_link(obj) - LOG.debug(f"Remove finalizer {finalizer} from {path}") - url, headers = self._get_url_and_header( - path, 'application/merge-patch+json') - - for i in range(3): # Let's make sure it's not infinite loop - finalizers = obj['metadata'].get('finalizers', []).copy() - try: - finalizers.remove(finalizer) - except ValueError: - # Finalizer is not there, return. - return True - - data = { - 'metadata': { - 'finalizers': finalizers, - 'resourceVersion': obj['metadata']['resourceVersion'], - }, - } - - response = self.session.patch(url, json=data, headers=headers) - - if response.ok: - return True - - try: - try: - self._raise_from_response(response) - except exc.K8sConflict: - obj = self.get(path) - except (exc.K8sFieldValueForbidden, exc.K8sResourceNotFound): - # Object is being deleted or gone already, stop. - return False - - # If after 3 iterations there's still conflict, just raise. - self._raise_from_response(response) - - def get_loadbalancer_crd(self, obj): - name = obj['metadata']['name'] - namespace = obj['metadata']['namespace'] - - try: - crd = self.get('{}/{}/kuryrloadbalancers/{}'.format( - constants.K8S_API_CRD_NAMESPACES, namespace, - name)) - except exc.K8sResourceNotFound: - return None - except exc.K8sClientException: - LOG.exception("Kubernetes Client Exception.") - raise - return crd - - def annotate(self, path, annotations, resource_version=None): - """Pushes a resource annotation to the K8s API resource - - The annotate operation is made with a PATCH HTTP request of kind: - application/merge-patch+json as described in: - - https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#patch-operations # noqa - """ - LOG.debug("Annotate %(path)s: %(names)s", { - 'path': path, 'names': list(annotations)}) - - content_type = 'application/merge-patch+json' - url, header = self._get_url_and_header(path, content_type) - - while itertools.count(1): - metadata = {"annotations": annotations} - if resource_version: - metadata['resourceVersion'] = resource_version - data = jsonutils.dumps({"metadata": metadata}, sort_keys=True) - response = self.session.patch(url, data=data, headers=header) - if response.ok: - return response.json()['metadata'].get('annotations', {}) - if response.status_code == requests.codes.conflict: - resource = self.get(path) - new_version = resource['metadata']['resourceVersion'] - retrieved_annotations = resource['metadata'].get( - 'annotations', {}) - - for k, v in annotations.items(): - if v != retrieved_annotations.get(k): - break - else: - LOG.debug("Annotations for %(path)s already present: " - "%(names)s", {'path': path, - 'names': retrieved_annotations}) - return retrieved_annotations - # Retry patching with updated resourceVersion - resource_version = new_version - continue - - LOG.error("Exception response, headers: %(headers)s, " - "content: %(content)s, text: %(text)s" - % {'headers': response.headers, - 'content': response.content, 'text': response.text}) - - self._raise_from_response(response) - - def watch(self, path): - url = self._base_url + path - resource_version = None - - attempt = 0 - while True: - try: - params = {'watch': 'true'} - if resource_version: - params['resourceVersion'] = resource_version - with contextlib.closing( - self.session.get( - url, params=params, stream=True)) as response: - if not response.ok: - raise exc.K8sClientException(response.text) - attempt = 0 - for line in response.iter_lines(): - line = line.decode('utf-8').strip() - if line: - line_dict = jsonutils.loads(line) - yield line_dict - # Saving the resourceVersion in case of a restart. - # At this point it's safely passed to handler. - m = line_dict.get('object', {}).get('metadata', {}) - resource_version = m.get('resourceVersion', None) - except (requests.ReadTimeout, requests.ConnectionError, - ssl.SSLError, requests.exceptions.ChunkedEncodingError, - urllib3.exceptions.SSLError): - t = utils.exponential_backoff(attempt) - log = LOG.debug - if attempt > 0: - # Only make it a warning if it's happening again, no need - # to inform about all the read timeouts. - log = LOG.warning - log('Connection error when watching %s. Retrying in %ds with ' - 'resourceVersion=%s', path, t, - params.get('resourceVersion')) - time.sleep(t) - attempt += 1 - - def add_event(self, resource, reason, message, type_='Normal', - component='kuryr-controller'): - """Create an Event object for the provided resource.""" - if not self.are_events_enabled: - return {} - - if not resource: - return {} - - involved_object = {'apiVersion': resource['apiVersion'], - 'kind': resource['kind'], - 'name': resource['metadata']['name'], - 'namespace': resource['metadata']['namespace'], - 'uid': resource['metadata']['uid']} - - # This is needed for Event date, otherwise LAST SEEN/Age will be empty - # and misleading. - now = datetime.datetime.utcnow().replace(tzinfo=datetime.timezone.utc) - date_time = now.strftime("%Y-%m-%dT%H:%M:%SZ") - - name = ".".join((resource['metadata']['name'], - self._get_hex_timestamp(now))) - - event = {'kind': 'Event', - 'apiVersion': 'v1', - 'firstTimestamp': date_time, - 'metadata': {'name': name}, - 'reason': reason, - 'message': message, - 'type': type_, - 'involvedObject': involved_object, - 'source': {'component': component, - 'host': utils.get_nodename()}} - - try: - return self.post(f'{constants.K8S_API_BASE}/namespaces/' - f'{resource["metadata"]["namespace"]}/events', - event) - except exc.K8sNamespaceTerminating: - # We can't create events in a Namespace that is being terminated, - # there's no workaround, no need to log it, just ignore it. - return {} - except exc.K8sClientException: - LOG.warning(f'There was non critical error during creating an ' - 'Event for resource: "{resource}", with reason: ' - f'"{reason}", message: "{message}" and type: ' - f'"{type_}"') - return {} - - def _get_hex_timestamp(self, datetimeobj): - """Get hex representation for timestamp. - - In Kuberenets, Event name is constructed name of the bounded object - and timestamp in hexadecimal representation. - Note, that Python timestamp is represented as floating figure: - 1631622163.8534190654754638671875 - while those which origin from K8s, after change to int: - 1631622163915909162 - so, to get similar integer, we need to multiply the float by - 100000000 to get the same precision and cast to integer, to get rid - of the fractures, and finally convert it to hex representation. - """ - timestamp = datetime.datetime.timestamp(datetimeobj) - return format(int(timestamp * 100000000), 'x') diff --git a/kuryr_kubernetes/linux_net_utils.py b/kuryr_kubernetes/linux_net_utils.py deleted file mode 100644 index 424e9bc56..000000000 --- a/kuryr_kubernetes/linux_net_utils.py +++ /dev/null @@ -1,57 +0,0 @@ -# Derived from nova/network/linux_net.py -# -# Copyright (c) 2011 X.commerce, a business unit of eBay Inc. -# Copyright 2010 United States Government as represented by the -# Administrator of the National Aeronautics and Space Administration. -# 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. - -""" Implements linux net utils""" - -from oslo_concurrency import processutils -from oslo_log import log as logging - -LOG = logging.getLogger(__name__) - - -def _ovs_vsctl(args, timeout=None): - full_args = ['ovs-vsctl'] - if timeout is not None: - full_args += ['--timeout=%s' % timeout] - full_args += args - try: - return processutils.execute(*full_args, run_as_root=True) - except Exception as e: - LOG.error("Unable to execute %(cmd)s. Exception: %(exception)s", - {'cmd': full_args, 'exception': e}) - raise - - -def _create_ovs_vif_cmd(bridge, dev, iface_id, mac, instance_id): - cmd = ['--', '--if-exists', 'del-port', dev, '--', - 'add-port', bridge, dev, - '--', 'set', 'Interface', dev, - 'external-ids:iface-id=%s' % iface_id, - 'external-ids:iface-status=active', - 'external-ids:attached-mac=%s' % mac, - 'external-ids:vm-uuid=%s' % instance_id] - return cmd - - -def create_ovs_vif_port(bridge, dev, iface_id, mac, instance_id): - _ovs_vsctl(_create_ovs_vif_cmd(bridge, dev, iface_id, mac, instance_id)) - - -def delete_ovs_vif_port(bridge, dev): - _ovs_vsctl(['--', '--if-exists', 'del-port', bridge, dev]) diff --git a/kuryr_kubernetes/objects/__init__.py b/kuryr_kubernetes/objects/__init__.py deleted file mode 100644 index 6d97e444d..000000000 --- a/kuryr_kubernetes/objects/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -def register_locally_defined_vifs(): - __import__('kuryr_kubernetes.objects.vif') diff --git a/kuryr_kubernetes/objects/base.py b/kuryr_kubernetes/objects/base.py deleted file mode 100644 index 75498d05e..000000000 --- a/kuryr_kubernetes/objects/base.py +++ /dev/null @@ -1,30 +0,0 @@ -# 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 - -from oslo_versionedobjects import base as obj_base - - -class KuryrK8sObjectBase(obj_base.VersionedObject, - obj_base.ComparableVersionedObject, - metaclass=abc.ABCMeta): - - OBJ_PROJECT_NAMESPACE = 'kuryr_kubernetes' - - def __init__(self, context=None, **kwargs): - super(KuryrK8sObjectBase, self).__init__(context, **kwargs) - self.obj_set_defaults() - self.obj_reset_changes() diff --git a/kuryr_kubernetes/objects/fields.py b/kuryr_kubernetes/objects/fields.py deleted file mode 100644 index df19295b3..000000000 --- a/kuryr_kubernetes/objects/fields.py +++ /dev/null @@ -1,26 +0,0 @@ -# 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. - -from os_vif.objects import vif -from oslo_versionedobjects import fields as obj_fields - - -class ListOfUUIDField(obj_fields.AutoTypedField): - AUTO_TYPE = obj_fields.List(obj_fields.UUID()) - - -class DictOfVIFsField(obj_fields.AutoTypedField): - AUTO_TYPE = obj_fields.Dict(obj_fields.Object(vif.VIFBase.__name__, - subclasses=True)) diff --git a/kuryr_kubernetes/objects/lbaas.py b/kuryr_kubernetes/objects/lbaas.py deleted file mode 100644 index 51f87633d..000000000 --- a/kuryr_kubernetes/objects/lbaas.py +++ /dev/null @@ -1,164 +0,0 @@ -# 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. - -from oslo_versionedobjects import base as obj_base -from oslo_versionedobjects import fields as obj_fields - -from kuryr_kubernetes.objects import base as k_obj -from kuryr_kubernetes.objects import fields as k_fields - - -@obj_base.VersionedObjectRegistry.register -class LBaaSLoadBalancer(k_obj.KuryrK8sObjectBase): - # Version 1.0: Initial version - # Version 1.1: Added provider field and security_groups field. - # Version 1.2: Added support for security_groups=None - # Version 1.3: Added support for provider=None - # Version 1.4: Added support for security_groups=[] - VERSION = '1.4' - - fields = { - 'id': obj_fields.UUIDField(), - 'project_id': obj_fields.StringField(), - 'name': obj_fields.StringField(), - 'ip': obj_fields.IPAddressField(), - 'subnet_id': obj_fields.UUIDField(), - 'port_id': obj_fields.UUIDField(), - 'provider': obj_fields.StringField(nullable=True, - default=None), - 'security_groups': k_fields.ListOfUUIDField(nullable=True, - default=[]), - } - - -@obj_base.VersionedObjectRegistry.register -class LBaaSListener(k_obj.KuryrK8sObjectBase): - VERSION = '1.0' - - fields = { - 'id': obj_fields.UUIDField(), - 'project_id': obj_fields.StringField(), - 'name': obj_fields.StringField(), - 'loadbalancer_id': obj_fields.UUIDField(), - 'protocol': obj_fields.StringField(), - 'port': obj_fields.IntegerField(), - } - - -@obj_base.VersionedObjectRegistry.register -class LBaaSPool(k_obj.KuryrK8sObjectBase): - # Version 1.0: Initial version - # Version 1.1: Added support for pool attached directly to loadbalancer. - VERSION = '1.1' - - fields = { - 'id': obj_fields.UUIDField(), - 'project_id': obj_fields.StringField(), - 'name': obj_fields.StringField(), - 'loadbalancer_id': obj_fields.UUIDField(), - 'listener_id': obj_fields.UUIDField(nullable=True), - 'protocol': obj_fields.StringField(), - } - - -@obj_base.VersionedObjectRegistry.register -class LBaaSMember(k_obj.KuryrK8sObjectBase): - VERSION = '1.0' - - fields = { - 'id': obj_fields.UUIDField(), - 'project_id': obj_fields.StringField(), - 'name': obj_fields.StringField(), - 'pool_id': obj_fields.UUIDField(), - 'subnet_id': obj_fields.UUIDField(), - 'ip': obj_fields.IPAddressField(), - 'port': obj_fields.IntegerField(), - } - - -@obj_base.VersionedObjectRegistry.register -class LBaaSPubIp(k_obj.KuryrK8sObjectBase): - VERSION = '1.0' - - fields = { - 'ip_id': obj_fields.UUIDField(), - 'ip_addr': obj_fields.IPAddressField(), - 'alloc_method': obj_fields.StringField(), - } - - -@obj_base.VersionedObjectRegistry.register -class LBaaSState(k_obj.KuryrK8sObjectBase): - VERSION = '1.0' - - fields = { - 'loadbalancer': obj_fields.ObjectField(LBaaSLoadBalancer.__name__, - nullable=True, - default=None), - 'listeners': obj_fields.ListOfObjectsField(LBaaSListener.__name__, - default=[]), - 'pools': obj_fields.ListOfObjectsField(LBaaSPool.__name__, - default=[]), - 'members': obj_fields.ListOfObjectsField(LBaaSMember.__name__, - default=[]), - 'service_pub_ip_info': obj_fields.ObjectField(LBaaSPubIp.__name__, - nullable=True, - default=None), - } - - -@obj_base.VersionedObjectRegistry.register -class LBaaSPortSpec(k_obj.KuryrK8sObjectBase): - VERSION = '1.1' - # Version 1.0: Initial version - # Version 1.1: Added targetPort field. - - fields = { - 'name': obj_fields.StringField(nullable=True), - 'protocol': obj_fields.StringField(), - 'port': obj_fields.IntegerField(), - 'targetPort': obj_fields.StringField(), - } - - -@obj_base.VersionedObjectRegistry.register -class LBaaSServiceSpec(k_obj.KuryrK8sObjectBase): - VERSION = '1.0' - - fields = { - 'ip': obj_fields.IPAddressField(nullable=True, default=None), - 'ports': obj_fields.ListOfObjectsField(LBaaSPortSpec.__name__, - default=[]), - 'project_id': obj_fields.StringField(nullable=True, default=None), - 'subnet_id': obj_fields.UUIDField(nullable=True, default=None), - 'security_groups_ids': k_fields.ListOfUUIDField(default=[]), - 'type': obj_fields.StringField(nullable=True, default=None), - 'lb_ip': obj_fields.IPAddressField(nullable=True, default=None), - } - - -def flatten_object(ovo_primitive): - if type(ovo_primitive) is dict: - d = {} - for k, v in ovo_primitive['versioned_object.data'].items(): - d[k] = flatten_object(v) - return d - elif type(ovo_primitive) is list: - ls = [] - for v in ovo_primitive: - ls.append(flatten_object(v)) - return ls - else: - return ovo_primitive diff --git a/kuryr_kubernetes/objects/vif.py b/kuryr_kubernetes/objects/vif.py deleted file mode 100644 index 92b9a9ea2..000000000 --- a/kuryr_kubernetes/objects/vif.py +++ /dev/null @@ -1,82 +0,0 @@ -# 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_versionedobjects import base as obj_base -from oslo_versionedobjects import fields as obj_fields - -from os_vif.objects import vif as obj_osvif - -from kuryr_kubernetes import constants -from kuryr_kubernetes.objects import base -from kuryr_kubernetes.objects import fields - - -@obj_base.VersionedObjectRegistry.register -class PodState(base.KuryrK8sObjectBase): - VERSION = '1.0' - - # FIXME(dulek): I know it's an ugly hack, but turns out you cannot - # serialize-deserialize objects containing objects from - # different namespaces, so we need 'os_vif' namespace here. - OBJ_PROJECT_NAMESPACE = 'os_vif' - - fields = { - 'default_vif': obj_fields.ObjectField(obj_osvif.VIFBase.__name__, - subclasses=True, nullable=False), - 'additional_vifs': fields.DictOfVIFsField(default={}), - } - - @property - def vifs(self): - d = { - constants.DEFAULT_IFNAME: self.default_vif, - } - d.update(self.additional_vifs) - return d - - -@obj_base.VersionedObjectRegistry.register -class VIFVlanNested(obj_osvif.VIFBase): - # This is OVO based vlan vif. - - VERSION = '1.0' - - fields = { - # Name of the device to create - 'vif_name': obj_fields.StringField(), - # vlan ID allocated to this vif - 'vlan_id': obj_fields.IntegerField() - } - - -@obj_base.VersionedObjectRegistry.register -class VIFMacvlanNested(obj_osvif.VIFBase): - # This is OVO based macvlan vif. - - VERSION = '1.0' - - fields = { - # Name of the device to create - 'vif_name': obj_fields.StringField(), - } - - -@obj_base.VersionedObjectRegistry.register -class VIFDPDKNested(obj_osvif.VIFNestedDPDK): - # This is OVO based DPDK Nested vif. - - VERSION = '1.0' - - fields = { - # name of the VIF - 'vif_name': obj_fields.StringField(), - } diff --git a/kuryr_kubernetes/opts.py b/kuryr_kubernetes/opts.py deleted file mode 100644 index 1b02ea39a..000000000 --- a/kuryr_kubernetes/opts.py +++ /dev/null @@ -1,61 +0,0 @@ -# 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 copy - -from oslo_log import _options - -from kuryr.lib import opts as lib_opts -from kuryr_kubernetes.cni import health as cni_health -from kuryr_kubernetes import config -from kuryr_kubernetes.controller.drivers import namespace_subnet -from kuryr_kubernetes.controller.drivers import vif_pool -from kuryr_kubernetes.controller.managers import health -from kuryr_kubernetes.controller.managers import pool -from kuryr_kubernetes import utils - -_kuryr_k8s_opts = [ - ('kubernetes', config.k8s_opts), - ('kuryr-kubernetes', config.kuryr_k8s_opts), - ('neutron_defaults', config.neutron_defaults), - ('pod_vif_nested', config.nested_vif_driver_opts), - ('vif_pool', vif_pool.vif_pool_driver_opts), - ('octavia_defaults', config.octavia_defaults), - ('cache_defaults', config.cache_defaults), - ('subnet_caching', utils.subnet_caching_opts), - ('node_driver_caching', vif_pool.node_vif_driver_caching_opts), - ('pool_manager', pool.pool_manager_opts), - ('cni_daemon', config.daemon_opts), - ('health_server', health.health_server_opts), - ('cni_health_server', cni_health.cni_health_server_opts), - ('namespace_subnet', namespace_subnet.namespace_subnet_driver_opts), -] - - -def list_kuryr_opts(): - """Return a list of oslo_config options available in Kuryr service. - - Each element of the list is a tuple. The first element is the name of the - group under which the list of elements in the second element will be - registered. A group name of None corresponds to the [DEFAULT] group in - config files. - - This function is also discoverable via the 'kuryr' entry point under - the 'oslo_config.opts' namespace. - - The purpose of this is to allow tools like the Oslo sample config file - generator to discover the options exposed to users by Kuryr. - - :returns: a list of (group_name, opts) tuples - """ - - return ([(k, copy.deepcopy(o)) for k, o in _kuryr_k8s_opts] + - lib_opts.list_kuryr_opts() + _options.list_opts()) diff --git a/kuryr_kubernetes/os_vif_plug_noop.py b/kuryr_kubernetes/os_vif_plug_noop.py deleted file mode 100644 index 4690ef0db..000000000 --- a/kuryr_kubernetes/os_vif_plug_noop.py +++ /dev/null @@ -1,44 +0,0 @@ -# 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 os_vif import objects -from os_vif.plugin import PluginBase - -from kuryr_kubernetes.objects import vif as k_vif - - -class NoOpPlugin(PluginBase): - """No Op Plugin to be used with VIF types that dont need plugging""" - - def describe(self): - return objects.host_info.HostPluginInfo( - plugin_name='noop', - vif_info=[ - objects.host_info.HostVIFInfo( - vif_object_name=k_vif.VIFVlanNested.__name__, - min_version="1.0", - max_version="1.0"), - objects.host_info.HostVIFInfo( - vif_object_name=k_vif.VIFMacvlanNested.__name__, - min_version="1.0", - max_version="1.0"), - objects.host_info.HostVIFInfo( - vif_object_name=k_vif.VIFDPDKNested.__name__, - min_version="1.0", - max_version="1.0"), - ]) - - def plug(self, vif, instance_info): - pass - - def unplug(self, vif, instance_info): - pass diff --git a/kuryr_kubernetes/os_vif_util.py b/kuryr_kubernetes/os_vif_util.py deleted file mode 100644 index fa2f616ab..000000000 --- a/kuryr_kubernetes/os_vif_util.py +++ /dev/null @@ -1,432 +0,0 @@ -# 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 os - -from kuryr.lib._i18n import _ -from kuryr.lib.binding.drivers import utils as kl_utils -from kuryr.lib import constants as kl_const -from os_vif.objects import fixed_ip as osv_fixed_ip -from os_vif.objects import network as osv_network -from os_vif.objects import route as osv_route -from os_vif.objects import subnet as osv_subnet -from os_vif.objects import vif as osv_vif -from oslo_config import cfg as oslo_cfg -from oslo_log import log as logging -from stevedore import driver as stv_driver -from vif_plug_ovs import constants as osv_const - -from kuryr_kubernetes import config -from kuryr_kubernetes import constants as const -from kuryr_kubernetes import exceptions as k_exc -from kuryr_kubernetes.objects import vif as k_vif -from kuryr_kubernetes import utils - - -LOG = logging.getLogger(__name__) - -# REVISIT(ivc): consider making this module part of kuryr-lib -_VIF_TRANSLATOR_NAMESPACE = "kuryr_kubernetes.vif_translators" -_VIF_MANAGERS = {} - - -def neutron_to_osvif_network(os_network): - """Converts Neutron network to os-vif Subnet. - - :param os_network: openstack.network.v2.netwrork.Network object. - :return: an os-vif Network object - """ - - obj = osv_network.Network(id=os_network.id) - - if os_network.name is not None: - obj.label = os_network.name - - if os_network.mtu is not None: - obj.mtu = os_network.mtu - - # Vlan information will be used later in Sriov binding driver - if os_network.provider_network_type == 'vlan': - obj.should_provide_vlan = True - obj.vlan = os_network.provider_segmentation_id - - return obj - - -def neutron_to_osvif_subnet(os_subnet): - """Converts Neutron subnet to os-vif Subnet. - - :param os_subnet: openstack.network.v2.subnet.Subnet object - :return: an os-vif Subnet object - """ - - obj = osv_subnet.Subnet( - cidr=os_subnet.cidr, - dns=os_subnet.dns_nameservers, - routes=_neutron_to_osvif_routes(os_subnet.host_routes)) - - if os_subnet.gateway_ip is not None: - obj.gateway = os_subnet.gateway_ip - - return obj - - -def _neutron_to_osvif_routes(neutron_routes): - """Converts Neutron host_routes to os-vif RouteList. - - :param neutron_routes: list of routes as returned by neutron client's - 'show_subnet' in 'host_routes' attribute - :return: an os-vif RouteList object - """ - - # NOTE(gryf): Nested attributes for OpenStackSDK objects are simple types, - # like dicts and lists, that's why neutron_routes is a list of dicts. - obj_list = [osv_route.Route(cidr=route['destination'], - gateway=route['nexthop']) - for route in neutron_routes] - - return osv_route.RouteList(objects=obj_list) - - -def _make_vif_subnet(subnets, subnet_id): - """Makes a copy of an os-vif Subnet from subnets mapping. - - :param subnets: subnet mapping as returned by PodSubnetsDriver.get_subnets - :param subnet_id: ID of the subnet to extract from 'subnets' mapping - :return: a copy of an os-vif Subnet object matching 'subnet_id' - """ - - network = subnets[subnet_id] - - if len(network.subnets.objects) != 1: - raise k_exc.IntegrityError(_( - "Network object for subnet %(subnet_id)s is invalid, " - "must contain a single subnet, but %(num_subnets)s found") % { - 'subnet_id': subnet_id, - 'num_subnets': len(network.subnets.objects)}) - - subnet = network.subnets.objects[0].obj_clone() - subnet.ips = osv_fixed_ip.FixedIPList(objects=[]) - return subnet - - -def _make_vif_subnets(neutron_port, subnets): - """Gets a list of os-vif Subnet objects for port. - - :param neutron_port: dict containing port information as returned by - neutron client's 'show_port' or - openstack.network.v2.port.Port object - :param subnets: subnet mapping as returned by PodSubnetsDriver.get_subnets - :return: list of os-vif Subnet object - """ - - vif_subnets = {} - try: - fixed_ips = neutron_port.get('fixed_ips', []) - port_id = neutron_port.get('id') - except TypeError: - fixed_ips = neutron_port.fixed_ips - port_id = neutron_port.get.id - - for neutron_fixed_ip in fixed_ips: - subnet_id = neutron_fixed_ip['subnet_id'] - ip_address = neutron_fixed_ip['ip_address'] - - if subnet_id not in subnets: - continue - - try: - subnet = vif_subnets[subnet_id] - except KeyError: - subnet = _make_vif_subnet(subnets, subnet_id) - vif_subnets[subnet_id] = subnet - - subnet.ips.objects.append(osv_fixed_ip.FixedIP(address=ip_address)) - - if not vif_subnets: - raise k_exc.IntegrityError(_( - "No valid subnets found for port %(port_id)s") % { - 'port_id': port_id}) - - return list(vif_subnets.values()) - - -def _make_vif_network(neutron_port, subnets): - """Get an os-vif Network object for port. - - :param neutron_port: dict containing port information as returned by - neutron client's 'show_port', or - openstack.network.v2.port.Port object - :param subnets: subnet mapping as returned by PodSubnetsDriver.get_subnets - :return: os-vif Network object - """ - - # NOTE(gryf): Because we didn't convert macvlan driver, neutron_port can - # be either a dict or an object - try: - network_id = neutron_port.get('network_id') - port_id = neutron_port.get('id') - except TypeError: - network_id = neutron_port.network_id - port_id = neutron_port.id - - try: - network = next(net.obj_clone() for net in subnets.values() - if net.id == network_id) - except StopIteration: - raise k_exc.IntegrityError(_( - "Port %(port_id)s belongs to network %(network_id)s, " - "but requested networks are: %(requested_networks)s") % { - 'port_id': port_id, - 'network_id': network_id, - 'requested_networks': [net.id for net in subnets.values()]}) - - network.subnets = osv_subnet.SubnetList( - objects=_make_vif_subnets(neutron_port, subnets)) - - return network - - -# TODO(a.perevalov) generalize it with get_veth_pair_names -# but it's reasonable if we're going to add vhostuser support -# into kuryr project -def _get_vhu_vif_name(port_id): - ifname = osv_const.OVS_VHOSTUSER_PREFIX + port_id - ifname = ifname[:kl_const.NIC_NAME_LEN] - return ifname - - -def _get_vif_name(neutron_port): - """Gets a VIF device name for port. - - :param neutron_port: dict containing port information as returned by - neutron client's 'show_port', or an port object - returned by openstack client. - """ - - try: - port_id = neutron_port['id'] - except TypeError: - port_id = neutron_port.id - - vif_name, _ = kl_utils.get_veth_pair_names(port_id) - return vif_name - - -def _get_ovs_hybrid_bridge_name(os_port): - """Gets a name of the Linux bridge name for hybrid OpenVSwitch port. - - :param os_port: openstack.network.v2.port.Port object - """ - return ('qbr' + os_port.id)[:kl_const.NIC_NAME_LEN] - - -def _is_port_active(neutron_port): - """Checks if port is active. - - :param neutron_port: dict containing port information as returned by - neutron client's 'show_port' or - openstack.network.v2.port.Port object - """ - try: - return (neutron_port['status'] == kl_const.PORT_STATUS_ACTIVE) - except TypeError: - return (neutron_port.status == kl_const.PORT_STATUS_ACTIVE) - - -def neutron_to_osvif_vif_ovs(vif_plugin, os_port, subnets): - """Converts Neutron port to VIF object for os-vif 'ovs' plugin. - - :param vif_plugin: name of the os-vif plugin to use (i.e. 'ovs') - :param os_port: openstack.network.v2.port.Port object - :param subnets: subnet mapping as returned by PodSubnetsDriver.get_subnets - :return: os-vif VIF object - """ - profile = osv_vif.VIFPortProfileOpenVSwitch(interface_id=os_port.id) - - details = os_port.binding_vif_details or {} - ovs_bridge = details.get('bridge_name', - config.CONF.neutron_defaults.ovs_bridge) - if not ovs_bridge: - raise oslo_cfg.RequiredOptError('ovs_bridge', 'neutron_defaults') - - network = _make_vif_network(os_port, subnets) - network.bridge = ovs_bridge - vhostuser_mode = details.get('vhostuser_mode', False) - - LOG.debug('Detected vhostuser_mode=%s for port %s', vhostuser_mode, - os_port.id) - if vhostuser_mode: - # TODO(a.perevalov) obtain path to mount point from pod's mountVolumes - vhostuser_mount_point = (config.CONF.vhostuser.mount_point) - if not vhostuser_mount_point: - raise oslo_cfg.RequiredOptError('vhostuser_mount_point', - 'neutron_defaults') - vif_name = _get_vhu_vif_name(os_port.id) - vif = osv_vif.VIFVHostUser( - id=os_port.id, - address=os_port.mac_address, - network=network, - has_traffic_filtering=details.get('port_filter', False), - preserve_on_delete=False, - active=_is_port_active(os_port), - port_profile=profile, - plugin='ovs', - path=os.path.join(vhostuser_mount_point, vif_name), - mode=vhostuser_mode, - vif_name=vif_name, - bridge_name=network.bridge) - elif details.get('ovs_hybrid_plug'): - vif = osv_vif.VIFBridge( - id=os_port.id, - address=os_port.mac_address, - network=network, - has_traffic_filtering=details.get('port_filter', False), - preserve_on_delete=False, - active=_is_port_active(os_port), - port_profile=profile, - plugin=vif_plugin, - vif_name=_get_vif_name(os_port), - bridge_name=_get_ovs_hybrid_bridge_name(os_port)) - else: - vif = osv_vif.VIFOpenVSwitch( - id=os_port.id, - address=os_port.mac_address, - network=network, - has_traffic_filtering=details.get('port_filter', False), - preserve_on_delete=False, - active=_is_port_active(os_port), - port_profile=profile, - plugin=vif_plugin, - vif_name=_get_vif_name(os_port), - bridge_name=network.bridge) - - return vif - - -def neutron_to_osvif_vif_nested_vlan(os_port, subnets, vlan_id): - """Converts Neutron port to VIF object for VLAN nested containers. - - :param os_port: openstack.network.v2.port.Port object - :param subnets: subnet mapping as returned by PodSubnetsDriver.get_subnets - :param vlan_id: VLAN id associated to the VIF object for the pod - :return: kuryr-k8s native VIF object for VLAN nested - """ - details = os_port.binding_vif_details or {} - - return k_vif.VIFVlanNested( - id=os_port.id, - address=os_port.mac_address, - network=_make_vif_network(os_port, subnets), - has_traffic_filtering=details.get('port_filter', False), - preserve_on_delete=False, - active=_is_port_active(os_port), - plugin=const.K8S_OS_VIF_NOOP_PLUGIN, - vif_name=_get_vif_name(os_port), - vlan_id=vlan_id) - - -def neutron_to_osvif_vif_nested_macvlan(neutron_port, subnets): - """Converts Neutron port to VIF object for MACVLAN nested containers. - - :param neutron_port: dict containing port information as returned by - neutron client's 'show_port' - :param subnets: subnet mapping as returned by PodSubnetsDriver.get_subnets - :return: kuryr-k8s native VIF object for MACVLAN nested - """ - details = neutron_port.get('binding:vif_details', {}) - - return k_vif.VIFMacvlanNested( - id=neutron_port['id'], - address=neutron_port['mac_address'], - network=_make_vif_network(neutron_port, subnets), - has_traffic_filtering=details.get('port_filter', False), - preserve_on_delete=False, - active=_is_port_active(neutron_port), - plugin=const.K8S_OS_VIF_NOOP_PLUGIN, - vif_name=_get_vif_name(neutron_port)) - - -def neutron_to_osvif_vif_dpdk(os_port, subnets, pod): - """Converts Neutron port to VIF object for nested dpdk containers. - - :param os_port: dict containing port information as returned by - neutron client's 'show_port' - :param subnets: subnet mapping as returned by PodSubnetsDriver.get_subnets - :param pod: pod object received by k8s and containing profile details - :return: os-vif VIF object - """ - - details = os_port.get('binding:vif_details', {}) - profile = osv_vif.VIFPortProfileK8sDPDK( - l3_setup=False, - selflink=utils.get_res_link(pod)) - - return k_vif.VIFDPDKNested( - id=os_port['id'], - port_profile=profile, - address=os_port['mac_address'], - network=_make_vif_network(os_port, subnets), - has_traffic_filtering=details.get('port_filter', False), - preserve_on_delete=False, - active=_is_port_active(os_port), - plugin=const.K8S_OS_VIF_NOOP_PLUGIN, - pci_address="", - dev_driver="", - vif_name=_get_vif_name(os_port)) - - -def neutron_to_osvif_vif(vif_translator, os_port, subnets): - """Converts Neutron port to os-vif VIF object. - - :param vif_translator: name of the traslator for the os-vif plugin to use - :param os_port: openstack.network.v2.port.Port object - :param subnets: subnet mapping as returned by PodSubnetsDriver.get_subnets - :return: os-vif VIF object - """ - try: - mgr = _VIF_MANAGERS[vif_translator] - except KeyError: - mgr = stv_driver.DriverManager( - namespace=_VIF_TRANSLATOR_NAMESPACE, - name=vif_translator, invoke_on_load=False) - _VIF_MANAGERS[vif_translator] = mgr - - return mgr.driver(vif_translator, os_port, subnets) - - -def osvif_to_neutron_fixed_ips(subnets): - fixed_ips = [] - - for subnet_id, network in subnets.items(): - ips = [] - if len(network.subnets.objects) > 1: - raise k_exc.IntegrityError(_( - "Network object for subnet %(subnet_id)s is invalid, " - "must contain a single subnet, but %(num_subnets)s found") % { - 'subnet_id': subnet_id, - 'num_subnets': len(network.subnets.objects)}) - - for subnet in network.subnets.objects: - if subnet.obj_attr_is_set('ips'): - ips.extend([str(ip.address) for ip in subnet.ips.objects]) - if ips: - fixed_ips.extend([{'subnet_id': subnet_id, 'ip_address': ip} - for ip in ips]) - else: - fixed_ips.append({'subnet_id': subnet_id}) - - return fixed_ips diff --git a/kuryr_kubernetes/tests/__init__.py b/kuryr_kubernetes/tests/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/kuryr_kubernetes/tests/base.py b/kuryr_kubernetes/tests/base.py deleted file mode 100644 index b84e00c0d..000000000 --- a/kuryr_kubernetes/tests/base.py +++ /dev/null @@ -1,24 +0,0 @@ -# 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 kuryr_kubernetes import config -import os_vif -from oslotest import base - - -class TestCase(base.BaseTestCase): - - """Test case base class for all unit tests.""" - def setUp(self): - super(TestCase, self).setUp() - args = [] - config.init(args=args) - os_vif.initialize() diff --git a/kuryr_kubernetes/tests/fake.py b/kuryr_kubernetes/tests/fake.py deleted file mode 100644 index 57e00ea31..000000000 --- a/kuryr_kubernetes/tests/fake.py +++ /dev/null @@ -1,182 +0,0 @@ -# Copyright (c) 2017 Red Hat. -# 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 uuid - -from openstack.network.v2 import port as os_port -from openstack.network.v2 import security_group_rule as os_sgr -from os_vif import objects as osv_objects -from os_vif.objects import vif as osv_vif -from oslo_serialization import jsonutils - -from kuryr_kubernetes import constants - - -def _fake_vif(cls=osv_vif.VIFOpenVSwitch): - vif = cls( - id=uuid.uuid4(), - vif_name='h_interface', - bridge_name='bridge', - address='3e:94:b7:31:a0:83', - port_profile=osv_objects.vif.VIFPortProfileOpenVSwitch( - interface_id='89eccd45-43e9-43d8-b4cc-4c13db13f782', - profile_id=str(uuid.uuid4()), - ), - ) - vif.network = osv_objects.network.Network(id=uuid.uuid4(), mtu=1) - subnet = osv_objects.subnet.Subnet( - uuid=uuid.uuid4(), - dns=['192.168.0.1'], - cidr='192.168.0.0/24', - gateway='192.168.0.1', - routes=osv_objects.route.RouteList(objects=[]), - ) - subnet.ips = osv_objects.fixed_ip.FixedIPList(objects=[]) - subnet.ips.objects.append( - osv_objects.fixed_ip.FixedIP(address='192.168.0.2')) - vif.network.subnets.objects.append(subnet) - vif.active = True - return vif - - -def _fake_vif_dict(obj=None): - if obj: - return obj.obj_to_primitive() - else: - return _fake_vif().obj_to_primitive() - - -def _fake_vif_string(dictionary=None): - if dictionary: - return jsonutils.dumps(dictionary) - else: - return jsonutils.dumps(_fake_vif_dict()) - - -def _fake_vifs(cls=osv_vif.VIFOpenVSwitch, prefix='eth'): - return {'eth0': _fake_vif(cls), prefix+'1': _fake_vif(cls)} - - -def _fake_vifs_dict(obj=None): - if obj: - return { - ifname: vif.obj_to_primitive() for - ifname, vif in obj.items() - } - else: - return { - ifname: vif.obj_to_primitive() for - ifname, vif in _fake_vifs().items() - } - - -def _fake_vifs_string(dictionary=None): - if dictionary: - return jsonutils.dumps(dictionary) - else: - return jsonutils.dumps(_fake_vifs_dict()) - - -def get_port_obj(port_id='07cfe856-11cc-43d9-9200-ff4dc02d3620', - device_owner='compute:kuryr', ip_address=None, - vif_details=None, **kwargs): - - fixed_ips = [{'subnet_id': 'e1942bb1-5f51-4646-9885-365b66215592', - 'ip_address': '10.10.0.5'}, - {'subnet_id': '4894baaf-df06-4a54-9885-9cd99d1cc245', - 'ip_address': 'fd35:7db5:e3fc:0:f816:3eff:fe80:d421'}] - if ip_address: - fixed_ips[0]['ip_address'] = ip_address - security_group_ids = ['cfb3dfc4-7a43-4ba1-b92d-b8b2650d7f88'] - - if not vif_details: - vif_details = {'port_filter': True, 'ovs_hybrid_plug': False} - - port_data = {'allowed_address_pairs': [], - 'binding_host_id': 'kuryr-devstack', - 'binding_profile': {}, - 'binding_vif_details': vif_details, - 'binding_vif_type': 'ovs', - 'binding_vnic_type': 'normal', - 'created_at': '2017-06-09T13:23:24Z', - 'data_plane_status': None, - 'description': '', - 'device_id': '', - 'device_owner': device_owner, - 'dns_assignment': None, - 'dns_domain': None, - 'dns_name': None, - 'extra_dhcp_opts': [], - 'fixed_ips': fixed_ips, - 'id': port_id, - 'ip_address': None, - 'is_admin_state_up': True, - 'is_port_security_enabled': True, - 'location': None, - 'mac_address': 'fa:16:3e:80:d4:21', - 'name': constants.KURYR_PORT_NAME, - 'network_id': 'ba44f957-c467-412b-b985-ae720514bc46', - 'option_name': None, - 'option_value': None, - 'project_id': 'b6e8fb2bde594673923afc19cf168f3a', - 'qos_policy_id': None, - 'revision_number': 9, - 'security_group_ids': security_group_ids, - 'status': u'DOWN', - 'subnet_id': None, - 'tags': [], - 'trunk_details': None, - 'updated_at': u'2019-12-04T15:06:09Z'} - port_data.update(kwargs) - return os_port.Port(**port_data) - - -def get_sgr_obj(sgr_id='7621d1e0-a2d2-4496-94eb-ffd375d20877', - sg_id='cfb3dfc4-7a43-4ba1-b92d-b8b2650d7f88', - protocol='tcp', direction='ingress'): - - sgr_data = {'description': '', - 'direction': direction, - 'ether_type': 'IPv4', - 'id': sgr_id, - 'port_range_max': 8080, - 'port_range_min': 8080, - 'project_id': '5ea46368c7fe436bb8732738c149fbce', - 'protocol': protocol, - 'remote_group_id': None, - 'remote_ip_prefix': None, - 'security_group_id': sg_id, - 'tenant_id': '5ea46368c7fe436bb8732738c149fbce'} - - return os_sgr.SecurityGroupRule(**sgr_data) - - -def get_k8s_pod(name='pod-5bb648d658-55n76', namespace='namespace', - uid='683da866-6bb1-4da2-bf6a-a5f4137c38e7'): - - return {'apiVersion': 'v1', - 'kind': 'Pod', - 'metadata': {'creationTimestamp': '2020-12-22T09:04:29Z', - 'finalizers': ['kuryr.openstack.org/pod-finalizer'], - 'generateName': 'pod-5bb648d658-', - 'labels': {'app': 'pod', - 'pod-template-hash': '5bb648d658'}, - 'operation': 'Update', - 'name': name, - 'namespace': namespace, - 'resourceVersion': '19416', - 'uid': uid}, - 'spec': {}, - 'status': {}} diff --git a/kuryr_kubernetes/tests/unit/__init__.py b/kuryr_kubernetes/tests/unit/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/kuryr_kubernetes/tests/unit/cmd/__init__.py b/kuryr_kubernetes/tests/unit/cmd/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/kuryr_kubernetes/tests/unit/cmd/eventlet/__init__.py b/kuryr_kubernetes/tests/unit/cmd/eventlet/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/kuryr_kubernetes/tests/unit/cmd/eventlet/test_controller.py b/kuryr_kubernetes/tests/unit/cmd/eventlet/test_controller.py deleted file mode 100644 index 992972849..000000000 --- a/kuryr_kubernetes/tests/unit/cmd/eventlet/test_controller.py +++ /dev/null @@ -1,34 +0,0 @@ -# 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. - -from unittest import mock - -from kuryr_kubernetes.tests import base as test_base - - -class TestControllerCmd(test_base.TestCase): - - @mock.patch('kuryr_kubernetes.controller.service.start') - @mock.patch('eventlet.monkey_patch') - def test_start(self, m_evmp, m_start): - # NOTE(ivc): eventlet.monkey_patch is invoked during the module - # import, so the controller cmd has to be imported locally to verify - # that monkey_patch is called - from kuryr_kubernetes.cmd.eventlet import controller - - controller.start() - - m_evmp.assert_called() - m_start.assert_called() diff --git a/kuryr_kubernetes/tests/unit/cmd/test_daemon.py b/kuryr_kubernetes/tests/unit/cmd/test_daemon.py deleted file mode 100644 index fa4a5c1e5..000000000 --- a/kuryr_kubernetes/tests/unit/cmd/test_daemon.py +++ /dev/null @@ -1,27 +0,0 @@ -# Copyright (c) 2017 NEC Corporation. -# 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. - -from unittest import mock - -from kuryr_kubernetes.tests import base as test_base - - -class TestDaemonCmd(test_base.TestCase): - @mock.patch('kuryr_kubernetes.cni.daemon.service.start') - def test_start(self, m_start): - from kuryr_kubernetes.cmd import daemon # To make it import a mock. - daemon.start() - - m_start.assert_called() diff --git a/kuryr_kubernetes/tests/unit/cmd/test_status.py b/kuryr_kubernetes/tests/unit/cmd/test_status.py deleted file mode 100644 index fc00c0b21..000000000 --- a/kuryr_kubernetes/tests/unit/cmd/test_status.py +++ /dev/null @@ -1,148 +0,0 @@ -# Copyright 2018 Red Hat -# -# 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 io -from unittest import mock - -from oslo_serialization import jsonutils - -from kuryr_kubernetes.cmd import status -from kuryr_kubernetes import constants -from kuryr_kubernetes.objects import vif -from kuryr_kubernetes.tests import base as test_base - - -class TestStatusCmd(test_base.TestCase): - @mock.patch('kuryr_kubernetes.clients.get_kubernetes_client') - @mock.patch('kuryr_kubernetes.clients.setup_kubernetes_client') - def setUp(self, m_client_setup, m_client_get): - super(TestStatusCmd, self).setUp() - self.cmd = status.UpgradeCommands() - - def test_upgrade_result_get_details(self): - res = status.UpgradeCheckResult(0, 'a ' * 50) - - self.assertEqual( - (('a ' * 30).rstrip() + '\n' + (' ' * 9) + ('a ' * 20)).rstrip(), - res.get_details()) - - def test__get_annotation_missing(self): - pod = { - 'metadata': { - 'annotations': {} - } - } - - self.assertIsNone(self.cmd._get_annotation(pod)) - - def test__get_annotation_existing(self): - mock_obj = vif.PodState( - default_vif=vif.VIFMacvlanNested(vif_name='foo')) - - pod = { - 'metadata': { - 'annotations': { - constants.K8S_ANNOTATION_VIF: jsonutils.dumps( - mock_obj.obj_to_primitive()) - } - } - } - - obj = self.cmd._get_annotation(pod) - self.assertEqual(mock_obj, obj) - - @mock.patch('sys.stdout', new_callable=io.StringIO) - def _test_upgrade_check(self, code, code_name, m_stdout): - method_success_m = mock.Mock() - method_success_m.return_value = status.UpgradeCheckResult(0, 'foo') - method_code_m = mock.Mock() - method_code_m.return_value = status.UpgradeCheckResult(code, 'bar') - - self.cmd.check_methods = {'baz': method_success_m, - 'blah': method_code_m} - self.assertEqual(code, self.cmd.upgrade_check()) - - output = m_stdout.getvalue() - self.assertIn('baz', output) - self.assertIn('bar', output) - self.assertIn('foo', output) - self.assertIn('blah', output) - self.assertIn('Success', output) - self.assertIn(code_name, output) - - def test_upgrade_check_success(self): - self._test_upgrade_check(0, 'Success') - - def test_upgrade_check_warning(self): - self._test_upgrade_check(1, 'Warning') - - def test_upgrade_check_failure(self): - self._test_upgrade_check(2, 'Failure') - - def _test__check_annotations(self, ann_objs, code): - pods = { - 'items': [ - { - 'metadata': { - 'annotations': { - constants.K8S_ANNOTATION_VIF: ann - } - } - } for ann in ann_objs - ] - } - self.cmd.k8s = mock.Mock(get=mock.Mock(return_value=pods)) - res = self.cmd._check_annotations() - self.assertEqual(code, res.code) - - def test__check_annotations_succeed(self): - ann_objs = [ - vif.PodState(default_vif=vif.VIFMacvlanNested(vif_name='foo')), - vif.PodState(default_vif=vif.VIFMacvlanNested(vif_name='bar')), - ] - ann_objs = [jsonutils.dumps(ann.obj_to_primitive()) - for ann in ann_objs] - - self._test__check_annotations(ann_objs, 0) - - def test__check_annotations_failure(self): - ann_objs = [ - vif.PodState(default_vif=vif.VIFMacvlanNested(vif_name='foo')), - vif.VIFMacvlanNested(vif_name='bar'), - ] - ann_objs = [jsonutils.dumps(ann.obj_to_primitive()) - for ann in ann_objs] - - self._test__check_annotations(ann_objs, 2) - - def test__check_annotations_malformed_and_old(self): - ann_objs = [ - vif.PodState(default_vif=vif.VIFMacvlanNested(vif_name='foo')), - vif.VIFMacvlanNested(vif_name='bar'), - ] - ann_objs = [jsonutils.dumps(ann.obj_to_primitive()) - for ann in ann_objs] - ann_objs.append('{}') - - self._test__check_annotations(ann_objs, 2) - - def test__check_annotations_malformed(self): - ann_objs = [ - vif.PodState(default_vif=vif.VIFMacvlanNested(vif_name='foo')), - ] - ann_objs = [jsonutils.dumps(ann.obj_to_primitive()) - for ann in ann_objs] - ann_objs.append('{}') - - self._test__check_annotations(ann_objs, 1) diff --git a/kuryr_kubernetes/tests/unit/cni/__init__.py b/kuryr_kubernetes/tests/unit/cni/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/kuryr_kubernetes/tests/unit/cni/plugins/__init__.py b/kuryr_kubernetes/tests/unit/cni/plugins/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/kuryr_kubernetes/tests/unit/cni/plugins/test_k8s_cni_registry.py b/kuryr_kubernetes/tests/unit/cni/plugins/test_k8s_cni_registry.py deleted file mode 100644 index 678b70ad6..000000000 --- a/kuryr_kubernetes/tests/unit/cni/plugins/test_k8s_cni_registry.py +++ /dev/null @@ -1,258 +0,0 @@ -# Copyright 2017 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 unittest import mock - -from oslo_config import cfg - -from kuryr_kubernetes.cni.plugins import k8s_cni_registry -from kuryr_kubernetes.cni import utils -from kuryr_kubernetes import exceptions -from kuryr_kubernetes.tests import base -from kuryr_kubernetes.tests import fake -from kuryr_kubernetes.tests.unit import kuryr_fixtures - - -class TestK8sCNIRegistryPlugin(base.TestCase): - def setUp(self): - super(TestK8sCNIRegistryPlugin, self).setUp() - self.k8s_mock = self.useFixture(kuryr_fixtures.MockK8sClient()).client - self.default_iface = 'baz' - self.additional_iface = 'eth1' - self.kp = {'apiVersion': 'openstack.org/v1', - 'kind': 'KuryrPort', - 'metadata': {'name': 'foo', 'uid': 'bar', - 'namespace': 'default'}, - 'spec': {'podUid': 'bar', 'podStatic': False}} - self.vifs = fake._fake_vifs() - registry = {'default/foo': {'kp': self.kp, 'vifs': self.vifs, - 'containerid': None, - 'vif_unplugged': False, - 'del_received': False}} - healthy = mock.Mock() - self.plugin = k8s_cni_registry.K8sCNIRegistryPlugin(registry, healthy) - self.params = mock.Mock( - args=utils.CNIArgs('K8S_POD_NAME=foo;K8S_POD_NAMESPACE=default;' - 'K8S_POD_UID=bar'), - CNI_IFNAME=self.default_iface, CNI_NETNS=123, - CNI_CONTAINERID='cont_id') - - @mock.patch('oslo_concurrency.lockutils.lock') - @mock.patch('kuryr_kubernetes.cni.binding.base.connect') - def test_add_present(self, m_connect, m_lock): - self.k8s_mock.get.return_value = self.kp - - self.plugin.add(self.params) - - m_lock.assert_called_with('default/foo', external=True) - m_connect.assert_any_call(mock.ANY, mock.ANY, self.default_iface, - 123, report_health=mock.ANY, - is_default_gateway=True, - container_id='cont_id') - m_connect.assert_any_call(mock.ANY, mock.ANY, self.additional_iface, - 123, report_health=mock.ANY, - is_default_gateway=False, - container_id='cont_id') - self.assertEqual('cont_id', - self.plugin.registry['default/foo']['containerid']) - - @mock.patch('oslo_concurrency.lockutils.lock') - @mock.patch('kuryr_kubernetes.cni.binding.base.connect') - def test_add_no_uid(self, m_connect, m_lock): - self.k8s_mock.get.return_value = self.kp - - self.params.args = utils.CNIArgs( - 'K8S_POD_NAME=foo;K8S_POD_NAMESPACE=default') - self.plugin.add(self.params) - - m_lock.assert_called_with('default/foo', external=True) - m_connect.assert_any_call(mock.ANY, mock.ANY, self.default_iface, - 123, report_health=mock.ANY, - is_default_gateway=True, - container_id='cont_id') - m_connect.assert_any_call(mock.ANY, mock.ANY, self.additional_iface, - 123, report_health=mock.ANY, - is_default_gateway=False, - container_id='cont_id') - self.k8s_mock.get.assert_any_call( - '/api/v1/namespaces/default/pods/foo') - self.assertEqual('cont_id', - self.plugin.registry['default/foo']['containerid']) - - @mock.patch('kuryr_kubernetes.cni.binding.base.connect') - def test_add_wrong_uid(self, m_connect): - cfg.CONF.set_override('vif_annotation_timeout', 0, group='cni_daemon') - self.addCleanup(cfg.CONF.set_override, 'vif_annotation_timeout', 120, - group='cni_daemon') - self.k8s_mock.get.return_value = self.kp - - self.params.args = utils.CNIArgs( - 'K8S_POD_NAME=foo;K8S_POD_NAMESPACE=default;K8S_POD_UID=blob') - self.assertRaises(exceptions.CNIPodUidMismatch, self.plugin.add, - self.params) - - m_connect.assert_not_called() - self.k8s_mock.get.assert_not_called() - - @mock.patch('oslo_concurrency.lockutils.lock') - @mock.patch('kuryr_kubernetes.cni.binding.base.connect') - def test_add_wrong_uid_static(self, m_connect, m_lock): - cfg.CONF.set_override('vif_annotation_timeout', 0, group='cni_daemon') - self.addCleanup(cfg.CONF.set_override, 'vif_annotation_timeout', 120, - group='cni_daemon') - self.k8s_mock.get.return_value = self.kp - - self.params.args = utils.CNIArgs( - 'K8S_POD_NAME=foo;K8S_POD_NAMESPACE=default;K8S_POD_UID=blob') - self.kp['spec']['podStatic'] = True - self.plugin.add(self.params) - - m_lock.assert_called_with('default/foo', external=True) - m_connect.assert_any_call(mock.ANY, mock.ANY, self.default_iface, - 123, report_health=mock.ANY, - is_default_gateway=True, - container_id='cont_id') - m_connect.assert_any_call(mock.ANY, mock.ANY, self.additional_iface, - 123, report_health=mock.ANY, - is_default_gateway=False, - container_id='cont_id') - self.k8s_mock.get.assert_any_call( - '/api/v1/namespaces/default/pods/foo') - self.assertEqual('cont_id', - self.plugin.registry['default/foo']['containerid']) - - @mock.patch('oslo_concurrency.lockutils.lock') - @mock.patch('kuryr_kubernetes.cni.binding.base.connect') - def test_add_wrong_uid_none_static(self, m_connect, m_lock): - cfg.CONF.set_override('vif_annotation_timeout', 0, group='cni_daemon') - self.addCleanup(cfg.CONF.set_override, 'vif_annotation_timeout', 120, - group='cni_daemon') - self.k8s_mock.get.side_effect = [ - {'metadata': { - 'annotations': {'kubernetes.io/config.source': 'file'}}}, - self.kp] - - self.params.args = utils.CNIArgs( - 'K8S_POD_NAME=foo;K8S_POD_NAMESPACE=default;K8S_POD_UID=blob') - del self.kp['spec']['podStatic'] - self.plugin.add(self.params) - - m_lock.assert_called_with('default/foo', external=True) - m_connect.assert_any_call(mock.ANY, mock.ANY, self.default_iface, - 123, report_health=mock.ANY, - is_default_gateway=True, - container_id='cont_id') - m_connect.assert_any_call(mock.ANY, mock.ANY, self.additional_iface, - 123, report_health=mock.ANY, - is_default_gateway=False, - container_id='cont_id') - self.k8s_mock.get.assert_any_call( - '/api/v1/namespaces/default/pods/foo') - self.assertEqual('cont_id', - self.plugin.registry['default/foo']['containerid']) - - @mock.patch('oslo_concurrency.lockutils.lock') - @mock.patch('kuryr_kubernetes.cni.binding.base.disconnect') - def test_del_present(self, m_disconnect, m_lock): - self.plugin.delete(self.params) - - m_lock.assert_called_with('default/foo', external=True) - m_disconnect.assert_any_call(mock.ANY, mock.ANY, self.default_iface, - 123, report_health=mock.ANY, - is_default_gateway=True, - container_id='cont_id') - m_disconnect.assert_any_call(mock.ANY, mock.ANY, self.additional_iface, - 123, report_health=mock.ANY, - is_default_gateway=False, - container_id='cont_id') - self.assertIn('default/foo', self.plugin.registry) - self.assertEqual(True, - self.plugin.registry['default/foo']['vif_unplugged']) - - @mock.patch('oslo_concurrency.lockutils.lock') - @mock.patch('kuryr_kubernetes.cni.binding.base.disconnect') - def test_remove_pod_from_registry_after_del(self, m_disconnect, m_lock): - self.plugin.registry['default/foo']['del_received'] = True - self.plugin.delete(self.params) - - m_lock.assert_called_with('default/foo', external=True) - self.assertNotIn('default/foo', self.plugin.registry) - m_disconnect.assert_any_call(mock.ANY, mock.ANY, self.default_iface, - 123, report_health=mock.ANY, - is_default_gateway=True, - container_id='cont_id') - m_disconnect.assert_any_call(mock.ANY, mock.ANY, self.additional_iface, - 123, report_health=mock.ANY, - is_default_gateway=False, - container_id='cont_id') - - @mock.patch('oslo_concurrency.lockutils.lock') - @mock.patch('kuryr_kubernetes.cni.binding.base.disconnect') - def test_del_wrong_container_id(self, m_disconnect, m_lock): - registry = {'default/foo': {'kp': self.kp, 'vifs': self.vifs, - 'containerid': 'different'}} - healthy = mock.Mock() - self.plugin = k8s_cni_registry.K8sCNIRegistryPlugin(registry, healthy) - self.plugin.delete(self.params) - - m_disconnect.assert_not_called() - m_lock.assert_called_with('default/foo', external=True) - - @mock.patch('oslo_concurrency.lockutils.lock') - @mock.patch('time.sleep', mock.Mock()) - @mock.patch('kuryr_kubernetes.cni.binding.base.connect') - def test_add_present_on_5_try(self, m_connect, m_lock): - se = [KeyError] * 5 - se.append({'kp': self.kp, 'vifs': self.vifs, 'containerid': None, - 'vif_unplugged': False, 'del_received': False}) - se.append({'kp': self.kp, 'vifs': self.vifs, 'containerid': None, - 'vif_unplugged': False, 'del_received': False}) - se.append({'kp': self.kp, 'vifs': self.vifs, 'containerid': None, - 'vif_unplugged': False, 'del_received': False}) - m_getitem = mock.Mock(side_effect=se) - m_setitem = mock.Mock() - m_registry = mock.Mock(__getitem__=m_getitem, __setitem__=m_setitem, - __contains__=mock.Mock(return_value=False)) - self.plugin.registry = m_registry - self.plugin.add(self.params) - - m_lock.assert_called_with('default/foo', external=True) - m_setitem.assert_called_once_with('default/foo', - {'kp': self.kp, - 'vifs': self.vifs, - 'containerid': 'cont_id', - 'vif_unplugged': False, - 'del_received': False}) - m_connect.assert_any_call(mock.ANY, mock.ANY, self.default_iface, - 123, report_health=mock.ANY, - is_default_gateway=True, - container_id='cont_id') - m_connect.assert_any_call(mock.ANY, mock.ANY, self.additional_iface, - 123, report_health=mock.ANY, - is_default_gateway=False, - container_id='cont_id') - - @mock.patch('time.sleep', mock.Mock()) - @mock.patch('oslo_concurrency.lockutils.lock', mock.Mock( - return_value=mock.Mock(__enter__=mock.Mock(), __exit__=mock.Mock()))) - def test_add_not_present(self): - cfg.CONF.set_override('vif_annotation_timeout', 0, group='cni_daemon') - self.addCleanup(cfg.CONF.set_override, 'vif_annotation_timeout', 120, - group='cni_daemon') - - m_getitem = mock.Mock(side_effect=KeyError) - m_registry = mock.Mock(__getitem__=m_getitem, __contains__=False) - self.plugin.registry = m_registry - self.assertRaises(exceptions.CNIKuryrPortTimeout, self.plugin.add, - self.params) diff --git a/kuryr_kubernetes/tests/unit/cni/test_api.py b/kuryr_kubernetes/tests/unit/cni/test_api.py deleted file mode 100644 index 6bb4a14ab..000000000 --- a/kuryr_kubernetes/tests/unit/cni/test_api.py +++ /dev/null @@ -1,102 +0,0 @@ -# Copyright (c) 2017 NEC Corporation. -# 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. - -from io import StringIO -from unittest import mock - -from oslo_config import cfg -from oslo_serialization import jsonutils -import requests - -from kuryr_kubernetes.cni import api -from kuryr_kubernetes.tests import base as test_base -from kuryr_kubernetes.tests import fake - -CONF = cfg.CONF - - -class TestCNIRunnerMixin(object): - def test_run_invalid(self, *args): - m_fin = StringIO() - m_fout = StringIO() - code = self.runner.run( - {'CNI_COMMAND': 'INVALID', 'CNI_ARGS': 'foo=bar'}, m_fin, m_fout) - - self.assertEqual(1, code) - - def test_run_write_version(self, *args): - m_fin = StringIO() - m_fout = StringIO() - code = self.runner.run( - {'CNI_COMMAND': 'VERSION', 'CNI_ARGS': 'foo=bar'}, m_fin, m_fout) - result = jsonutils.loads(m_fout.getvalue()) - - self.assertEqual(0, code) - self.assertEqual(api.CNIRunner.SUPPORTED_VERSIONS, - result['supportedVersions']) - self.assertEqual(api.CNIRunner.VERSION, result['cniVersion']) - - -@mock.patch('requests.post') -class TestCNIDaemonizedRunner(test_base.TestCase, TestCNIRunnerMixin): - def setUp(self): - super(TestCNIDaemonizedRunner, self).setUp() - self.runner = api.CNIDaemonizedRunner() - self.port = int(CONF.cni_daemon.bind_address.split(':')[1]) - - def _test_run(self, cni_cmd, path, m_post): - m_fin = StringIO() - m_fout = StringIO() - env = { - 'CNI_COMMAND': cni_cmd, - 'CNI_CONTAINERID': 'a4181c680a39', - 'CNI_ARGS': 'foo=bar', - 'CNI_IFNAME': 'eth0', - } - result = self.runner.run(env, m_fin, m_fout) - m_post.assert_called_with( - 'http://127.0.0.1:%d/%s' % (self.port, path), - json=mock.ANY, headers={'Connection': 'close'}) - return result - - def test_run_add(self, m_post): - m_response = mock.Mock(status_code=202) - m_response.json = mock.Mock(return_value=fake._fake_vif_dict()) - m_post.return_value = m_response - result = self._test_run('ADD', 'addNetwork', m_post) - self.assertEqual(0, result) - - def test_run_add_invalid(self, m_post): - m_response = mock.Mock(status_code=400) - m_response.json = mock.Mock() - m_post.return_value = m_response - result = self._test_run('ADD', 'addNetwork', m_post) - self.assertEqual(1, result) - m_response.json.assert_not_called() - - def test_run_del(self, m_post): - m_post.return_value = mock.Mock(status_code=204) - result = self._test_run('DEL', 'delNetwork', m_post) - self.assertEqual(0, result) - - def test_run_del_invalid(self, m_post): - m_post.return_value = mock.Mock(status_code=400) - result = self._test_run('DEL', 'delNetwork', m_post) - self.assertEqual(1, result) - - def test_run_socket_error(self, m_post): - m_post.side_effect = requests.ConnectionError - result = self._test_run('DEL', 'delNetwork', m_post) - self.assertEqual(1, result) diff --git a/kuryr_kubernetes/tests/unit/cni/test_binding.py b/kuryr_kubernetes/tests/unit/cni/test_binding.py deleted file mode 100644 index 8f066db52..000000000 --- a/kuryr_kubernetes/tests/unit/cni/test_binding.py +++ /dev/null @@ -1,383 +0,0 @@ -# Copyright 2017 Red Hat, 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 collections -import os -from unittest import mock -import uuid - - -from os_vif import objects as osv_objects -from os_vif.objects import fields as osv_fields -from oslo_config import cfg -from oslo_utils import uuidutils - -from kuryr_kubernetes.cni.binding import base -from kuryr_kubernetes.cni.binding import nested -from kuryr_kubernetes.cni.binding import vhostuser -from kuryr_kubernetes import exceptions -from kuryr_kubernetes import objects -from kuryr_kubernetes.tests import base as test_base -from kuryr_kubernetes.tests import fake - -CONF = cfg.CONF - - -class TestDriverMixin(test_base.TestCase): - def setUp(self): - super(TestDriverMixin, self).setUp() - self.instance_info = osv_objects.instance_info.InstanceInfo( - uuid=uuid.uuid4(), name='foo') - self.ifname = 'c_interface' - self.netns = '/proc/netns/1234' - - # Mock IPDB context managers - self.ipdbs = {} - self.m_bridge_iface = mock.Mock(__exit__=mock.Mock(return_value=None)) - self.m_c_iface = mock.Mock() - self.m_h_iface = mock.Mock() - self.h_ipdb, self.h_ipdb_exit = self._mock_ipdb_context_manager(None) - self.c_ipdb, self.c_ipdb_exit = self._mock_ipdb_context_manager( - self.netns) - self.m_create = mock.Mock() - self.h_ipdb.create = mock.Mock( - return_value=mock.Mock( - __enter__=mock.Mock(return_value=self.m_create), - __exit__=mock.Mock(return_value=None))) - self.c_ipdb.create = mock.Mock( - return_value=mock.Mock( - __enter__=mock.Mock(return_value=self.m_create), - __exit__=mock.Mock(return_value=None))) - - def _mock_ipdb_context_manager(self, netns): - mock_ipdb = mock.Mock( - interfaces={ - 'bridge': mock.Mock( - __enter__=mock.Mock(return_value=self.m_bridge_iface), - __exit__=mock.Mock(return_value=None), - mtu=1, - ), - 'c_interface': mock.Mock( - __enter__=mock.Mock(return_value=self.m_c_iface), - __exit__=mock.Mock(return_value=None), - ), - 'h_interface': mock.Mock( - __enter__=mock.Mock(return_value=self.m_h_iface), - __exit__=mock.Mock(return_value=None), - ), - } - ) - mock_exit = mock.Mock(return_value=None) - mock_ipdb.__exit__ = mock_exit - mock_ipdb.__enter__ = mock.Mock(return_value=mock_ipdb) - self.ipdbs[netns] = mock_ipdb - - return mock_ipdb, mock_exit - - @mock.patch('kuryr_kubernetes.cni.binding.base._need_configure_l3') - @mock.patch('kuryr_kubernetes.cni.binding.base.get_ipdb') - @mock.patch('os_vif.plug') - def _test_connect(self, m_vif_plug, m_get_ipdb, m_need_l3, report=None): - def get_ipdb(netns=None): - return self.ipdbs[netns] - - m_get_ipdb.side_effect = get_ipdb - m_need_l3.return_value = True - - base.connect(self.vif, self.instance_info, self.ifname, self.netns, - report) - m_vif_plug.assert_called_once_with(self.vif, self.instance_info) - self.m_c_iface.add_ip.assert_called_once_with('192.168.0.2/24') - if report: - report.assert_called_once() - - @mock.patch('kuryr_kubernetes.cni.binding.base.get_ipdb') - @mock.patch('os_vif.unplug') - def _test_disconnect(self, m_vif_unplug, m_get_ipdb, report=None): - def get_ipdb(netns=None): - return self.ipdbs[netns] - m_get_ipdb.side_effect = get_ipdb - - base.disconnect(self.vif, self.instance_info, self.ifname, self.netns, - report) - m_vif_unplug.assert_called_once_with(self.vif, self.instance_info) - if report: - report.assert_called_once() - - -class TestOpenVSwitchDriver(TestDriverMixin, test_base.TestCase): - def setUp(self): - super(TestOpenVSwitchDriver, self).setUp() - self.vif = fake._fake_vif(osv_objects.vif.VIFOpenVSwitch) - - @mock.patch('kuryr_kubernetes.cni.plugins.k8s_cni_registry.' - 'K8sCNIRegistryPlugin.report_drivers_health') - @mock.patch('os.getpid', mock.Mock(return_value=123)) - @mock.patch('kuryr_kubernetes.linux_net_utils.create_ovs_vif_port') - def test_connect(self, mock_create_ovs, m_report): - self._test_connect(report=m_report) - self.assertEqual(3, self.h_ipdb_exit.call_count) - self.assertEqual(2, self.c_ipdb_exit.call_count) - self.c_ipdb.create.assert_called_once_with( - ifname=self.ifname, peer='h_interface', kind='veth') - self.assertEqual(1, self.m_create.mtu) - self.assertEqual(str(self.vif.address), - self.m_create.address) - self.m_create.up.assert_called_once_with() - self.assertEqual(123, self.m_h_iface.net_ns_pid) - self.assertEqual(1, self.m_h_iface.mtu) - self.m_h_iface.up.assert_called_once_with() - - mock_create_ovs.assert_called_once_with( - 'bridge', 'h_interface', '89eccd45-43e9-43d8-b4cc-4c13db13f782', - '3e:94:b7:31:a0:83', 'kuryr') - - @mock.patch('kuryr_kubernetes.cni.plugins.k8s_cni_registry.' - 'K8sCNIRegistryPlugin.report_drivers_health') - @mock.patch('kuryr_kubernetes.linux_net_utils.delete_ovs_vif_port') - def test_disconnect(self, mock_delete_ovs, m_report): - self._test_disconnect(report=m_report) - mock_delete_ovs.assert_called_once_with('bridge', 'h_interface') - - -class TestBridgeDriver(TestDriverMixin, test_base.TestCase): - def setUp(self): - super(TestBridgeDriver, self).setUp() - self.vif = fake._fake_vif(osv_objects.vif.VIFBridge) - - @mock.patch('os.getpid', mock.Mock(return_value=123)) - def test_connect(self): - self._test_connect() - - self.m_h_iface.remove.assert_called_once_with() - - self.assertEqual(3, self.h_ipdb_exit.call_count) - self.assertEqual(2, self.c_ipdb_exit.call_count) - self.c_ipdb.create.assert_called_once_with( - ifname=self.ifname, peer='h_interface', kind='veth') - self.assertEqual(1, self.m_create.mtu) - self.assertEqual(str(self.vif.address), - self.m_create.address) - self.m_create.up.assert_called_once_with() - self.assertEqual(123, self.m_h_iface.net_ns_pid) - self.assertEqual(1, self.m_h_iface.mtu) - self.m_h_iface.up.assert_called_once_with() - - self.m_bridge_iface.add_port.assert_called_once_with('h_interface') - - def test_disconnect(self): - self._test_disconnect() - - -class TestNestedDriver(TestDriverMixin, test_base.TestCase): - def setUp(self): - super(TestNestedDriver, self).setUp() - ifaces = { - 'lo': {'flags': 0x8, 'ipaddr': (('127.0.0.1', 8),)}, - 'first': {'flags': 0, 'ipaddr': (('192.168.0.1', 8),)}, - 'kubelet': {'flags': 0, 'ipaddr': (('192.168.1.1', 8),)}, - 'bridge': {'flags': 0, 'ipaddr': (('192.168.2.1', 8),)}, - } - self.h_ipdb = mock.Mock(interfaces=ifaces) - self.h_ipdb_loopback = mock.Mock(interfaces=ifaces) - self.sconn = collections.namedtuple( - 'sconn', ['fd', 'family', 'type', 'laddr', 'raddr', 'status', - 'pid']) - self.addr = collections.namedtuple('addr', ['ip', 'port']) - - @mock.patch.multiple(nested.NestedDriver, __abstractmethods__=set()) - def test_detect_config(self): - driver = nested.NestedDriver() - self.addCleanup(CONF.clear_override, 'link_iface', group='binding') - CONF.set_override('link_iface', 'bridge', group='binding') - iface = driver._detect_iface_name(self.h_ipdb) - self.assertEqual('bridge', iface) - - @mock.patch.multiple(nested.NestedDriver, __abstractmethods__=set()) - @mock.patch('psutil.net_connections') - def test_detect_kubelet_port(self, m_net_connections): - driver = nested.NestedDriver() - m_net_connections.return_value = [ - self.sconn(-1, 2, 2, laddr=self.addr(ip='192.168.1.1', port=53), - raddr=(), status='LISTEN', pid=None), - self.sconn(-1, 2, 2, laddr=self.addr(ip='192.168.1.1', port=10250), - raddr=(), status='ESTABLISHED', pid=None), - self.sconn(-1, 2, 2, laddr=self.addr(ip='192.168.1.1', port=10250), - raddr=(), status='LISTEN', pid=None), - ] - iface = driver._detect_iface_name(self.h_ipdb) - self.assertEqual('kubelet', iface) - - @mock.patch.multiple(nested.NestedDriver, __abstractmethods__=set()) - @mock.patch('psutil.net_connections') - def test_detect_non_loopback(self, m_net_connections): - driver = nested.NestedDriver() - m_net_connections.return_value = [] - - iface = driver._detect_iface_name(self.h_ipdb) - self.assertEqual('first', iface) - - @mock.patch.multiple(nested.NestedDriver, __abstractmethods__=set()) - @mock.patch('psutil.net_connections') - def test_detect_none(self, m_net_connections): - driver = nested.NestedDriver() - m_net_connections.return_value = [] - - self.h_ipdb.interfaces = { - 'lo': {'flags': 0x8, 'ipaddr': (('127.0.0.1', 8),)}, - } - self.assertRaises(exceptions.CNIBindingFailure, - driver._detect_iface_name, self.h_ipdb) - - -class TestNestedVlanDriver(TestDriverMixin, test_base.TestCase): - def setUp(self): - super(TestNestedVlanDriver, self).setUp() - self.vif = fake._fake_vif(objects.vif.VIFVlanNested) - self.vif.vlan_id = 7 - CONF.set_override('link_iface', 'bridge', group='binding') - self.addCleanup(CONF.clear_override, 'link_iface', group='binding') - - def test_connect(self): - self._test_connect() - - self.assertEqual(2, self.h_ipdb_exit.call_count) - self.assertEqual(3, self.c_ipdb_exit.call_count) - - self.assertEqual(self.ifname, self.m_h_iface.ifname) - self.assertEqual(1, self.m_h_iface.mtu) - self.assertEqual(str(self.vif.address), self.m_h_iface.address) - self.m_h_iface.up.assert_called_once_with() - - def test_connect_mtu_mismatch(self): - self.vif.network.mtu = 2 - self.assertRaises(exceptions.CNIBindingFailure, self._test_connect) - - def test_disconnect(self): - self._test_disconnect() - - -class TestNestedMacvlanDriver(TestDriverMixin, test_base.TestCase): - def setUp(self): - super(TestNestedMacvlanDriver, self).setUp() - self.vif = fake._fake_vif(objects.vif.VIFMacvlanNested) - CONF.set_override('link_iface', 'bridge', group='binding') - self.addCleanup(CONF.clear_override, 'link_iface', group='binding') - - def test_connect(self): - self._test_connect() - - self.assertEqual(2, self.h_ipdb_exit.call_count) - self.assertEqual(3, self.c_ipdb_exit.call_count) - - self.assertEqual(self.ifname, self.m_h_iface.ifname) - self.assertEqual(1, self.m_h_iface.mtu) - self.assertEqual(str(self.vif.address), self.m_h_iface.address) - self.m_h_iface.up.assert_called_once_with() - - def test_connect_mtu_mismatch(self): - self.vif.network.mtu = 2 - self.assertRaises(exceptions.CNIBindingFailure, self._test_connect) - - def test_disconnect(self): - self._test_disconnect() - - -class TestVHostUserDriver(TestDriverMixin, test_base.TestCase): - def setUp(self): - super(TestVHostUserDriver, self).setUp() - self.vu_mount_point = '/var/run/cni' - self.vu_ovs_path = '/var/run/openvswitch' - CONF.set_override('mount_point', self.vu_mount_point, - group='vhostuser') - CONF.set_override('ovs_vhu_path', self.vu_ovs_path, - group='vhostuser') - self.vif = fake._fake_vif(osv_objects.vif.VIFVHostUser) - self.vif.path = self.vu_mount_point - self.vif.address = '64:0f:2b:5f:0c:1c' - self.port_name = vhostuser._get_vhostuser_port_name(self.vif) - self.cont_id = uuidutils.generate_uuid() - - @mock.patch('kuryr_kubernetes.cni.binding.base._need_configure_l3') - @mock.patch('kuryr_kubernetes.cni.plugins.k8s_cni_registry.' - 'K8sCNIRegistryPlugin.report_drivers_health') - @mock.patch('os.rename') - @mock.patch('os.path.exists', mock.Mock(return_value=True)) - @mock.patch('kuryr_kubernetes.cni.binding.vhostuser.VIFVHostUserDriver.' - '_write_config') - @mock.patch('kuryr_kubernetes.cni.binding.vhostuser._check_sock_file') - @mock.patch('os_vif.plug') - def test_connect_client(self, m_vif_plug, m_check_sock, m_write_conf, - m_os_rename, m_report, m_need_l3): - m_need_l3.return_value = False - self.vif.mode = osv_fields.VIFVHostUserMode.CLIENT - m_check_sock.return_value = True - base.connect(self.vif, self.instance_info, self.ifname, self.netns, - m_report, container_id=self.cont_id) - vu_dst_socket = os.path.join(self.vu_mount_point, self.port_name) - vu_src_socket = os.path.join(self.vu_ovs_path, self.port_name) - - m_vif_plug.assert_called_once_with(self.vif, self.instance_info) - m_os_rename.assert_called_once_with(vu_src_socket, vu_dst_socket) - m_write_conf.assert_called_once_with(self.cont_id, self.ifname, - self.port_name, self.vif) - m_report.assert_called_once() - - @mock.patch('kuryr_kubernetes.cni.binding.base._need_configure_l3') - @mock.patch('kuryr_kubernetes.cni.plugins.k8s_cni_registry.' - 'K8sCNIRegistryPlugin.report_drivers_health') - @mock.patch('kuryr_kubernetes.cni.binding.vhostuser.VIFVHostUserDriver.' - '_write_config') - @mock.patch('os_vif.plug') - def test_connect_server(self, m_vif_plug, m_write_conf, - m_report, m_need_l3): - m_need_l3.return_value = False - self.vif.mode = osv_fields.VIFVHostUserMode.SERVER - base.connect(self.vif, self.instance_info, self.ifname, self.netns, - m_report, container_id=self.cont_id) - m_vif_plug.assert_called_once_with(self.vif, self.instance_info) - m_write_conf.assert_called_once_with(self.cont_id, self.ifname, - self.port_name, self.vif) - m_report.assert_called_once() - - @mock.patch('kuryr_kubernetes.cni.plugins.k8s_cni_registry.' - 'K8sCNIRegistryPlugin.report_drivers_health') - @mock.patch('kuryr_kubernetes.cni.binding.vhostuser._check_sock_file', - mock.Mock(return_value=False)) - @mock.patch('kuryr_kubernetes.cni.binding.vhostuser.VIFVHostUserDriver.' - '_write_config', mock.Mock()) - @mock.patch('os_vif.plug') - def test_connect_nosocket(self, m_vif_plug, m_report): - self.vif.mode = osv_fields.VIFVHostUserMode.CLIENT - self.assertRaises(exceptions.CNIError, base.connect, self.vif, - self.instance_info, self.ifname, self.netns, - m_report, container_id=self.cont_id) - - @mock.patch('kuryr_kubernetes.cni.plugins.k8s_cni_registry.' - 'K8sCNIRegistryPlugin.report_drivers_health') - @mock.patch('kuryr_kubernetes.cni.binding.vhostuser._get_vhu_sock') - @mock.patch('os.remove') - @mock.patch('os.path.exists', mock.Mock(return_value=True)) - @mock.patch('os_vif.unplug') - def test_disconnect(self, m_os_unplug, m_os_remove, m_get_vhu_sock, - m_report): - m_get_vhu_sock.return_value = self.port_name - base.disconnect(self.vif, self.instance_info, self.ifname, self.netns, - m_report, container_id=self.cont_id) - conf_file_path = '{}/{}-{}'.format(self.vu_mount_point, - self.cont_id, self.ifname) - vhu_sock_path = '{}/{}'.format(self.vu_mount_point, - self.port_name) - os_remove_calls = [mock.call(vhu_sock_path), mock.call(conf_file_path)] - m_os_remove.assert_has_calls(os_remove_calls) diff --git a/kuryr_kubernetes/tests/unit/cni/test_handlers.py b/kuryr_kubernetes/tests/unit/cni/test_handlers.py deleted file mode 100644 index 75dff35b6..000000000 --- a/kuryr_kubernetes/tests/unit/cni/test_handlers.py +++ /dev/null @@ -1,68 +0,0 @@ -# Copyright 2021 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 unittest import mock - -from kuryr_kubernetes.cni import handlers -from kuryr_kubernetes.tests import base - - -class TestCNIDaemonHandlers(base.TestCase): - def setUp(self): - super().setUp() - self.registry = {} - self.pod = {'metadata': {'namespace': 'testing', - 'name': 'default'}, - 'vif_unplugged': False, - 'del_receieved': False} - self.healthy = mock.Mock() - self.port_handler = handlers.CNIKuryrPortHandler(self.registry) - self.pod_handler = handlers.CNIPodHandler(self.registry) - - @mock.patch('oslo_concurrency.lockutils.lock') - def test_kp_on_deleted(self, m_lock): - pod = self.pod - pod['vif_unplugged'] = True - pod_name = 'testing/default' - self.registry[pod_name] = pod - self.port_handler.on_deleted(pod) - self.assertNotIn(pod_name, self.registry) - - @mock.patch('oslo_concurrency.lockutils.lock') - def test_kp_on_deleted_false(self, m_lock): - pod = self.pod - pod_name = 'testing/default' - self.registry[pod_name] = pod - self.port_handler.on_deleted(pod) - self.assertIn(pod_name, self.registry) - self.assertIs(True, pod['del_received']) - - @mock.patch('oslo_concurrency.lockutils.lock') - def test_pod_on_finalize(self, m_lock): - pod = self.pod - pod_name = 'testing/default' - self.pod_handler.on_finalize(pod) - self.assertIn(pod_name, self.registry) - self.assertIsNone(self.registry[pod_name]) - m_lock.assert_called_once_with(pod_name, external=True) - - @mock.patch('oslo_concurrency.lockutils.lock') - def test_pod_on_finalize_exists(self, m_lock): - pod = self.pod - pod_name = 'testing/default' - self.registry[pod_name] = pod - self.pod_handler.on_finalize(pod) - self.assertIn(pod_name, self.registry) - self.assertIsNotNone(self.registry[pod_name]) - m_lock.assert_called_once_with(pod_name, external=True) diff --git a/kuryr_kubernetes/tests/unit/cni/test_health.py b/kuryr_kubernetes/tests/unit/cni/test_health.py deleted file mode 100644 index 8aaedfa17..000000000 --- a/kuryr_kubernetes/tests/unit/cni/test_health.py +++ /dev/null @@ -1,127 +0,0 @@ -# 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 ctypes import c_bool -from kuryr_kubernetes.cni import health -from kuryr_kubernetes.tests import base -import multiprocessing -import os -import tempfile -from unittest import mock - -from oslo_config import cfg - - -class TestCNIHealthServer(base.TestCase): - - def setUp(self): - super(TestCNIHealthServer, self).setUp() - healthy = multiprocessing.Value(c_bool, True) - self.srv = health.CNIHealthServer(healthy) - self.srv.application.testing = True - self.test_client = self.srv.application.test_client() - - @mock.patch('kuryr_kubernetes.cni.health._has_cap') - @mock.patch('kuryr_kubernetes.cni.health.CNIHealthServer.' - 'verify_k8s_connection') - def test_readiness_status(self, m_verify_k8s_conn, cap_tester): - cap_tester.return_value = True - m_verify_k8s_conn.return_value = True, 200 - resp = self.test_client.get('/ready') - self.assertEqual(200, resp.status_code) - - @mock.patch('kuryr_kubernetes.cni.health._has_cap') - @mock.patch('kuryr_kubernetes.cni.health.CNIHealthServer.' - 'verify_k8s_connection') - def test_readiness_status_net_admin_error(self, m_verify_k8s_conn, - cap_tester): - cap_tester.return_value = False - m_verify_k8s_conn.return_value = True, 200 - resp = self.test_client.get('/ready') - self.assertEqual(500, resp.status_code) - - @mock.patch('kuryr_kubernetes.cni.health._has_cap') - @mock.patch('kuryr_kubernetes.cni.health.CNIHealthServer.' - 'verify_k8s_connection') - def test_readiness_status_k8s_error(self, m_verify_k8s_conn, cap_tester): - cap_tester.return_value = True - m_verify_k8s_conn.return_value = False - resp = self.test_client.get('/ready') - self.assertEqual(500, resp.status_code) - - @mock.patch('pyroute2.IPDB.release') - def test_liveness_status(self, m_ipdb): - self.srv._components_healthy.value = True - resp = self.test_client.get('/alive') - m_ipdb.assert_called() - self.assertEqual(200, resp.status_code) - - def test_liveness_status_components_error(self): - self.srv._components_healthy.value = False - resp = self.test_client.get('/alive') - self.assertEqual(500, resp.status_code) - - @mock.patch('pyroute2.IPDB.release') - def test_liveness_status_ipdb_error(self, m_ipdb): - m_ipdb.side_effect = Exception - resp = self.test_client.get('/alive') - self.assertEqual(500, resp.status_code) - - @mock.patch('kuryr_kubernetes.cni.health._get_memsw_usage') - def test_liveness_status_mem_usage_error(self, get_memsw_usage): - get_memsw_usage.return_value = 5368709120 / health.BYTES_AMOUNT - cfg.CONF.set_override('max_memory_usage', 4096, - group='cni_health_server') - - resp = self.test_client.get('/alive') - self.assertEqual(500, resp.status_code) - - -class TestCNIHealthUtils(base.TestCase): - def test_has_cap(self): - with tempfile.NamedTemporaryFile() as fake_status: - fake_status.write(b'CapInh:\t0000000000000000\n' - b'CapPrm:\t0000000000000000\n' - b'CapEff:\t0000000000000000\n' - b'CapBnd:\t0000003fffffffff\n') - fake_status.flush() - self.assertTrue( - health._has_cap(health.CAP_NET_ADMIN, - 'CapBnd:\t', - fake_status.name)) - - def test__get_mem_usage(self): - mem_usage = 500 # Arbitrary mem usage amount - fake_cg_path = tempfile.mkdtemp(suffix='kuryr') - usage_in_bytes_path = os.path.join(fake_cg_path, health.MEMSW_FILENAME) - try: - with open(usage_in_bytes_path, 'w') as cgroup_mem_usage: - cgroup_mem_usage.write('{}\n'.format( - mem_usage * health.BYTES_AMOUNT)) - self.assertEqual(health._get_memsw_usage(fake_cg_path), mem_usage) - finally: - os.unlink(usage_in_bytes_path) - os.rmdir(fake_cg_path) - - @mock.patch('kuryr_kubernetes.cni.utils.running_under_container_runtime') - def test__get_cni_cgroup_path_system(self, running_containerized): - running_containerized.return_value = False - fake_path = '/kuryr/rules' - cfg.CONF.set_override('cg_path', fake_path, - group='cni_health_server') - self.assertEqual(health._get_cni_cgroup_path(), fake_path) - - @mock.patch('kuryr_kubernetes.cni.utils.running_under_container_runtime') - def test__get_cni_cgroup_path_container(self, running_containerized): - running_containerized.return_value = True - self.assertEqual(health._get_cni_cgroup_path(), - health.TOP_CGROUP_MEMORY_PATH) diff --git a/kuryr_kubernetes/tests/unit/cni/test_main.py b/kuryr_kubernetes/tests/unit/cni/test_main.py deleted file mode 100644 index e360b8f3c..000000000 --- a/kuryr_kubernetes/tests/unit/cni/test_main.py +++ /dev/null @@ -1,43 +0,0 @@ -# Copyright (c) 2017 NEC Corporation. -# 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. - -from unittest import mock - -from kuryr_kubernetes.cni import main -from kuryr_kubernetes.tests import base as test_base - - -class TestCNIMain(test_base.TestCase): - @mock.patch('kuryr_kubernetes.cni.main.jsonutils.load') - @mock.patch('sys.exit') - @mock.patch('sys.stdin') - @mock.patch('kuryr_kubernetes.cni.utils.CNIConfig') - @mock.patch('kuryr_kubernetes.cni.api') - @mock.patch('kuryr_kubernetes.config.init') - @mock.patch('kuryr_kubernetes.config.setup_logging') - @mock.patch('kuryr_kubernetes.cni.api.CNIDaemonizedRunner') - def test_daemonized_run(self, m_cni_dr, m_setup_logging, m_config_init, - m_api, m_conf, m_sys, m_sysexit, m_json): - m_conf.debug = mock.Mock() - m_conf.debug.return_value = True - m_cni_dr.return_value = mock.MagicMock() - m_cni_daemon = m_cni_dr.return_value - - main.run() - - m_config_init.assert_called() - m_setup_logging.assert_called() - m_cni_daemon.run.assert_called() - m_sysexit.assert_called() diff --git a/kuryr_kubernetes/tests/unit/cni/test_service.py b/kuryr_kubernetes/tests/unit/cni/test_service.py deleted file mode 100644 index ef1ece7fe..000000000 --- a/kuryr_kubernetes/tests/unit/cni/test_service.py +++ /dev/null @@ -1,109 +0,0 @@ -# Copyright 2017 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. - -import queue -from unittest import mock - -from oslo_serialization import jsonutils - -from kuryr_kubernetes.cni.daemon import service -from kuryr_kubernetes.cni.plugins import k8s_cni_registry -from kuryr_kubernetes import exceptions -from kuryr_kubernetes.tests import base -from kuryr_kubernetes.tests import fake -from kuryr_kubernetes.tests.unit import kuryr_fixtures - - -class TestDaemonServer(base.TestCase): - def setUp(self): - super(TestDaemonServer, self).setUp() - healthy = mock.Mock() - self.k8s_mock = self.useFixture(kuryr_fixtures.MockK8sClient()) - self.plugin = k8s_cni_registry.K8sCNIRegistryPlugin({}, healthy) - self.health_registry = mock.Mock() - self.metrics = queue.Queue() - self.srv = service.DaemonServer( - self.plugin, self.health_registry, self.metrics) - - self.srv.application.testing = True - self.test_client = self.srv.application.test_client() - cni_args = 'foo=bar;K8S_POD_NAMESPACE=test;K8S_POD_NAME=test' - params = {'config_kuryr': {}, 'CNI_ARGS': cni_args, - 'CNI_CONTAINERID': 'baz', 'CNI_COMMAND': 'ADD'} - self.params_str = jsonutils.dumps(params) - - @mock.patch('kuryr_kubernetes.cni.plugins.k8s_cni_registry.' - 'K8sCNIRegistryPlugin.add') - def test_add(self, m_add): - vif = fake._fake_vif() - m_add.return_value = vif - - resp = self.test_client.post('/addNetwork', data=self.params_str, - content_type='application/json') - - m_add.assert_called_once_with(mock.ANY) - self.assertEqual( - fake._fake_vif_string(vif.obj_to_primitive()).encode(), resp.data) - self.assertEqual(202, resp.status_code) - - @mock.patch('kuryr_kubernetes.cni.plugins.k8s_cni_registry.' - 'K8sCNIRegistryPlugin.add') - def test_add_timeout(self, m_add): - m_add.side_effect = exceptions.CNIKuryrPortTimeout('bar') - - resp = self.test_client.post('/addNetwork', data=self.params_str, - content_type='application/json') - - m_add.assert_called_once_with(mock.ANY) - self.assertEqual(504, resp.status_code) - - @mock.patch('kuryr_kubernetes.cni.plugins.k8s_cni_registry.' - 'K8sCNIRegistryPlugin.add') - def test_add_error(self, m_add): - m_add.side_effect = Exception - - resp = self.test_client.post('/addNetwork', data=self.params_str, - content_type='application/json') - - m_add.assert_called_once_with(mock.ANY) - self.assertEqual(500, resp.status_code) - - @mock.patch('kuryr_kubernetes.cni.plugins.k8s_cni_registry.' - 'K8sCNIRegistryPlugin.delete') - def test_delete(self, m_delete): - resp = self.test_client.post('/delNetwork', data=self.params_str, - content_type='application/json') - - m_delete.assert_called_once_with(mock.ANY) - self.assertEqual(204, resp.status_code) - - @mock.patch('kuryr_kubernetes.cni.plugins.k8s_cni_registry.' - 'K8sCNIRegistryPlugin.delete') - def test_delete_timeout(self, m_delete): - m_delete.side_effect = exceptions.CNIKuryrPortTimeout('foo') - resp = self.test_client.post('/delNetwork', data=self.params_str, - content_type='application/json') - - m_delete.assert_called_once_with(mock.ANY) - self.assertEqual(204, resp.status_code) - - @mock.patch('kuryr_kubernetes.cni.plugins.k8s_cni_registry.' - 'K8sCNIRegistryPlugin.delete') - def test_delete_error(self, m_delete): - m_delete.side_effect = Exception - resp = self.test_client.post('/delNetwork', data=self.params_str, - content_type='application/json') - - m_delete.assert_called_once_with(mock.ANY) - self.assertEqual(500, resp.status_code) diff --git a/kuryr_kubernetes/tests/unit/cni/test_utils.py b/kuryr_kubernetes/tests/unit/cni/test_utils.py deleted file mode 100644 index c2abe4ada..000000000 --- a/kuryr_kubernetes/tests/unit/cni/test_utils.py +++ /dev/null @@ -1,34 +0,0 @@ -# Copyright Red Hat, Inc. 2018 -# 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 tempfile - -import ddt -from kuryr_kubernetes.cni import utils -from kuryr_kubernetes.tests import base - - -@ddt.ddt -class TestCNIUtils(base.TestCase): - @ddt.data(*utils.CONTAINER_RUNTIME_CGROUP_IDS) - def test_running_under_container_runtime(self, container_runtime_id): - with tempfile.NamedTemporaryFile() as proc_one_cgroup: - proc_one_cgroup.write(container_runtime_id.encode()) - proc_one_cgroup.write(b'\n') - proc_one_cgroup.flush() - self.assertTrue( - utils.running_under_container_runtime(proc_one_cgroup.name)) - - def test_not_running_under_container_runtime(self): - with tempfile.NamedTemporaryFile() as proc_one_cgroup: - self.assertFalse( - utils.running_under_container_runtime(proc_one_cgroup.name)) diff --git a/kuryr_kubernetes/tests/unit/controller/__init__.py b/kuryr_kubernetes/tests/unit/controller/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/kuryr_kubernetes/tests/unit/controller/drivers/__init__.py b/kuryr_kubernetes/tests/unit/controller/drivers/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/kuryr_kubernetes/tests/unit/controller/drivers/test_annotation_project.py b/kuryr_kubernetes/tests/unit/controller/drivers/test_annotation_project.py deleted file mode 100644 index be6d13815..000000000 --- a/kuryr_kubernetes/tests/unit/controller/drivers/test_annotation_project.py +++ /dev/null @@ -1,122 +0,0 @@ -# Copyright (c) 2022 Troila -# 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. - -from unittest import mock - -from oslo_config import cfg - -from kuryr_kubernetes import constants -from kuryr_kubernetes.controller.drivers import annotation_project -from kuryr_kubernetes.tests import base as test_base - - -class TestAnnotationProjectDriverBase(test_base.TestCase): - - project_id = 'fake_project_id' - - def _get_project_from_namespace(self, resource, driver): - m_get_k8s_res = mock.patch('kuryr_kubernetes.controller.drivers.' - 'utils.get_k8s_resource').start() - m_get_k8s_res.return_value = { - 'metadata': { - 'name': 'fake_namespace', - 'annotations': { - constants.K8s_ANNOTATION_PROJECT: self.project_id}}} - project_id = driver.get_project(resource) - self.assertEqual(self.project_id, project_id) - - def _get_project_from_configure_option(self, resource, driver): - m_cfg = mock.patch('kuryr_kubernetes.config.CONF').start() - m_cfg.neutron_defaults.project = self.project_id - m_get_k8s_res = mock.patch('kuryr_kubernetes.controller.drivers.' - 'utils.get_k8s_resource').start() - m_get_k8s_res.return_value = { - 'metadata': { - 'name': 'fake_namespace', - 'annotations': {}}} - project_id = driver.get_project(resource) - self.assertEqual(self.project_id, project_id) - - def _project_id_not_set(self, resource, driver): - m_cfg = mock.patch('kuryr_kubernetes.config.CONF').start() - m_cfg.neutron_defaults.project = "" - m_get_k8s_res = mock.patch('kuryr_kubernetes.controller.drivers.' - 'utils.get_k8s_resource').start() - m_get_k8s_res.return_value = { - 'metadata': { - 'name': 'fake_namespace', - 'annotations': {}}} - self.assertRaises(cfg.RequiredOptError, driver.get_project, resource) - - -class TestAnnotationPodProjectDriver(TestAnnotationProjectDriverBase): - - pod = {'metadata': {'namespace': 'fake_namespace'}} - - def test_get_project(self): - driver = annotation_project.AnnotationPodProjectDriver() - self._get_project_from_namespace(self.pod, driver) - self._get_project_from_configure_option(self.pod, driver) - self._project_id_not_set(self.pod, driver) - - -class TestAnnotationServiceProjectDriver(TestAnnotationProjectDriverBase): - - service = {'metadata': {'namespace': 'fake_namespace'}} - - def test_get_project(self): - driver = annotation_project.AnnotationPodProjectDriver() - self._get_project_from_namespace(self.service, driver) - self._get_project_from_configure_option(self.service, driver) - self._project_id_not_set(self.service, driver) - - -class TestAnnotationNetworkPolicyProjectDriver( - TestAnnotationProjectDriverBase): - - network_policy = {'metadata': {'namespace': 'fake_namespace'}} - - def test_get_project(self): - driver = annotation_project.AnnotationPodProjectDriver() - self._get_project_from_namespace(self.network_policy, driver) - self._get_project_from_configure_option(self.network_policy, driver) - self._project_id_not_set(self.network_policy, driver) - - -class TestAnnotationNamespaceProjectDriver(test_base.TestCase): - - project_id = 'fake_project_id' - driver = annotation_project.AnnotationNamespaceProjectDriver() - - def test_get_project_from_annotation(self): - namespace = {'metadata': { - 'annotations': { - constants.K8s_ANNOTATION_PROJECT: self.project_id}}} - project_id = self.driver.get_project(namespace) - self.assertEqual(self.project_id, project_id) - - @mock.patch('kuryr_kubernetes.config.CONF') - def test_get_project_from_configure_option(self, m_cfg): - m_cfg.neutron_defaults.project = self.project_id - namespace = {'metadata': {'name': 'fake_namespace'}} - project_id = self.driver.get_project(namespace) - self.assertEqual(self.project_id, project_id) - - @mock.patch('kuryr_kubernetes.config.CONF') - def test_project_not_set(self, m_cfg): - m_cfg.neutron_defaults.project = "" - namespace = {'metadata': {'name': 'fake_namespace'}} - self.assertRaises( - cfg.RequiredOptError, self.driver.get_project, namespace) diff --git a/kuryr_kubernetes/tests/unit/controller/drivers/test_base.py b/kuryr_kubernetes/tests/unit/controller/drivers/test_base.py deleted file mode 100644 index c96e98691..000000000 --- a/kuryr_kubernetes/tests/unit/controller/drivers/test_base.py +++ /dev/null @@ -1,114 +0,0 @@ -# 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 -from unittest import mock - - -from kuryr_kubernetes.controller.drivers import base as d_base -from kuryr_kubernetes.tests import base as test_base - - -class _TestDriver(d_base.DriverBase, metaclass=abc.ABCMeta): - ALIAS = 'test_alias' - - @abc.abstractmethod - def test(self): - raise NotImplementedError() - - -class TestDriverBase(test_base.TestCase): - - @mock.patch.object(d_base, '_DRIVER_MANAGERS') - @mock.patch('kuryr_kubernetes.config.CONF') - @mock.patch('stevedore.driver.DriverManager') - def test_get_instance(self, m_stv_mgr, m_cfg, m_mgrs): - m_drv = mock.MagicMock(spec=_TestDriver) - m_mgr = mock.MagicMock() - m_mgr.driver = m_drv - m_mgrs.__getitem__.return_value = m_mgr - - self.assertEqual(m_drv, _TestDriver.get_instance()) - m_cfg.assert_not_called() - m_stv_mgr.assert_not_called() - - @mock.patch.object(d_base, '_DRIVER_MANAGERS') - @mock.patch('kuryr_kubernetes.config.CONF') - @mock.patch('stevedore.driver.DriverManager') - def test_get_instance_not_loaded(self, m_stv_mgr, m_cfg, m_mgrs): - alias = _TestDriver.ALIAS - cfg_name = '%s_driver' % (alias) - mgr_key = '%s:_from_cfg:default' % (alias) - drv_name = 'driver_impl' - namespace = '%s.%s' % (d_base._DRIVER_NAMESPACE_BASE, alias) - m_cfg.kubernetes.__getitem__.return_value = drv_name - m_drv = mock.MagicMock(spec=_TestDriver) - m_mgr = mock.MagicMock() - m_mgr.driver = m_drv - m_stv_mgr.return_value = m_mgr - m_mgrs.__getitem__.side_effect = KeyError - - self.assertEqual(m_drv, _TestDriver.get_instance()) - m_cfg.kubernetes.__getitem__.assert_called_with(cfg_name) - m_stv_mgr.assert_called_with(namespace=namespace, name=drv_name, - invoke_on_load=True) - m_mgrs.__setitem__.assert_called_once_with(mgr_key, m_mgr) - - @mock.patch.object(d_base, '_DRIVER_MANAGERS') - @mock.patch('kuryr_kubernetes.config.CONF') - @mock.patch('stevedore.driver.DriverManager') - def test_get_instance_invalid_type(self, m_stv_mgr, m_cfg, m_mgrs): - class _InvalidDriver(object): - pass - - m_drv = mock.MagicMock(spec=_InvalidDriver) - m_mgr = mock.MagicMock() - m_mgr.driver = m_drv - m_mgrs.__getitem__.return_value = m_mgr - - self.assertRaises(TypeError, _TestDriver.get_instance) - m_cfg.assert_not_called() - m_stv_mgr.assert_not_called() - - -class TestMultiVIFDriver(test_base.TestCase): - - @mock.patch.object(d_base, '_MULTI_VIF_DRIVERS', []) - @mock.patch('kuryr_kubernetes.config.CONF') - def test_get_enabled_drivers(self, m_cfg): - cfg_name = 'multi_vif_drivers' - drv_name = 'driver_impl' - m_cfg.kubernetes.__getitem__.return_value = [drv_name] - m_drv = mock.MagicMock() - d_base.MultiVIFDriver.get_instance = mock.MagicMock(return_value=m_drv) - - self.assertIn(m_drv, d_base.MultiVIFDriver.get_enabled_drivers()) - m_cfg.kubernetes.__getitem__.assert_called_once_with(cfg_name) - - @mock.patch.object(d_base, '_MULTI_VIF_DRIVERS', []) - @mock.patch('kuryr_kubernetes.config.CONF') - def test_get_enabled_drivers_multiple(self, m_cfg): - cfg_name = 'multi_vif_drivers' - drv1_name = 'driver_impl_1' - drv2_name = 'driver_impl_2' - m_cfg.kubernetes.__getitem__.return_value = [drv1_name, drv2_name] - m_drv1 = mock.MagicMock() - m_drv2 = mock.MagicMock() - d_base.MultiVIFDriver.get_instance = mock.MagicMock() - d_base.MultiVIFDriver.get_instance.side_effect = [m_drv1, m_drv2] - - self.assertIn(m_drv1, d_base.MultiVIFDriver.get_enabled_drivers()) - self.assertIn(m_drv2, d_base.MultiVIFDriver.get_enabled_drivers()) - m_cfg.kubernetes.__getitem__.assert_called_once_with(cfg_name) diff --git a/kuryr_kubernetes/tests/unit/controller/drivers/test_default_project.py b/kuryr_kubernetes/tests/unit/controller/drivers/test_default_project.py deleted file mode 100644 index 7c1e29baf..000000000 --- a/kuryr_kubernetes/tests/unit/controller/drivers/test_default_project.py +++ /dev/null @@ -1,89 +0,0 @@ -# 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. - -from unittest import mock - -from oslo_config import cfg - -from kuryr_kubernetes.controller.drivers import default_project -from kuryr_kubernetes.tests import base as test_base - - -class TestDefaultPodProjectDriver(test_base.TestCase): - - @mock.patch('kuryr_kubernetes.config.CONF') - def test_get_project(self, m_cfg): - project_id = mock.sentinel.project_id - pod = mock.sentinel.pod - m_cfg.neutron_defaults.project = project_id - driver = default_project.DefaultPodProjectDriver() - - self.assertEqual(project_id, driver.get_project(pod)) - - def test_get_project_not_set(self): - pod = mock.sentinel.pod - driver = default_project.DefaultPodProjectDriver() - self.assertRaises(cfg.RequiredOptError, driver.get_project, pod) - - -class TestDefaultServiceProjectDriver(test_base.TestCase): - - @mock.patch('kuryr_kubernetes.config.CONF') - def test_get_project(self, m_cfg): - project_id = mock.sentinel.project_id - service = mock.sentinel.service - m_cfg.neutron_defaults.project = project_id - driver = default_project.DefaultServiceProjectDriver() - - self.assertEqual(project_id, driver.get_project(service)) - - def test_get_project_not_set(self): - service = mock.sentinel.service - driver = default_project.DefaultServiceProjectDriver() - self.assertRaises(cfg.RequiredOptError, driver.get_project, service) - - -class TestDefaultNamespaceProjectDriver(test_base.TestCase): - - @mock.patch('kuryr_kubernetes.config.CONF') - def test_get_project(self, m_cfg): - project_id = mock.sentinel.project_id - namespace = mock.sentinel.namespace - m_cfg.neutron_defaults.project = project_id - driver = default_project.DefaultNamespaceProjectDriver() - - self.assertEqual(project_id, driver.get_project(namespace)) - - def test_get_project_not_set(self): - namespace = mock.sentinel.namespace - driver = default_project.DefaultNamespaceProjectDriver() - self.assertRaises(cfg.RequiredOptError, driver.get_project, namespace) - - -class TestDefaultNetworkPolicyProjectDriver(test_base.TestCase): - - @mock.patch('kuryr_kubernetes.config.CONF') - def test_get_project(self, m_cfg): - project_id = mock.sentinel.project_id - policy = mock.sentinel.policy - m_cfg.neutron_defaults.project = project_id - driver = default_project.DefaultNetworkPolicyProjectDriver() - - self.assertEqual(project_id, driver.get_project(policy)) - - def test_get_project_not_set(self): - policy = mock.sentinel.policy - driver = default_project.DefaultNetworkPolicyProjectDriver() - self.assertRaises(cfg.RequiredOptError, driver.get_project, policy) diff --git a/kuryr_kubernetes/tests/unit/controller/drivers/test_default_security_groups.py b/kuryr_kubernetes/tests/unit/controller/drivers/test_default_security_groups.py deleted file mode 100644 index bc6c5ad08..000000000 --- a/kuryr_kubernetes/tests/unit/controller/drivers/test_default_security_groups.py +++ /dev/null @@ -1,69 +0,0 @@ -# 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. - -from unittest import mock - -from oslo_config import cfg - -from kuryr_kubernetes.controller.drivers import default_security_groups -from kuryr_kubernetes.tests import base as test_base - - -class TestDefaultPodSecurityGroupsDriver(test_base.TestCase): - - @mock.patch('kuryr_kubernetes.config.CONF') - def test_get_security_groups(self, m_cfg): - sg_list = [mock.sentinel.sg_id] - project_id = mock.sentinel.project_id - pod = mock.sentinel.pod - m_cfg.neutron_defaults.pod_security_groups = sg_list - driver = default_security_groups.DefaultPodSecurityGroupsDriver() - - ret = driver.get_security_groups(pod, project_id) - - self.assertEqual(sg_list, ret) - self.assertIsNot(sg_list, ret) - - def test_get_security_groups_not_set(self): - project_id = mock.sentinel.project_id - pod = mock.sentinel.pod - driver = default_security_groups.DefaultPodSecurityGroupsDriver() - - self.assertRaises(cfg.RequiredOptError, driver.get_security_groups, - pod, project_id) - - -class TestDefaultServiceSecurityGroupsDriver(test_base.TestCase): - - @mock.patch('kuryr_kubernetes.config.CONF') - def test_get_security_groups(self, m_cfg): - sg_list = [mock.sentinel.sg_id] - project_id = mock.sentinel.project_id - service = mock.sentinel.service - m_cfg.neutron_defaults.pod_security_groups = sg_list - driver = default_security_groups.DefaultServiceSecurityGroupsDriver() - - ret = driver.get_security_groups(service, project_id) - - self.assertEqual(sg_list, ret) - self.assertIsNot(sg_list, ret) - - def test_get_security_groups_not_set(self): - project_id = mock.sentinel.project_id - service = mock.sentinel.service - driver = default_security_groups.DefaultServiceSecurityGroupsDriver() - - self.assertRaises(cfg.RequiredOptError, driver.get_security_groups, - service, project_id) diff --git a/kuryr_kubernetes/tests/unit/controller/drivers/test_default_subnet.py b/kuryr_kubernetes/tests/unit/controller/drivers/test_default_subnet.py deleted file mode 100644 index 90dc1fdee..000000000 --- a/kuryr_kubernetes/tests/unit/controller/drivers/test_default_subnet.py +++ /dev/null @@ -1,78 +0,0 @@ -# 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. - -from unittest import mock - -from oslo_config import cfg - -from kuryr_kubernetes.controller.drivers import default_subnet -from kuryr_kubernetes.tests import base as test_base - - -class TestDefaultPodSubnetDriver(test_base.TestCase): - - @mock.patch('kuryr_kubernetes.utils.get_subnet') - @mock.patch('kuryr_kubernetes.config.CONF') - def test_get_subnets(self, m_cfg, m_get_subnet): - subnet_id = mock.sentinel.subnet_id - subnet = mock.sentinel.subnet - pod = mock.sentinel.pod - project_id = mock.sentinel.project_id - m_cfg.neutron_defaults.pod_subnet = subnet_id - m_get_subnet.return_value = subnet - driver = default_subnet.DefaultPodSubnetDriver() - - subnets = driver.get_subnets(pod, project_id) - - self.assertEqual({subnet_id: subnet}, subnets) - m_get_subnet.assert_called_once_with(subnet_id) - - @mock.patch('kuryr_kubernetes.utils.get_subnet') - def test_get_subnets_not_set(self, m_get_subnet): - pod = mock.sentinel.pod - project_id = mock.sentinel.project_id - driver = default_subnet.DefaultPodSubnetDriver() - - self.assertRaises(cfg.RequiredOptError, driver.get_subnets, - pod, project_id) - m_get_subnet.assert_not_called() - - -class TestDefaultServiceSubnetDriver(test_base.TestCase): - - @mock.patch('kuryr_kubernetes.utils.get_subnet') - @mock.patch('kuryr_kubernetes.config.CONF') - def test_get_subnets(self, m_cfg, m_get_subnet): - subnet_id = mock.sentinel.subnet_id - subnet = mock.sentinel.subnet - service = mock.sentinel.service - project_id = mock.sentinel.project_id - m_cfg.neutron_defaults.service_subnet = subnet_id - m_get_subnet.return_value = subnet - driver = default_subnet.DefaultServiceSubnetDriver() - - subnets = driver.get_subnets(service, project_id) - - self.assertEqual({subnet_id: subnet}, subnets) - m_get_subnet.assert_called_once_with(subnet_id) - - @mock.patch('kuryr_kubernetes.utils.get_subnet') - def test_get_subnets_not_set(self, m_get_subnet): - service = mock.sentinel.service - project_id = mock.sentinel.project_id - driver = default_subnet.DefaultPodSubnetDriver() - self.assertRaises(cfg.RequiredOptError, driver.get_subnets, - service, project_id) - m_get_subnet.assert_not_called() diff --git a/kuryr_kubernetes/tests/unit/controller/drivers/test_lb_public_ip.py b/kuryr_kubernetes/tests/unit/controller/drivers/test_lb_public_ip.py deleted file mode 100644 index 1bc78769d..000000000 --- a/kuryr_kubernetes/tests/unit/controller/drivers/test_lb_public_ip.py +++ /dev/null @@ -1,390 +0,0 @@ -# Copyright (c) 2017 RedHat, 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. - -from openstack import exceptions as os_exc -from openstack.network.v2 import floating_ip as os_fip -from openstack.network.v2 import subnet as os_subnet -from unittest import mock - -from oslo_config import cfg - -from kuryr_kubernetes.controller.drivers import lb_public_ip\ - as d_lb_public_ip -from kuryr_kubernetes.controller.drivers import public_ip -from kuryr_kubernetes.objects import lbaas as obj_lbaas -from kuryr_kubernetes.tests import base as test_base -from kuryr_kubernetes.tests.unit import kuryr_fixtures as k_fix - -CONF = cfg.CONF - - -class TestFloatingIpServicePubIPDriverDriver(test_base.TestCase): - - def test_acquire_service_pub_ip_info_clusterip(self): - cls = d_lb_public_ip.FloatingIpServicePubIPDriver - m_driver = mock.Mock(spec=cls) - m_driver._drv_pub_ip = public_ip.FipPubIpDriver() - project_id = mock.sentinel.project_id - cur_service_pub_ip_info = None - service = {'spec': {'type': 'ClusterIP'}} - - result = cls.acquire_service_pub_ip_info(m_driver, service, project_id, - cur_service_pub_ip_info) - self.assertIsNone(result) - - def test_acquire_service_pub_ip_info_usr_specified_ip(self): - cls = d_lb_public_ip.FloatingIpServicePubIPDriver - m_driver = mock.Mock(spec=cls) - m_driver._drv_pub_ip = public_ip.FipPubIpDriver() - os_net = self.useFixture(k_fix.MockNetworkClient()).client - - fip = os_fip.FloatingIP( - floating_ip_address='1.2.3.4', - port_id=None, - id='a2a62ea7-e3bf-40df-8c09-aa0c29876a6b', - ) - os_net.ips.return_value = (ip for ip in [fip]) - project_id = mock.sentinel.project_id - spec_type = 'LoadBalancer' - spec_lb_ip = '1.2.3.4' - CONF.set_override('external_svc_net', - '9767e1bd-40a7-4294-8e59-29dd77edb0e3', - group='neutron_defaults') - - expected_resp = { - 'ip_id': fip.id, - 'ip_addr': fip.floating_ip_address, - 'alloc_method': 'user' - } - - result = cls.acquire_service_pub_ip_info(m_driver, spec_type, - spec_lb_ip, project_id) - self.assertEqual(result, expected_resp) - - def test_acquire_service_pub_ip_info_user_specified_non_exist_fip(self): - cls = d_lb_public_ip.FloatingIpServicePubIPDriver - m_driver = mock.Mock(spec=cls) - m_driver._drv_pub_ip = public_ip.FipPubIpDriver() - os_net = self.useFixture(k_fix.MockNetworkClient()).client - - fip = os_fip.FloatingIP( - floating_ip_address='1.2.3.5', - port_id=None, - ) - os_net.ips.return_value = (ip for ip in [fip]) - - project_id = mock.sentinel.project_id - - spec_type = 'LoadBalancer' - spec_lb_ip = '1.2.3.4' - - result = cls.acquire_service_pub_ip_info(m_driver, spec_type, - spec_lb_ip, project_id) - self.assertIsNone(result) - - def test_acquire_service_pub_ip_info_user_specified_occupied_fip(self): - cls = d_lb_public_ip.FloatingIpServicePubIPDriver - m_driver = mock.Mock(spec=cls) - m_driver._drv_pub_ip = public_ip.FipPubIpDriver() - os_net = self.useFixture(k_fix.MockNetworkClient()).client - - fip = os_fip.FloatingIP( - floating_ip_address='1.2.3.4', - port_id='ec29d641-fec4-4f67-928a-124a76b3a8e6', - ) - os_net.ips.return_value = (ip for ip in [fip]) - - project_id = mock.sentinel.project_id - spec_type = 'LoadBalancer' - spec_lb_ip = '1.2.3.4' - - result = cls.acquire_service_pub_ip_info(m_driver, spec_type, - spec_lb_ip, project_id) - self.assertIsNone(result) - - @mock.patch('kuryr_kubernetes.config.CONF') - def test_acquire_service_pub_ip_info_pool_net_not_defined(self, m_cfg): - driver = d_lb_public_ip.FloatingIpServicePubIPDriver() - public_net = '' - m_cfg.neutron_defaults.external_svc_net = public_net - os_net = self.useFixture(k_fix.MockNetworkClient()).client - os_net.ips.return_value = (ip for ip in []) - project_id = mock.sentinel.project_id - spec_type = 'LoadBalancer' - spec_lb_ip = None - - result = driver.acquire_service_pub_ip_info( - spec_type, spec_lb_ip, project_id) - self.assertIsNone(result) - - @mock.patch('kuryr_kubernetes.config.CONF') - def test_acquire_service_pub_ip_info_pool_subnet_is_none(self, m_cfg): - cls = d_lb_public_ip.FloatingIpServicePubIPDriver - m_driver = mock.Mock(spec=cls) - m_driver._drv_pub_ip = public_ip.FipPubIpDriver() - os_net = self.useFixture(k_fix.MockNetworkClient()).client - public_net = mock.sentinel.public_subnet - m_cfg.neutron_defaults.external_svc_net = public_net - m_cfg.neutron_defaults.external_svc_subnet = None - - os_net.get_subnet.return_value = os_subnet.Subnet( - network_id='ec29d641-fec4-4f67-928a-124a76b3a8e6', - ) - fip = os_fip.FloatingIP( - floating_ip_address='1.2.3.5', - id='ec29d641-fec4-4f67-928a-124a76b3a888', - ) - os_net.create_ip.return_value = fip - - project_id = mock.sentinel.project_id - spec_type = 'LoadBalancer' - spec_lb_ip = None - - expected_resp = { - 'ip_id': fip.id, - 'ip_addr': fip.floating_ip_address, - 'alloc_method': 'pool' - } - - result = cls.acquire_service_pub_ip_info(m_driver, spec_type, - spec_lb_ip, project_id) - self.assertEqual(result, expected_resp) - - @mock.patch('kuryr_kubernetes.config.CONF') - def test_acquire_service_pub_ip_info_alloc_from_pool(self, m_cfg): - cls = d_lb_public_ip.FloatingIpServicePubIPDriver - m_driver = mock.Mock(spec=cls) - m_driver._drv_pub_ip = public_ip.FipPubIpDriver() - os_net = self.useFixture(k_fix.MockNetworkClient()).client - m_cfg.neutron_defaults.external_svc_subnet = (mock.sentinel - .external_svc_subnet) - - os_net.get_subnet.return_value = os_subnet.Subnet( - network_id='ec29d641-fec4-4f67-928a-124a76b3a8e6', - ) - fip = os_fip.FloatingIP( - floating_ip_address='1.2.3.5', - id='ec29d641-fec4-4f67-928a-124a76b3a888', - ) - os_net.create_ip.return_value = fip - - project_id = mock.sentinel.project_id - spec_type = 'LoadBalancer' - spec_lb_ip = None - - expected_resp = { - 'ip_id': fip.id, - 'ip_addr': fip.floating_ip_address, - 'alloc_method': 'pool' - } - - result = cls.acquire_service_pub_ip_info(m_driver, spec_type, - spec_lb_ip, project_id) - self.assertEqual(result, expected_resp) - - def test_release_pub_ip_empty_lb_ip_info(self): - cls = d_lb_public_ip.FloatingIpServicePubIPDriver - m_driver = mock.Mock(spec=cls) - service_pub_ip_info = None - - rc = cls.release_pub_ip(m_driver, service_pub_ip_info) - self.assertIs(rc, True) - - def test_release_pub_ip_alloc_method_non_pool(self): - cls = d_lb_public_ip.FloatingIpServicePubIPDriver - m_driver = mock.Mock(spec=cls) - - fip = os_fip.FloatingIP( - floating_ip_address='1.2.3.5', - id='ec29d641-fec4-4f67-928a-124a76b3a888', - ) - - service_pub_ip_info = { - 'ip_id': fip.id, - 'ip_addr': fip.floating_ip_address, - 'alloc_method': 'kk' - } - - rc = cls.release_pub_ip(m_driver, service_pub_ip_info) - self.assertIs(rc, True) - - def test_release_pub_ip_alloc_method_user(self): - cls = d_lb_public_ip.FloatingIpServicePubIPDriver - m_driver = mock.Mock(spec=cls) - - fip = os_fip.FloatingIP( - floating_ip_address='1.2.3.5', - id='ec29d641-fec4-4f67-928a-124a76b3a888', - ) - - service_pub_ip_info = { - 'ip_id': fip.id, - 'ip_addr': fip.floating_ip_address, - 'alloc_method': 'user' - } - - rc = cls.release_pub_ip(m_driver, service_pub_ip_info) - self.assertIs(rc, True) - - def test_release_pub_ip_alloc_method_pool_neutron_exception(self): - cls = d_lb_public_ip.FloatingIpServicePubIPDriver - m_driver = mock.Mock(spec=cls) - m_driver._drv_pub_ip = public_ip.FipPubIpDriver() - os_net = self.useFixture(k_fix.MockNetworkClient()).client - os_net.delete_ip.side_effect = os_exc.SDKException - - fip = os_fip.FloatingIP( - floating_ip_address='1.2.3.5', - id='ec29d641-fec4-4f67-928a-124a76b3a888', - ) - - service_pub_ip_info = { - 'ip_id': fip.id, - 'ip_addr': fip.floating_ip_address, - 'alloc_method': 'pool' - } - rc = cls.release_pub_ip(m_driver, service_pub_ip_info) - self.assertIs(rc, False) - - def test_release_pub_ip_alloc_method_pool_neutron_succeeded(self): - cls = d_lb_public_ip.FloatingIpServicePubIPDriver - m_driver = mock.Mock(spec=cls) - m_driver._drv_pub_ip = public_ip.FipPubIpDriver() - self.useFixture(k_fix.MockNetworkClient()).client - - fip = os_fip.FloatingIP( - floating_ip_address='1.2.3.5', - id='ec29d641-fec4-4f67-928a-124a76b3a888', - ) - - service_pub_ip_info = { - 'ip_id': fip.id, - 'ip_addr': fip.floating_ip_address, - 'alloc_method': 'pool' - } - rc = cls.release_pub_ip(m_driver, service_pub_ip_info) - self.assertIs(rc, True) - - def test_associate_pub_ip_empty_params(self): - cls = d_lb_public_ip.FloatingIpServicePubIPDriver - m_driver = mock.Mock(spec=cls) - os_net = self.useFixture(k_fix.MockNetworkClient()).client - os_net.update_floatingip.return_value = None - - service_pub_ip_info = None - vip_port_id = None - - result = cls.associate_pub_ip(m_driver, service_pub_ip_info, - vip_port_id) - self.assertIsNone(result) - - def test_associate_lb_fip_id_not_exist(self): - cls = d_lb_public_ip.FloatingIpServicePubIPDriver - m_driver = mock.Mock(spec=cls) - os_net = self.useFixture(k_fix.MockNetworkClient()).client - os_net.update_floatingip.return_value = None - m_driver._drv_pub_ip = public_ip.FipPubIpDriver() - - fip = os_fip.FloatingIP( - floating_ip_address='1.2.3.5', - id='ec29d641-fec4-4f67-928a-124a76b3a888', - ) - service_pub_ip_info = (obj_lbaas - .LBaaSPubIp(ip_id=0, - ip_addr=fip.floating_ip_address, - alloc_method='pool')) - service_pub_ip_info = { - 'ip_id': 0, - 'ip_addr': fip.floating_ip_address, - 'alloc_method': 'pool' - } - - vip_port_id = 'ec29d641-fec4-4f67-928a-124a76b3a777' - - result = cls.associate_pub_ip(m_driver, service_pub_ip_info, - vip_port_id) - self.assertIsNone(result) - - def test_associate_lb_fip_id_not_exist_neutron_exception(self): - cls = d_lb_public_ip.FloatingIpServicePubIPDriver - m_driver = mock.Mock(spec=cls) - m_driver._drv_pub_ip = public_ip.FipPubIpDriver() - os_net = self.useFixture(k_fix.MockNetworkClient()).client - os_net.update_ip.side_effect = os_exc.SDKException - - fip = os_fip.FloatingIP( - floating_ip_address='1.2.3.5', - id='ec29d641-fec4-4f67-928a-124a76b3a888', - ) - - service_pub_ip_info = { - 'ip_id': fip.id, - 'ip_addr': fip.floating_ip_address, - 'alloc_method': 'pool' - } - vip_port_id = 'ec29d641-fec4-4f67-928a-124a76b3a777' - - self.assertRaises(os_exc.SDKException, cls.associate_pub_ip, - m_driver, service_pub_ip_info, vip_port_id) - - def test_disassociate_pub_ip_empty_param(self): - cls = d_lb_public_ip.FloatingIpServicePubIPDriver - m_driver = mock.Mock(spec=cls) - self.useFixture(k_fix.MockNetworkClient()).client - service_pub_ip_info = None - - result = cls.disassociate_pub_ip(m_driver, service_pub_ip_info) - - self.assertIsNone(result) - - def test_disassociate_pub_ip_fip_id_not_exist(self): - cls = d_lb_public_ip.FloatingIpServicePubIPDriver - m_driver = mock.Mock(spec=cls) - m_driver._drv_pub_ip = public_ip.FipPubIpDriver() - os_net = self.useFixture(k_fix.MockNetworkClient()).client - os_net.update_floatingip.return_value = None - fip = os_fip.FloatingIP( - floating_ip_address='1.2.3.5', - id='ec29d641-fec4-4f67-928a-124a76b3a888', - ) - service_pub_ip_info = { - 'ip_id': 0, - 'ip_addr': fip.floating_ip_address, - 'alloc_method': 'pool' - } - - result = cls.disassociate_pub_ip(m_driver, service_pub_ip_info) - - self.assertIsNone(result) - - def test_disassociate_pub_ip_neutron_exception(self): - cls = d_lb_public_ip.FloatingIpServicePubIPDriver - m_driver = mock.Mock(spec=cls) - m_driver._drv_pub_ip = public_ip.FipPubIpDriver() - os_net = self.useFixture(k_fix.MockNetworkClient()).client - os_net.update_ip.side_effect = os_exc.SDKException - fip = os_fip.FloatingIP( - floating_ip_address='1.2.3.5', - id='ec29d641-fec4-4f67-928a-124a76b3a888', - ) - - service_pub_ip_info = { - 'ip_id': fip.id, - 'ip_addr': fip.floating_ip_address, - 'alloc_method': 'pool' - } - - self.assertRaises(os_exc.SDKException, cls.disassociate_pub_ip, - m_driver, service_pub_ip_info) diff --git a/kuryr_kubernetes/tests/unit/controller/drivers/test_lbaasv2.py b/kuryr_kubernetes/tests/unit/controller/drivers/test_lbaasv2.py deleted file mode 100644 index d6b6e2e05..000000000 --- a/kuryr_kubernetes/tests/unit/controller/drivers/test_lbaasv2.py +++ /dev/null @@ -1,1602 +0,0 @@ -# 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. - -from unittest import mock - -from openstack import exceptions as os_exc -from openstack.load_balancer.v2 import listener as o_lis -from openstack.load_balancer.v2 import load_balancer as o_lb -from openstack.load_balancer.v2 import member as o_mem -from openstack.load_balancer.v2 import pool as o_pool -from openstack.network.v2 import port as os_port -from oslo_config import cfg - -from kuryr_kubernetes import constants as k_const -from kuryr_kubernetes.controller.drivers import lbaasv2 as d_lbaasv2 -from kuryr_kubernetes import exceptions as k_exc -from kuryr_kubernetes.objects import lbaas as obj_lbaas -from kuryr_kubernetes.tests import base as test_base -from kuryr_kubernetes.tests import fake -from kuryr_kubernetes.tests.unit import kuryr_fixtures as k_fix - -CONF = cfg.CONF - -OCTAVIA_VERSIONS = { - 'regionOne': { - 'public': { - 'load-balancer': [ - { - 'status': 'SUPPORTED', - 'version': '2.0', - 'raw_status': u'SUPPORTED', - }, - { - 'status': 'SUPPORTED', - 'version': '2.1', - 'raw_status': u'SUPPORTED', - }, - { - 'status': 'CURRENT', - 'version': '2.2', - 'raw_status': u'CURRENT', - }, - ], - }, - }, -} - -BAD_OCTAVIA_VERSIONS = { - 'regionOne': { - 'public': { - 'load-balancer': [ - { - 'status': 'CURRENT', - 'version': None, - 'raw_status': u'CURRENT', - }, - ], - }, - }, -} - - -class TestLBaaSv2Driver(test_base.TestCase): - @mock.patch('kuryr_kubernetes.controller.drivers.lbaasv2.LBaaSv2Driver.' - 'get_octavia_version', return_value=(2, 5)) - def test_add_tags(self, _m_get): - CONF.set_override('resource_tags', ['foo'], group='neutron_defaults') - self.addCleanup(CONF.clear_override, 'resource_tags', - group='neutron_defaults') - d = d_lbaasv2.LBaaSv2Driver() - req = {} - d.add_tags('loadbalancer', req) - self.assertEqual({'tags': ['foo']}, req) - - @mock.patch('kuryr_kubernetes.controller.drivers.lbaasv2.LBaaSv2Driver.' - 'get_octavia_version', return_value=(2, 5)) - def test_add_tags_no_tag(self, _m_get): - d = d_lbaasv2.LBaaSv2Driver() - req = {} - d.add_tags('loadbalancer', req) - self.assertEqual({}, req) - - @mock.patch('kuryr_kubernetes.controller.drivers.lbaasv2.LBaaSv2Driver.' - 'get_octavia_version', return_value=(2, 4)) - def test_add_tags_no_support(self, _m_get): - CONF.set_override('resource_tags', ['foo'], group='neutron_defaults') - self.addCleanup(CONF.clear_override, 'resource_tags', - group='neutron_defaults') - d = d_lbaasv2.LBaaSv2Driver() - for res in ('loadbalancer', 'listener', 'pool'): - req = {} - d.add_tags(res, req) - self.assertEqual({'description': 'foo'}, req, - 'No description added to resource %s' % res) - - @mock.patch('kuryr_kubernetes.controller.drivers.lbaasv2.LBaaSv2Driver.' - 'get_octavia_version', return_value=(2, 4)) - def test_add_tags_no_support_resource_no_description(self, _m_get): - CONF.set_override('resource_tags', ['foo'], group='neutron_defaults') - self.addCleanup(CONF.clear_override, 'resource_tags', - group='neutron_defaults') - d = d_lbaasv2.LBaaSv2Driver() - for res in ('member', 'rule'): - req = {} - d.add_tags(res, req) - self.assertEqual({}, req, 'Unnecessary description added to ' - 'resource %s' % res) - - def test_get_octavia_version(self): - lbaas = self.useFixture(k_fix.MockLBaaSClient()).client - lbaas.get_all_version_data.return_value = OCTAVIA_VERSIONS - self.assertEqual((2, 2), - d_lbaasv2.LBaaSv2Driver.get_octavia_version(None)) - - def test_get_octavia_version_is_none(self): - lbaas = self.useFixture(k_fix.MockLBaaSClient()).client - lbaas.get_all_version_data.return_value = BAD_OCTAVIA_VERSIONS - self.assertRaises(k_exc.UnreachableOctavia, - d_lbaasv2.LBaaSv2Driver.get_octavia_version, None) - - def test_ensure_loadbalancer(self): - os_net = self.useFixture(k_fix.MockNetworkClient()).client - cls = d_lbaasv2.LBaaSv2Driver - m_driver = mock.Mock(spec=d_lbaasv2.LBaaSv2Driver) - expected_resp = { - 'provide': 'octavia', - 'port_id': 'D3FA400A-F543-4B91-9CD3-047AF0CE42E2', - 'security_groups': [] - } - project_id = 'TEST_PROJECT' - subnet_id = 'D3FA400A-F543-4B91-9CD3-047AF0CE42D1' - ip = '1.2.3.4' - sg_ids = ['foo', 'bar'] - lb_name = 'just_a_name' - - m_driver._ensure_loadbalancer.return_value = expected_resp - os_net.update_port = mock.Mock() - resp = cls.ensure_loadbalancer(m_driver, lb_name, project_id, - subnet_id, ip, sg_ids, 'ClusterIP') - m_driver._ensure_loadbalancer.assert_called_once_with( - mock.ANY) - req = m_driver._ensure_loadbalancer.call_args[0][0] - self.assertEqual(lb_name, req['name']) - self.assertEqual(project_id, req['project_id']) - self.assertEqual(subnet_id, req['subnet_id']) - self.assertEqual(ip, str(req['ip'])) - self.assertEqual(expected_resp, resp) - os_net.update_port.assert_not_called() - - def test_ensure_loadbalancer_not_ready(self): - cls = d_lbaasv2.LBaaSv2Driver - m_driver = mock.Mock(spec=d_lbaasv2.LBaaSv2Driver) - name = 'TEST_NAME' - project_id = 'TEST_PROJECT' - subnet_id = 'D3FA400A-F543-4B91-9CD3-047AF0CE42D1' - ip = '1.2.3.4' - # TODO(ivc): handle security groups - sg_ids = [] - - m_driver._ensure_loadbalancer.return_value = None - self.assertRaises(k_exc.ResourceNotReady, cls.ensure_loadbalancer, - m_driver, name, project_id, subnet_id, ip, - sg_ids, 'ClusterIP') - - def test_cascade_release_loadbalancer(self): - self.useFixture(k_fix.MockNetworkClient()).client - lbaas = self.useFixture(k_fix.MockLBaaSClient()).client - lbaas.lbaas_loadbalancer_path = "boo %s" - cls = d_lbaasv2.LBaaSv2Driver - m_driver = mock.Mock(spec=d_lbaasv2.LBaaSv2Driver) - loadbalancer = { - 'name': 'TEST_NAME', - 'project_id': 'TEST_PROJECT', - 'subnet_id': 'D3FA400A-F543-4B91-9CD3-047AF0CE42D1', - 'ip': '1.2.3.4', - 'security_groups': [], - 'id': '00EE9E11-91C2-41CF-8FD4-7970579E5C4C', - 'provider': None - } - - cls.release_loadbalancer(m_driver, loadbalancer) - - m_driver._release.assert_called_once_with( - loadbalancer, loadbalancer, lbaas.delete_load_balancer, - loadbalancer['id'], cascade=True) - - def _test_ensure_listener(self): - cls = d_lbaasv2.LBaaSv2Driver - m_driver = mock.Mock(spec=d_lbaasv2.LBaaSv2Driver) - expected_resp = mock.sentinel.expected_resp - project_id = 'TEST_PROJECT' - loadbalancer_id = '00EE9E11-91C2-41CF-8FD4-7970579E5C4C' - protocol = 'TCP' - port = 1234 - loadbalancer = { - 'name': 'TEST_NAME', - 'project_id': project_id, - 'subnet_id': 'D3FA400A-F543-4B91-9CD3-047AF0CE42D1', - 'ip': '1.2.3.4', - 'id': '00EE9E11-91C2-41CF-8FD4-7970579E5C4C', - 'provider': 'amphora' - } - # TODO(ivc): handle security groups - m_driver._ensure_provisioned.return_value = expected_resp - - resp = cls.ensure_listener(m_driver, loadbalancer, protocol, port) - - m_driver._ensure_provisioned.assert_called_once_with( - loadbalancer, mock.ANY, m_driver._create_listener, - m_driver._find_listener, d_lbaasv2._LB_STS_POLL_SLOW_INTERVAL) - listener = m_driver._ensure_provisioned.call_args[0][1] - - self.assertEqual("%s:%s:%s" % (loadbalancer['name'], protocol, port), - listener['name']) - self.assertEqual(project_id, listener['project_id']) - self.assertEqual(loadbalancer_id, listener['loadbalancer_id']) - self.assertEqual(protocol, listener['protocol']) - self.assertEqual(port, listener['port']) - self.assertEqual(expected_resp, resp) - - def test_ensure_listener_bad_request_exception(self): - cls = d_lbaasv2.LBaaSv2Driver - m_driver = mock.Mock(spec=d_lbaasv2.LBaaSv2Driver) - port = 1234 - protocol = 'TCP' - loadbalancer = { - 'name': 'TEST_NAME', - 'project_id': 'TEST_PROJECT', - 'subnet_id': 'D3FA400A-F543-4B91-9CD3-047AF0CE42D1', - 'ip': '1.2.3.4', - 'id': '00EE9E11-91C2-41CF-8FD4-7970579E5C4C', - 'provider': 'amphora' - } - m_driver._ensure_provisioned.side_effect = os_exc.BadRequestException - - resp = cls.ensure_listener(m_driver, loadbalancer, - protocol, port) - self.assertIsNone(resp) - - def test_release_listener(self): - os_net = self.useFixture(k_fix.MockNetworkClient()).client - lbaas = self.useFixture(k_fix.MockLBaaSClient()).client - os_net.security_group_rules.return_value = (x for x in []) - cls = d_lbaasv2.LBaaSv2Driver - m_driver = mock.Mock(spec=d_lbaasv2.LBaaSv2Driver) - m_driver._get_vip_port.return_value = os_port.Port( - security_group_ids=[mock.sentinel.sg_id], - ) - loadbalancer = { - 'name': 'TEST_NAME', - 'project_id': 'TEST_PROJECT', - 'subnet_id': 'D3FA400A-F543-4B91-9CD3-047AF0CE42D1', - 'ip': '1.2.3.4', - 'id': '00EE9E11-91C2-41CF-8FD4-7970579E5C4C', - 'security_groups': [], - 'provider': 'amphora' - } - listener = { - 'name': 'TEST_NAME', - 'project_id': 'TEST_PROJECT', - 'loadbalancer_id': '00EE9E11-91C2-41CF-8FD4-7970579E5C4C', - 'protocol': 'TCP', - 'port': 1234, - 'id': 'A57B7771-6050-4CA8-A63C-443493EC98AB' - } - - cls.release_listener(m_driver, loadbalancer, listener) - - m_driver._release.assert_called_once_with(loadbalancer, listener, - lbaas.delete_listener, - listener['id']) - - def test_ensure_pool(self): - cls = d_lbaasv2.LBaaSv2Driver - m_driver = mock.Mock(spec=d_lbaasv2.LBaaSv2Driver) - expected_resp = mock.sentinel.expected_resp - loadbalancer = { - 'project_id': 'TEST_PROJECT', - 'id': '00EE9E11-91C2-41CF-8FD4-7970579E5C4C', - } - listener = { - 'id': 'A57B7771-6050-4CA8-A63C-443493EC98AB', - 'name': 'TEST_LISTENER_NAME', - 'protocol': 'TCP', - } - m_driver._ensure_provisioned.return_value = expected_resp - - resp = cls.ensure_pool(m_driver, loadbalancer, listener) - - m_driver._ensure_provisioned.assert_called_once_with( - loadbalancer, mock.ANY, m_driver._create_pool, - m_driver._find_pool) - pool = m_driver._ensure_provisioned.call_args[0][1] - self.assertEqual(listener['name'], pool['name']) - self.assertEqual(loadbalancer['project_id'], pool['project_id']) - self.assertEqual(listener['id'], pool['listener_id']) - self.assertEqual(listener['protocol'], pool['protocol']) - self.assertEqual(expected_resp, resp) - - def test_release_pool(self): - lbaas = self.useFixture(k_fix.MockLBaaSClient()).client - cls = d_lbaasv2.LBaaSv2Driver - m_driver = mock.Mock(spec=d_lbaasv2.LBaaSv2Driver) - loadbalancer = mock.Mock() - pool = { - 'name': 'TEST_NAME', - 'project_id': 'TEST_PROJECT', - 'loadbalancer_id': '00EE9E11-91C2-41CF-8FD4-7970579E5C4C', - 'listener_id': 'A57B7771-6050-4CA8-A63C-443493EC98AB', - 'protocol': 'TCP', - 'id': 'D4F35594-27EB-4F4C-930C-31DD40F53B77' - } - - cls.release_pool(m_driver, loadbalancer, pool) - - m_driver._release.assert_called_once_with(loadbalancer, pool, - lbaas.delete_pool, - pool['id']) - - def test_ensure_member(self): - lbaas = self.useFixture(k_fix.MockLBaaSClient()).client - cls = d_lbaasv2.LBaaSv2Driver - m_driver = mock.Mock(spec=d_lbaasv2.LBaaSv2Driver) - expected_resp = mock.sentinel.expected_resp - loadbalancer = { - 'id': '00EE9E11-91C2-41CF-8FD4-7970579E5C4C', - 'project_id': 'TEST_PROJECT' - } - pool = { - 'id': 'D4F35594-27EB-4F4C-930C-31DD40F53B77', - 'project_id': 'TEST_PROJECT' - } - - subnet_id = 'D3FA400A-F543-4B91-9CD3-047AF0CE42D1' - ip = '1.2.3.4' - port = 1234 - namespace = 'TEST_NAMESPACE' - name = 'TEST_NAME' - target_ref = {'namespace': namespace, 'name': name} - m_driver._ensure_provisioned.return_value = expected_resp - - resp = cls.ensure_member(m_driver, loadbalancer, pool, - subnet_id, ip, port, - target_ref['namespace'], target_ref['name']) - - m_driver._ensure_provisioned.assert_called_once_with( - loadbalancer, mock.ANY, m_driver._create_member, - m_driver._find_member, update=lbaas.update_member) - member = m_driver._ensure_provisioned.call_args[0][1] - self.assertEqual("%s/%s:%s" % (namespace, name, port), member['name']) - self.assertEqual(pool['project_id'], member['project_id']) - self.assertEqual(pool['id'], member['pool_id']) - self.assertEqual(subnet_id, member['subnet_id']) - self.assertEqual(ip, str(member['ip'])) - self.assertEqual(port, member['port']) - self.assertEqual(expected_resp, resp) - - def test_release_member(self): - lbaas = self.useFixture(k_fix.MockLBaaSClient()).client - cls = d_lbaasv2.LBaaSv2Driver - m_driver = mock.Mock(spec=d_lbaasv2.LBaaSv2Driver) - loadbalancer = { - 'name': 'TEST_NAME', - 'project_id': 'TEST_PROJECT', - 'subnet_id': 'D3FA400A-F543-4B91-9CD3-047AF0CE42D1', - 'ip': '1.2.3.4', - 'security_groups': [], - 'provider': None - } - - member = { - 'name': 'TEST_NAME', - 'project_id': 'TEST_PROJECT', - 'pool_id': 'D4F35594-27EB-4F4C-930C-31DD40F53B77', - 'subnet_id': 'D3FA400A-F543-4B91-9CD3-047AF0CE42D1', - 'ip': '1.2.3.4', - 'port': 1234, - 'id': '3A70CEC0-392D-4BC1-A27C-06E63A0FD54F' - } - - cls.release_member(m_driver, loadbalancer, member) - - m_driver._release.assert_called_once_with(loadbalancer, member, - lbaas.delete_member, - member['id'], - member['pool_id']) - - def test_create_loadbalancer(self): - cls = d_lbaasv2.LBaaSv2Driver - m_driver = mock.Mock(spec=d_lbaasv2.LBaaSv2Driver) - lbaas = self.useFixture(k_fix.MockLBaaSClient()).client - loadbalancer = { - 'name': 'TEST_NAME', - 'project_id': 'TEST_PROJECT', - 'subnet_id': 'D3FA400A-F543-4B91-9CD3-047AF0CE42D1', - 'ip': '1.2.3.4', - 'security_groups': [], - 'provider': None - } - - loadbalancer_id = '00EE9E11-91C2-41CF-8FD4-7970579E5C4C' - req = { - 'name': loadbalancer['name'], - 'project_id': loadbalancer['project_id'], - 'vip_address': str(loadbalancer['ip']), - 'vip_subnet_id': loadbalancer['subnet_id'], - } - resp = o_lb.LoadBalancer(id=loadbalancer_id, provider='haproxy') - lbaas.create_load_balancer.return_value = resp - m_driver._get_vip_port.return_value = os_port.Port( - id=mock.sentinel.port_id, - ) - - ret = cls._create_loadbalancer(m_driver, loadbalancer) - lbaas.create_load_balancer.assert_called_once_with(**req) - self.assertEqual(loadbalancer, ret) - self.assertEqual(loadbalancer_id, ret['id']) - - def test_create_loadbalancer_provider_defined(self): - cls = d_lbaasv2.LBaaSv2Driver - m_driver = mock.Mock(spec=d_lbaasv2.LBaaSv2Driver) - lbaas = self.useFixture(k_fix.MockLBaaSClient()).client - loadbalancer = { - 'name': 'TEST_NAME', - 'project_id': 'TEST_PROJECT', - 'subnet_id': 'D3FA400A-F543-4B91-9CD3-047AF0CE42D1', - 'ip': '1.2.3.4', - 'security_groups': [], - 'provider': 'amphora' - } - loadbalancer_id = '00EE9E11-91C2-41CF-8FD4-7970579E5C4C' - req = { - 'name': loadbalancer['name'], - 'project_id': loadbalancer['project_id'], - 'vip_address': str(loadbalancer['ip']), - 'vip_subnet_id': loadbalancer['subnet_id'], - 'provider': loadbalancer['provider'], - } - resp = o_lb.LoadBalancer(id=loadbalancer_id, provider='amphora') - lbaas.create_load_balancer.return_value = resp - m_driver._get_vip_port.return_value = os_port.Port( - id=mock.sentinel.port_id, - ) - - ret = cls._create_loadbalancer(m_driver, loadbalancer) - lbaas.create_load_balancer.assert_called_once_with(**req) - self.assertEqual(loadbalancer, ret) - self.assertEqual(loadbalancer_id, ret['id']) - - def test_create_loadbalancer_provider_mismatch(self): - cls = d_lbaasv2.LBaaSv2Driver - m_driver = mock.Mock(spec=d_lbaasv2.LBaaSv2Driver) - lbaas = self.useFixture(k_fix.MockLBaaSClient()).client - loadbalancer = { - 'name': 'TEST_NAME', - 'project_id': 'TEST_PROJECT', - 'subnet_id': 'D3FA400A-F543-4B91-9CD3-047AF0CE42D1', - 'ip': '1.2.3.4', - 'security_groups': [], - 'provider': 'amphora' - } - loadbalancer_id = '00EE9E11-91C2-41CF-8FD4-7970579E5C4C' - req = { - 'name': loadbalancer['name'], - 'project_id': loadbalancer['project_id'], - 'vip_address': str(loadbalancer['ip']), - 'vip_subnet_id': loadbalancer['subnet_id'], - 'provider': loadbalancer['provider'], - } - resp = o_lb.LoadBalancer(id=loadbalancer_id, provider='haproxy') - lbaas.create_load_balancer.return_value = resp - m_driver._get_vip_port.return_value = os_port.Port( - id=mock.sentinel.port_id, - ) - - ret = cls._create_loadbalancer(m_driver, loadbalancer) - lbaas.create_load_balancer.assert_called_once_with(**req) - self.assertIsNone(ret) - - def test_find_loadbalancer(self): - lbaas = self.useFixture(k_fix.MockLBaaSClient()).client - cls = d_lbaasv2.LBaaSv2Driver - m_driver = mock.Mock(spec=d_lbaasv2.LBaaSv2Driver) - loadbalancer = { - 'name': 'TEST_NAME', - 'project_id': 'TEST_PROJECT', - 'subnet_id': 'D3FA400A-F543-4B91-9CD3-047AF0CE42D1', - 'ip': '1.2.3.4', - 'security_groups': [], - 'provider': 'haproxy' - } - loadbalancer_id = '00EE9E11-91C2-41CF-8FD4-7970579E5C4C' - resp = iter([o_lb.LoadBalancer(id=loadbalancer_id, provider='haproxy', - provisioning_status='ACTIVE')]) - lbaas.load_balancers.return_value = resp - m_driver._get_vip_port.return_value = os_port.Port( - id=mock.sentinel.port_id, - ) - - ret = cls._find_loadbalancer(m_driver, loadbalancer) - lbaas.load_balancers.assert_called_once_with( - name=loadbalancer['name'], - project_id=loadbalancer['project_id'], - vip_address=str(loadbalancer['ip']), - vip_subnet_id=loadbalancer['subnet_id'], - provider='haproxy') - self.assertEqual(loadbalancer, ret) - self.assertEqual(loadbalancer_id, ret['id']) - m_driver.release_loadbalancer.assert_not_called() - - def test_find_loadbalancer_not_found(self): - lbaas = self.useFixture(k_fix.MockLBaaSClient()).client - cls = d_lbaasv2.LBaaSv2Driver - m_driver = mock.Mock(spec=d_lbaasv2.LBaaSv2Driver) - loadbalancer = obj_lbaas.LBaaSLoadBalancer( - name='TEST_NAME', project_id='TEST_PROJECT', ip='1.2.3.4', - subnet_id='D3FA400A-F543-4B91-9CD3-047AF0CE42D1') - loadbalancer = { - 'name': 'TEST_NAME', - 'project_id': 'TEST_PROJECT', - 'subnet_id': 'D3FA400A-F543-4B91-9CD3-047AF0CE42D1', - 'ip': '1.2.3.4', - 'provider': None - } - resp = iter([]) - lbaas.load_balancers.return_value = resp - - ret = cls._find_loadbalancer(m_driver, loadbalancer) - lbaas.load_balancers.assert_called_once_with( - name=loadbalancer['name'], - project_id=loadbalancer['project_id'], - vip_address=str(loadbalancer['ip']), - vip_subnet_id=loadbalancer['subnet_id'], - provider=None) - self.assertIsNone(ret) - m_driver.release_loadbalancer.assert_not_called() - - def test_find_loadbalancer_error(self): - lbaas = self.useFixture(k_fix.MockLBaaSClient()).client - cls = d_lbaasv2.LBaaSv2Driver - m_driver = mock.Mock(spec=d_lbaasv2.LBaaSv2Driver) - loadbalancer = { - 'name': 'test_namespace/test_name', - 'project_id': 'TEST_PROJECT', - 'subnet_id': 'D3FA400A-F543-4B91-9CD3-047AF0CE42D1', - 'ip': '1.2.3.4', - 'provider': None - } - loadbalancer_id = '00EE9E11-91C2-41CF-8FD4-7970579E5C4C' - resp = iter([o_lb.LoadBalancer(id=loadbalancer_id, provider='haproxy', - provisioning_status='ERROR')]) - lbaas.load_balancers.return_value = resp - m_driver._get_vip_port.return_value = os_port.Port( - id=mock.sentinel.port_id, - ) - - ret = cls._find_loadbalancer(m_driver, loadbalancer) - lbaas.load_balancers.assert_called_once_with( - name=loadbalancer['name'], - project_id=loadbalancer['project_id'], - vip_address=str(loadbalancer['ip']), - vip_subnet_id=loadbalancer['subnet_id'], - provider=None) - self.assertIsNone(ret) - m_driver.release_loadbalancer.assert_called_once() - - def test_create_listener(self): - cls = d_lbaasv2.LBaaSv2Driver - m_driver = mock.Mock(spec=d_lbaasv2.LBaaSv2Driver) - lbaas = self.useFixture(k_fix.MockLBaaSClient()).client - listener = { - 'name': 'TEST_NAME', - 'project_id': 'TEST_PROJECT', - 'loadbalancer_id': '00EE9E11-91C2-41CF-8FD4-7970579E5C4C', - 'protocol': 'TCP', - 'port': 1234 - } - listener_id = 'A57B7771-6050-4CA8-A63C-443493EC98AB' - - req = { - 'name': listener['name'], - 'project_id': listener['project_id'], - 'loadbalancer_id': listener['loadbalancer_id'], - 'protocol': listener['protocol'], - 'protocol_port': listener['port']} - resp = o_lis.Listener(id=listener_id) - lbaas.create_listener.return_value = resp - - ret = cls._create_listener(m_driver, listener) - lbaas.create_listener.assert_called_once_with(**req) - self.assertEqual(listener, ret) - self.assertEqual(listener_id, ret['id']) - - def test_create_listener_with_different_timeouts(self): - cls = d_lbaasv2.LBaaSv2Driver - m_driver = mock.Mock(spec=d_lbaasv2.LBaaSv2Driver) - lbaas = self.useFixture(k_fix.MockLBaaSClient()).client - listener = { - 'name': 'TEST_NAME', - 'project_id': 'TEST_PROJECT', - 'loadbalancer_id': '00EE9E11-91C2-41CF-8FD4-7970579E5C4C', - 'protocol': 'TCP', - 'port': 5678, - 'timeout_client_data': 75000, - 'timeout_member_data': 0 - } - listener_id = 'A57B7771-6050-4CA8-A63C-443493EC98AB' - - req = { - 'name': listener['name'], - 'project_id': listener['project_id'], - 'loadbalancer_id': listener['loadbalancer_id'], - 'protocol': listener['protocol'], - 'protocol_port': listener['port'], - 'timeout_client_data': listener['timeout_client_data']} - resp = o_lis.Listener(id=listener_id) - lbaas.create_listener.return_value = resp - - ret = cls._create_listener(m_driver, listener) - lbaas.create_listener.assert_called_once_with(**req) - self.assertEqual(listener, ret) - self.assertEqual(listener_id, ret['id']) - - def test_find_listener(self): - lbaas = self.useFixture(k_fix.MockLBaaSClient()).client - cls = d_lbaasv2.LBaaSv2Driver - m_driver = mock.Mock(spec=d_lbaasv2.LBaaSv2Driver) - loadbalancer = { - 'id': '00EE9E11-91C2-41CF-8FD4-7970579E5C4C', - } - listener = { - 'name': 'TEST_NAME', - 'project_id': 'TEST_PROJECT', - 'loadbalancer_id': '00EE9E11-91C2-41CF-8FD4-7970579E5C4C', - 'protocol': 'TCP', - 'port': 1234 - } - listener_id = 'A57B7771-6050-4CA8-A63C-443493EC98AB' - lbaas.listeners.return_value = iter([o_lis.Listener(id=listener_id)]) - - ret = cls._find_listener(m_driver, listener, loadbalancer) - lbaas.listeners.assert_called_once_with( - name=listener['name'], - project_id=listener['project_id'], - load_balancer_id=listener['loadbalancer_id'], - protocol=listener['protocol'], - protocol_port=listener['port']) - self.assertEqual(listener, ret) - self.assertEqual(listener_id, ret['id']) - - def test_find_listener_not_found(self): - lbaas = self.useFixture(k_fix.MockLBaaSClient()).client - cls = d_lbaasv2.LBaaSv2Driver - m_driver = mock.Mock(spec=d_lbaasv2.LBaaSv2Driver) - loadbalancer = { - 'id': '00EE9E11-91C2-41CF-8FD4-7970579E5C4C', - } - listener = { - 'name': 'TEST_NAME', - 'project_id': 'TEST_PROJECT', - 'loadbalancer_id': '00EE9E11-91C2-41CF-8FD4-7970579E5C4C', - 'protocol': 'TCP', - 'port': 1234 - } - resp = iter([]) - lbaas.listeners.return_value = resp - - ret = cls._find_listener(m_driver, listener, loadbalancer) - lbaas.listeners.assert_called_once_with( - name=listener['name'], - project_id=listener['project_id'], - load_balancer_id=listener['loadbalancer_id'], - protocol=listener['protocol'], - protocol_port=listener['port']) - self.assertIsNone(ret) - - def test_create_pool(self): - cls = d_lbaasv2.LBaaSv2Driver - m_driver = mock.Mock(spec=d_lbaasv2.LBaaSv2Driver) - lbaas = self.useFixture(k_fix.MockLBaaSClient()).client - lb_algorithm = 'ROUND_ROBIN' - pool = { - 'name': 'TEST_NAME', - 'project_id': 'TEST_PROJECT', - 'loadbalancer_id': '00EE9E11-91C2-41CF-8FD4-7970579E5C4C', - 'listener_id': 'A57B7771-6050-4CA8-A63C-443493EC98AB', - 'protocol': 'TCP' - } - pool_id = 'D4F35594-27EB-4F4C-930C-31DD40F53B77' - - req = { - 'name': pool['name'], - 'project_id': pool['project_id'], - 'listener_id': pool['listener_id'], - 'loadbalancer_id': pool['loadbalancer_id'], - 'protocol': pool['protocol'], - 'lb_algorithm': lb_algorithm} - resp = o_pool.Pool(id=pool_id) - lbaas.create_pool.return_value = resp - - ret = cls._create_pool(m_driver, pool) - lbaas.create_pool.assert_called_once_with(**req) - self.assertEqual(pool, ret) - self.assertEqual(pool_id, ret['id']) - - def test_create_pool_with_different_lb_algorithm(self): - cls = d_lbaasv2.LBaaSv2Driver - m_driver = mock.Mock(spec=d_lbaasv2.LBaaSv2Driver) - lbaas = self.useFixture(k_fix.MockLBaaSClient()).client - lb_algorithm = 'SOURCE_IP_PORT' - pool = { - 'name': 'TEST_NAME', - 'project_id': 'TEST_PROJECT', - 'loadbalancer_id': '00EE9E11-91C2-41CF-8FD4-7970579E5C4C', - 'listener_id': 'A57B7771-6050-4CA8-A63C-443493EC98AB', - 'protocol': 'TCP' - } - pool_id = 'D4F35594-27EB-4F4C-930C-31DD40F53B77' - req = { - 'name': pool['name'], - 'project_id': pool['project_id'], - 'listener_id': pool['listener_id'], - 'loadbalancer_id': pool['loadbalancer_id'], - 'protocol': pool['protocol'], - 'lb_algorithm': lb_algorithm} - resp = o_pool.Pool(id=pool_id) - lbaas.create_pool.return_value = resp - CONF.set_override('lb_algorithm', lb_algorithm, - group='octavia_defaults') - self.addCleanup(CONF.clear_override, 'lb_algorithm', - group='octavia_defaults') - - ret = cls._create_pool(m_driver, pool) - lbaas.create_pool.assert_called_once_with(**req) - self.assertEqual(pool, ret) - self.assertEqual(pool_id, ret['id']) - - def test_create_pool_conflict(self): - cls = d_lbaasv2.LBaaSv2Driver - m_driver = mock.Mock(spec=d_lbaasv2.LBaaSv2Driver) - lbaas = self.useFixture(k_fix.MockLBaaSClient()).client - lb_algorithm = 'ROUND_ROBIN' - pool = { - 'name': 'TEST_NAME', - 'project_id': 'TEST_PROJECT', - 'loadbalancer_id': '00EE9E11-91C2-41CF-8FD4-7970579E5C4C', - 'listener_id': 'A57B7771-6050-4CA8-A63C-443493EC98AB', - 'protocol': 'TCP' - } - req = { - 'name': pool['name'], - 'project_id': pool['project_id'], - 'listener_id': pool['listener_id'], - 'loadbalancer_id': pool['loadbalancer_id'], - 'protocol': pool['protocol'], - 'lb_algorithm': lb_algorithm} - lbaas.create_pool.side_effect = os_exc.BadRequestException - - self.assertRaises(os_exc.BadRequestException, cls._create_pool, - m_driver, pool) - lbaas.create_pool.assert_called_once_with(**req) - - def test_find_pool_by_listener(self): - lbaas = self.useFixture(k_fix.MockLBaaSClient()).client - cls = d_lbaasv2.LBaaSv2Driver - m_driver = mock.Mock(spec=d_lbaasv2.LBaaSv2Driver) - loadbalancer = { - 'id': '00EE9E11-91C2-41CF-8FD4-7970579E5C4C', - } - pool = { - 'name': 'TEST_NAME', - 'project_id': 'TEST_PROJECT', - 'loadbalancer_id': '00EE9E11-91C2-41CF-8FD4-7970579E5C4C', - 'listener_id': 'A57B7771-6050-4CA8-A63C-443493EC98AB', - 'protocol': 'TCP' - } - pool_id = 'D4F35594-27EB-4F4C-930C-31DD40F53B77' - resp = [o_pool.Pool(id=pool_id, - listeners=[{"id": pool['listener_id']}])] - lbaas.pools.return_value = resp - - ret = cls._find_pool(m_driver, pool, loadbalancer) - lbaas.pools.assert_called_once_with( - name=pool['name'], - project_id=pool['project_id'], - loadbalancer_id=pool['loadbalancer_id'], - protocol=pool['protocol']) - self.assertEqual(pool, ret) - self.assertEqual(pool_id, ret['id']) - - def test_find_pool_by_listener_not_found(self): - lbaas = self.useFixture(k_fix.MockLBaaSClient()).client - cls = d_lbaasv2.LBaaSv2Driver - m_driver = mock.Mock(spec=d_lbaasv2.LBaaSv2Driver) - loadbalancer = { - 'id': '00EE9E11-91C2-41CF-8FD4-7970579E5C4C' - } - pool = { - 'name': 'TEST_NAME', - 'project_id': 'TEST_PROJECT', - 'loadbalancer_id': '00EE9E11-91C2-41CF-8FD4-7970579E5C4C', - 'listener_id': 'A57B7771-6050-4CA8-A63C-443493EC98AB', - 'protocol': 'TCP' - } - resp = [] - lbaas.pools.return_value = resp - - ret = cls._find_pool(m_driver, pool, loadbalancer) - lbaas.pools.assert_called_once_with( - name=pool['name'], - project_id=pool['project_id'], - loadbalancer_id=pool['loadbalancer_id'], - protocol=pool['protocol']) - self.assertIsNone(ret) - - def test_create_member(self): - cls = d_lbaasv2.LBaaSv2Driver - m_driver = mock.Mock(spec=d_lbaasv2.LBaaSv2Driver) - lbaas = self.useFixture(k_fix.MockLBaaSClient()).client - member = { - 'name': 'TEST_NAME', - 'project_id': 'TEST_PROJECT', - 'pool_id': 'D4F35594-27EB-4F4C-930C-31DD40F53B77', - 'subnet_id': 'D3FA400A-F543-4B91-9CD3-047AF0CE42D1', - 'ip': '1.2.3.4', - 'port': 1234 - } - member_id = '3A70CEC0-392D-4BC1-A27C-06E63A0FD54F' - req = { - 'name': member['name'], - 'project_id': member['project_id'], - 'subnet_id': member['subnet_id'], - 'address': str(member['ip']), - 'protocol_port': member['port']} - resp = o_mem.Member(id=member_id) - lbaas.create_member.return_value = resp - - ret = cls._create_member(m_driver, member) - lbaas.create_member.assert_called_once_with(member['pool_id'], **req) - self.assertEqual(member, ret) - self.assertEqual(member_id, ret['id']) - - def test_find_member(self): - lbaas = self.useFixture(k_fix.MockLBaaSClient()).client - cls = d_lbaasv2.LBaaSv2Driver - m_driver = mock.Mock(spec=d_lbaasv2.LBaaSv2Driver) - loadbalancer = obj_lbaas.LBaaSLoadBalancer() - member = { - 'name': 'TEST_NAME', - 'project_id': 'TEST_PROJECT', - 'pool_id': 'D4F35594-27EB-4F4C-930C-31DD40F53B77', - 'subnet_id': 'D3FA400A-F543-4B91-9CD3-047AF0CE42D1', - 'ip': '1.2.3.4', - 'port': 1234 - } - member_id = '3A70CEC0-392D-4BC1-A27C-06E63A0FD54F' - resp = iter([o_mem.Member(id=member_id, name='TEST_NAME')]) - lbaas.members.return_value = resp - ret = cls._find_member(m_driver, member, loadbalancer) - lbaas.members.assert_called_once_with( - member['pool_id'], - project_id=member['project_id'], - subnet_id=member['subnet_id'], - address=member['ip'], - protocol_port=member['port']) - # the member dict is copied, so the id is added to the return obj - member['id'] = member_id - self.assertEqual(member, ret) - self.assertEqual(member_id, ret['id']) - - def test_find_member_not_found(self): - lbaas = self.useFixture(k_fix.MockLBaaSClient()).client - cls = d_lbaasv2.LBaaSv2Driver - m_driver = mock.Mock(spec=d_lbaasv2.LBaaSv2Driver) - loadbalancer = obj_lbaas.LBaaSLoadBalancer() - member = { - 'name': 'TEST_NAME', - 'project_id': 'TEST_PROJECT', - 'pool_id': 'D4F35594-27EB-4F4C-930C-31DD40F53B77', - 'subnet_id': 'D3FA400A-F543-4B91-9CD3-047AF0CE42D1', - 'ip': '1.2.3.4', - 'port': 1234 - } - resp = iter([]) - lbaas.members.return_value = resp - - ret = cls._find_member(m_driver, member, loadbalancer) - lbaas.members.assert_called_once_with( - member['pool_id'], - project_id=member['project_id'], - subnet_id=member['subnet_id'], - address=member['ip'], - protocol_port=member['port']) - self.assertIsNone(ret) - - def test_ensure(self): - cls = d_lbaasv2.LBaaSv2Driver - m_driver = mock.Mock(spec=d_lbaasv2.LBaaSv2Driver) - obj = mock.Mock() - lb = mock.Mock() - m_create = mock.Mock() - m_find = mock.Mock() - expected_result = mock.sentinel.expected_result - m_create.return_value = expected_result - - ret = cls._ensure(m_driver, m_create, m_find, - obj, lb) - m_create.assert_called_once_with(obj) - self.assertEqual(expected_result, ret) - - def _verify_ensure_with_exception(self, exception_value): - cls = d_lbaasv2.LBaaSv2Driver - m_driver = mock.Mock(spec=d_lbaasv2.LBaaSv2Driver) - obj = mock.Mock() - lb = mock.Mock() - m_create = mock.Mock() - m_find = mock.Mock() - expected_result = None - m_create.side_effect = exception_value - m_find.return_value = expected_result - - ret = cls._ensure(m_driver, m_create, m_find, - obj, lb) - m_create.assert_called_once_with(obj) - m_find.assert_called_once_with(obj, lb) - self.assertEqual(expected_result, ret) - - def test_ensure_with_conflict(self): - self._verify_ensure_with_exception( - os_exc.ConflictException(http_status=409)) - - def test_ensure_with_internalservererror(self): - self._verify_ensure_with_exception( - os_exc.HttpException(http_status=500)) - - def test_request(self): - cls = d_lbaasv2.LBaaSv2Driver - m_driver = mock.Mock(spec=d_lbaasv2.LBaaSv2Driver) - loadbalancer = mock.sentinel.loadbalancer - obj = mock.sentinel.obj - create = mock.sentinel.create - find = mock.sentinel.find - timer = [mock.sentinel.t0] - m_driver._provisioning_timer.return_value = timer - m_driver._ensure.side_effect = os_exc.BadRequestException() - - self.assertRaises(os_exc.BadRequestException, - cls._ensure_provisioned, m_driver, - loadbalancer, obj, create, find) - - m_driver._wait_for_provisioning.assert_has_calls( - [mock.call(loadbalancer, t, d_lbaasv2._LB_STS_POLL_FAST_INTERVAL) - for t in timer]) - m_driver._ensure.assert_has_calls( - [mock.call(create, find, obj, loadbalancer) for _ in timer]) - - def test_ensure_not_ready(self): - cls = d_lbaasv2.LBaaSv2Driver - m_driver = mock.Mock(spec=d_lbaasv2.LBaaSv2Driver) - loadbalancer = mock.sentinel.loadbalancer - obj = mock.sentinel.obj - create = mock.sentinel.create - find = mock.sentinel.find - timer = [mock.sentinel.t0, mock.sentinel.t1] - m_driver._provisioning_timer.return_value = timer - m_driver._ensure.return_value = None - - self.assertRaises(k_exc.ResourceNotReady, cls._ensure_provisioned, - m_driver, - loadbalancer, obj, create, find) - - m_driver._wait_for_provisioning.assert_has_calls( - [mock.call(loadbalancer, t, d_lbaasv2._LB_STS_POLL_FAST_INTERVAL) - for t in timer]) - m_driver._ensure.assert_has_calls( - [mock.call(create, find, obj, loadbalancer) for _ in timer]) - - def test_release(self): - cls = d_lbaasv2.LBaaSv2Driver - m_driver = mock.Mock(spec=d_lbaasv2.LBaaSv2Driver) - loadbalancer = mock.sentinel.loadbalancer - obj = mock.sentinel.obj - m_delete = mock.Mock() - timer = [mock.sentinel.t0, mock.sentinel.t1] - m_driver._provisioning_timer.return_value = timer - - cls._release(m_driver, loadbalancer, obj, m_delete) - - m_driver._wait_for_provisioning.assert_not_called() - m_delete.assert_called_once() - - def test_release_with_wait(self): - cls = d_lbaasv2.LBaaSv2Driver - m_driver = mock.Mock(spec=d_lbaasv2.LBaaSv2Driver) - loadbalancer = mock.sentinel.loadbalancer - obj = mock.sentinel.obj - m_delete = mock.Mock() - timer = [mock.sentinel.t0, mock.sentinel.t1] - m_driver._provisioning_timer.return_value = timer - m_delete.side_effect = [os_exc.BadRequestException, None] - - cls._release(m_driver, loadbalancer, obj, m_delete) - - m_driver._wait_for_provisioning.assert_called_once_with(loadbalancer, - mock.ANY) - self.assertEqual(2, m_delete.call_count) - - def test_release_not_found(self): - cls = d_lbaasv2.LBaaSv2Driver - m_driver = mock.Mock(spec=d_lbaasv2.LBaaSv2Driver) - loadbalancer = mock.sentinel.loadbalancer - obj = mock.sentinel.obj - m_delete = mock.Mock() - timer = [mock.sentinel.t0, mock.sentinel.t1] - m_driver._provisioning_timer.return_value = timer - m_delete.side_effect = os_exc.NotFoundException - - cls._release(m_driver, loadbalancer, obj, m_delete) - - m_driver._wait_for_provisioning.assert_not_called() - self.assertEqual(1, m_delete.call_count) - - def test_release_not_ready(self): - cls = d_lbaasv2.LBaaSv2Driver - m_driver = mock.Mock(spec=d_lbaasv2.LBaaSv2Driver) - loadbalancer = mock.sentinel.loadbalancer - obj = mock.sentinel.obj - m_delete = mock.Mock() - timer = [mock.sentinel.t0, mock.sentinel.t1] - m_driver._provisioning_timer.return_value = timer - m_delete.side_effect = os_exc.ConflictException - - self.assertRaises(k_exc.ResourceNotReady, cls._release, m_driver, - loadbalancer, obj, m_delete) - - call_count = len(timer) - self.assertEqual(call_count, - m_driver._wait_for_provisioning.call_count) - self.assertEqual(call_count, m_delete.call_count) - - def test_wait_for_provisioning(self): - lbaas = self.useFixture(k_fix.MockLBaaSClient()).client - cls = d_lbaasv2.LBaaSv2Driver - m_driver = mock.Mock(spec=d_lbaasv2.LBaaSv2Driver) - loadbalancer = { - 'name': 'TEST_NAME', - 'project_id': 'TEST_PROJECT', - 'subnet_id': 'D3FA400A-F543-4B91-9CD3-047AF0CE42D1', - 'ip': '1.2.3.4', - 'provider': None, - 'id': '00EE9E11-91C2-41CF-8FD4-7970579E5C4C' - } - timeout = mock.sentinel.timeout - timer = [mock.sentinel.t0, mock.sentinel.t1] - m_driver._provisioning_timer.return_value = timer - resp = o_lb.LoadBalancer(provisioning_status='ACTIVE') - lbaas.get_load_balancer.return_value = resp - - cls._wait_for_provisioning(m_driver, loadbalancer, timeout) - - lbaas.get_load_balancer.assert_called_once_with(loadbalancer['id']) - - def test_wait_for_provisioning_not_ready(self): - lbaas = self.useFixture(k_fix.MockLBaaSClient()).client - cls = d_lbaasv2.LBaaSv2Driver - m_driver = mock.Mock(spec=d_lbaasv2.LBaaSv2Driver) - loadbalancer = { - 'name': 'TEST_NAME', - 'project_id': 'TEST_PROJECT', - 'subnet_id': 'D3FA400A-F543-4B91-9CD3-047AF0CE42D1', - 'ip': '1.2.3.4', - 'provider': None, - 'id': '00EE9E11-91C2-41CF-8FD4-7970579E5C4C' - } - timeout = mock.sentinel.timeout - timer = [mock.sentinel.t0, mock.sentinel.t1] - m_driver._provisioning_timer.return_value = timer - resp = o_lb.LoadBalancer(provisioning_status='NOT_ACTIVE') - lbaas.get_load_balancer.return_value = resp - - self.assertRaises(k_exc.ResourceNotReady, cls._wait_for_provisioning, - m_driver, loadbalancer, timeout) - - self.assertEqual(len(timer), lbaas.get_load_balancer.call_count) - - def test_provisioning_timer(self): - # REVISIT(ivc): add test if _provisioning_timer is to stay - self.skipTest("not implemented") - - -class TestLBaaSv2AppyMembersSecurityGroup(test_base.TestCase): - - def setUp(self): - super().setUp() - self.lb = {'id': 'a4de5f1a-ac03-45b1-951d-39f108d52e7d', - 'ip': '10.0.0.142', - 'name': 'default/lb', - 'port_id': '5be1b3c4-7d44-4597-9294-cadafdf1ec69', - 'project_id': '7ef23242bb3f4773a58da681421ab26e', - 'provider': 'amphora', - 'security_groups': ['328900a2-c328-41cc-946f-56ae8720ec0d'], - 'subnet_id': 'c85e2e10-1fad-4218-ad10-7de4aa5de7ce'} - self.port = 80 - self.target_port = 8080 - self.protocol = 'TCP' - self.sg_rule_name = 'default/lb:TCP:80' - self.listener_id = '858869ec-e4fa-4715-b22f-bd08889c6235' - self.new_sgs = ['48cfc812-a442-44bf-989f-8dbaf23a7007'] - self.vip = fake.get_port_obj() - - @mock.patch('kuryr_kubernetes.clients.get_network_client') - def test__apply_members_security_groups_no_enforce(self, gnc): - CONF.set_override('enforce_sg_rules', False, group='octavia_defaults') - self.addCleanup(CONF.clear_override, 'enforce_sg_rules', - group='octavia_defaults') - cls = d_lbaasv2.LBaaSv2Driver - m_driver = mock.Mock(spec=d_lbaasv2.LBaaSv2Driver) - m_driver._get_vip_port.return_value = None - - cls._apply_members_security_groups(m_driver, self.lb, self.port, - self.target_port, self.protocol, - self.sg_rule_name, - self.listener_id, self.new_sgs) - - m_driver._get_vip_port.assert_not_called() - - @mock.patch('kuryr_kubernetes.clients.get_network_client') - def test__apply_members_security_groups_no_vip(self, gnc): - cls = d_lbaasv2.LBaaSv2Driver - m_driver = mock.Mock(spec=d_lbaasv2.LBaaSv2Driver) - m_driver._get_vip_port.return_value = None - - cls._apply_members_security_groups(m_driver, self.lb, self.port, - self.target_port, self.protocol, - self.sg_rule_name, - self.listener_id, self.new_sgs) - - m_driver._get_vip_port.assert_called_once_with(self.lb) - - @mock.patch('kuryr_kubernetes.clients.get_network_client') - def test__apply_members_security_groups_no_sg(self, gnc): - self.new_sgs = None - self.vip.security_group_ids = [] - cls = d_lbaasv2.LBaaSv2Driver - m_driver = mock.Mock(spec=d_lbaasv2.LBaaSv2Driver) - m_driver._get_vip_port.return_value = self.vip - - self.assertRaises(k_exc.ResourceNotReady, - cls._apply_members_security_groups, m_driver, - self.lb, self.port, self.target_port, self.protocol, - self.sg_rule_name, self.listener_id, self.new_sgs) - - m_driver._get_vip_port.assert_called_once_with(self.lb) - - @mock.patch('kuryr_kubernetes.clients.get_network_client') - def test__apply_members_security_groups_conf_with_octavia_acls(self, gnc): - self.new_sgs = None - cls = d_lbaasv2.LBaaSv2Driver - m_driver = mock.Mock(spec=d_lbaasv2.LBaaSv2Driver) - m_driver._get_vip_port = mock.Mock(return_value=self.vip) - m_driver._octavia_acls = True - m_driver._create_listeners_acls = mock.Mock() - - cls._apply_members_security_groups(m_driver, self.lb, self.port, - self.target_port, self.protocol, - self.sg_rule_name, self.listener_id, - self.new_sgs) - - m_driver._get_vip_port.assert_called_once_with(self.lb) - m_driver._create_listeners_acls.assert_called_once_with( - self.lb, self.port, self.target_port, self.protocol, - self.vip.security_group_ids[0], self.new_sgs, self.listener_id) - - def test__apply_members_security_groups_new_sgs(self): - os_net = self.useFixture(k_fix.MockNetworkClient()).client - cls = d_lbaasv2.LBaaSv2Driver - m_driver = mock.Mock(spec=d_lbaasv2.LBaaSv2Driver) - m_driver._get_vip_port.return_value = self.vip - m_driver._octavia_acls = False - os_net.security_group_rules.return_value = [] - CONF.set_override('pod_security_groups', [], group='neutron_defaults') - self.addCleanup(CONF.clear_override, 'pod_security_groups', - group='neutron_defaults') - - cls._apply_members_security_groups(m_driver, self.lb, self.port, - self.target_port, self.protocol, - self.sg_rule_name, self.listener_id, - self.new_sgs) - - m_driver._get_vip_port.assert_called_once_with(self.lb) - os_net.security_group_rules.assert_has_calls([ - mock.call(security_group_id=self.vip.security_group_ids[0], - project_id=self.lb['project_id']), - mock.call(security_group_id=self.new_sgs[0])]) - - def test__apply_members_security_groups_conf_lb_sgs(self): - os_net = self.useFixture(k_fix.MockNetworkClient()).client - cls = d_lbaasv2.LBaaSv2Driver - m_driver = mock.Mock(spec=d_lbaasv2.LBaaSv2Driver) - m_driver._get_vip_port.return_value = self.vip - m_driver._octavia_acls = False - sgr = fake.get_sgr_obj() - os_net.security_group_rules.side_effect = ([], [sgr]) - self.new_sgs = [] - CONF.set_override('pod_security_groups', [], group='neutron_defaults') - self.addCleanup(CONF.clear_override, 'pod_security_groups', - group='neutron_defaults') - - cls._apply_members_security_groups(m_driver, self.lb, self.port, - self.target_port, self.protocol, - self.sg_rule_name, - self.listener_id, self.new_sgs) - - m_driver._get_vip_port.assert_called_once_with(self.lb) - os_net.security_group_rules.assert_has_calls([ - mock.call(security_group_id=self.vip.security_group_ids[0], - project_id=self.lb['project_id']), - mock.call(security_group_id=self.lb['security_groups'][0])]) - os_net.create_security_group_rule.assert_called_once_with( - direction='ingress', - ether_type=k_const.IPv4, - port_range_min=self.port, - port_range_max=self.port, - protocol=self.protocol, - remote_ip_prefix=sgr.remote_ip_prefix, - security_group_id=sgr.security_group_id, - description=self.sg_rule_name) - - def test__apply_members_security_groups_conf_lb_sgs_conflict(self): - os_net = self.useFixture(k_fix.MockNetworkClient()).client - cls = d_lbaasv2.LBaaSv2Driver - m_driver = mock.Mock(spec=d_lbaasv2.LBaaSv2Driver) - m_driver._get_vip_port.return_value = self.vip - m_driver._octavia_acls = False - sgr = fake.get_sgr_obj() - os_net.security_group_rules.side_effect = ([], [sgr]) - os_net.create_security_group_rule.side_effect = (os_exc - .ConflictException) - self.new_sgs = [] - CONF.set_override('pod_security_groups', [], group='neutron_defaults') - self.addCleanup(CONF.clear_override, 'pod_security_groups', - group='neutron_defaults') - - cls._apply_members_security_groups(m_driver, self.lb, self.port, - self.target_port, self.protocol, - self.sg_rule_name, - self.listener_id, self.new_sgs) - - m_driver._get_vip_port.assert_called_once_with(self.lb) - os_net.security_group_rules.assert_has_calls([ - mock.call(security_group_id=self.vip.security_group_ids[0], - project_id=self.lb['project_id']), - mock.call(security_group_id=self.lb['security_groups'][0])]) - os_net.create_security_group_rule.assert_called_once_with( - direction='ingress', - ether_type=k_const.IPv4, - port_range_min=self.port, - port_range_max=self.port, - protocol=self.protocol, - remote_ip_prefix=None, - security_group_id=self.vip.security_group_ids[0], - description=self.sg_rule_name) - - def test__apply_members_security_groups_conf_lb_sgs_sdkexception(self): - os_net = self.useFixture(k_fix.MockNetworkClient()).client - cls = d_lbaasv2.LBaaSv2Driver - m_driver = mock.Mock(spec=d_lbaasv2.LBaaSv2Driver) - m_driver._get_vip_port.return_value = self.vip - m_driver._octavia_acls = False - sgr = fake.get_sgr_obj() - os_net.security_group_rules.side_effect = ([], [sgr]) - os_net.create_security_group_rule.side_effect = os_exc.SDKException - self.new_sgs = [] - CONF.set_override('pod_security_groups', [], group='neutron_defaults') - self.addCleanup(CONF.clear_override, 'pod_security_groups', - group='neutron_defaults') - - cls._apply_members_security_groups(m_driver, self.lb, self.port, - self.target_port, self.protocol, - self.sg_rule_name, - self.listener_id, self.new_sgs) - - m_driver._get_vip_port.assert_called_once_with(self.lb) - os_net.security_group_rules.assert_has_calls([ - mock.call(security_group_id=self.vip.security_group_ids[0], - project_id=self.lb['project_id']), - mock.call(security_group_id=self.lb['security_groups'][0])]) - os_net.create_security_group_rule.assert_called_once_with( - direction='ingress', - ether_type=k_const.IPv4, - port_range_min=self.port, - port_range_max=self.port, - protocol=self.protocol, - remote_ip_prefix=None, - security_group_id=self.vip.security_group_ids[0], - description=self.sg_rule_name) - - @mock.patch("kuryr_kubernetes.utils.get_service_subnet_version", - return_value=k_const.IP_VERSION_6) - def test__apply_members_security_groups_ipv6_add_default(self, gssv): - os_net = self.useFixture(k_fix.MockNetworkClient()).client - cls = d_lbaasv2.LBaaSv2Driver - m_driver = mock.Mock(spec=d_lbaasv2.LBaaSv2Driver) - m_driver._get_vip_port.return_value = self.vip - m_driver._octavia_acls = False - os_net.security_group_rules.return_value = [] - CONF.set_override('pod_security_groups', self.new_sgs, - group='neutron_defaults') - self.addCleanup(CONF.clear_override, 'pod_security_groups', - group='neutron_defaults') - - cls._apply_members_security_groups(m_driver, self.lb, self.port, - self.target_port, self.protocol, - self.sg_rule_name, self.listener_id, - self.new_sgs) - - m_driver._get_vip_port.assert_called_once_with(self.lb) - os_net.security_group_rules.assert_called_once_with( - security_group_id=self.vip.security_group_ids[0], - project_id=self.lb['project_id']) - os_net.create_security_group_rule.assert_called_once_with( - direction='ingress', - ether_type=k_const.IPv6, - port_range_min=self.port, - port_range_max=self.port, - protocol=self.protocol, - security_group_id=self.vip.security_group_ids[0], - description=self.sg_rule_name) - - def test__apply_members_security_groups_add_default_conflict(self): - os_net = self.useFixture(k_fix.MockNetworkClient()).client - cls = d_lbaasv2.LBaaSv2Driver - m_driver = mock.Mock(spec=d_lbaasv2.LBaaSv2Driver) - m_driver._get_vip_port.return_value = self.vip - m_driver._octavia_acls = False - os_net.security_group_rules.return_value = [] - CONF.set_override('pod_security_groups', self.new_sgs, - group='neutron_defaults') - self.addCleanup(CONF.clear_override, 'pod_security_groups', - group='neutron_defaults') - os_net.create_security_group_rule.side_effect = (os_exc - .ConflictException) - - cls._apply_members_security_groups(m_driver, self.lb, self.port, - self.target_port, self.protocol, - self.sg_rule_name, self.listener_id, - self.new_sgs) - - m_driver._get_vip_port.assert_called_once_with(self.lb) - os_net.security_group_rules.assert_called_once_with( - security_group_id=self.vip.security_group_ids[0], - project_id=self.lb['project_id']) - os_net.create_security_group_rule.assert_called_once_with( - direction='ingress', - ether_type=k_const.IPv4, - port_range_min=self.port, - port_range_max=self.port, - protocol=self.protocol, - security_group_id=self.vip.security_group_ids[0], - description=self.sg_rule_name) - - def test__apply_members_security_groups_add_default_sdk_exception(self): - os_net = self.useFixture(k_fix.MockNetworkClient()).client - cls = d_lbaasv2.LBaaSv2Driver - m_driver = mock.Mock(spec=d_lbaasv2.LBaaSv2Driver) - m_driver._get_vip_port.return_value = self.vip - m_driver._octavia_acls = False - os_net.security_group_rules.return_value = [] - CONF.set_override('pod_security_groups', self.new_sgs, - group='neutron_defaults') - self.addCleanup(CONF.clear_override, 'pod_security_groups', - group='neutron_defaults') - os_net.create_security_group_rule.side_effect = os_exc.SDKException - - cls._apply_members_security_groups(m_driver, self.lb, self.port, - self.target_port, self.protocol, - self.sg_rule_name, self.listener_id, - self.new_sgs) - - m_driver._get_vip_port.assert_called_once_with(self.lb) - os_net.security_group_rules.assert_called_once_with( - security_group_id=self.vip.security_group_ids[0], - project_id=self.lb['project_id']) - os_net.create_security_group_rule.assert_called_once_with( - direction='ingress', - ether_type=k_const.IPv4, - port_range_min=self.port, - port_range_max=self.port, - protocol=self.protocol, - security_group_id=self.vip.security_group_ids[0], - description=self.sg_rule_name) - - def test__apply_members_security_groups_same_sg(self): - os_net = self.useFixture(k_fix.MockNetworkClient()).client - cls = d_lbaasv2.LBaaSv2Driver - m_driver = mock.Mock(spec=d_lbaasv2.LBaaSv2Driver) - self.vip.security_group_ids = self.new_sgs - m_driver._get_vip_port.return_value = self.vip - m_driver._octavia_acls = False - os_net.security_group_rules.return_value = [] - CONF.set_override('pod_security_groups', [], group='neutron_defaults') - self.addCleanup(CONF.clear_override, 'pod_security_groups', - group='neutron_defaults') - - cls._apply_members_security_groups(m_driver, self.lb, self.port, - self.target_port, self.protocol, - self.sg_rule_name, self.listener_id, - self.new_sgs) - - m_driver._get_vip_port.assert_called_once_with(self.lb) - os_net.security_group_rules.assert_called_once_with( - security_group_id=self.vip.security_group_ids[0], - project_id=self.lb['project_id']) - - def test__apply_members_security_groups_unmatched_target_port(self): - os_net = self.useFixture(k_fix.MockNetworkClient()).client - cls = d_lbaasv2.LBaaSv2Driver - m_driver = mock.Mock(spec=d_lbaasv2.LBaaSv2Driver) - m_driver._get_vip_port.return_value = self.vip - m_driver._octavia_acls = False - sgr = fake.get_sgr_obj() - self.target_port = 9090 - os_net.security_group_rules.side_effect = ([], [sgr]) - self.new_sgs = [] - CONF.set_override('pod_security_groups', [], group='neutron_defaults') - self.addCleanup(CONF.clear_override, 'pod_security_groups', - group='neutron_defaults') - - cls._apply_members_security_groups(m_driver, self.lb, self.port, - self.target_port, self.protocol, - self.sg_rule_name, - self.listener_id, self.new_sgs) - - m_driver._get_vip_port.assert_called_once_with(self.lb) - os_net.security_group_rules.assert_has_calls([ - mock.call(security_group_id=self.vip.security_group_ids[0], - project_id=self.lb['project_id']), - mock.call(security_group_id=self.lb['security_groups'][0])]) - os_net.create_security_group_rule.assert_not_called() - - def test__apply_members_security_groups_egress(self): - os_net = self.useFixture(k_fix.MockNetworkClient()).client - cls = d_lbaasv2.LBaaSv2Driver - m_driver = mock.Mock(spec=d_lbaasv2.LBaaSv2Driver) - m_driver._get_vip_port.return_value = self.vip - m_driver._octavia_acls = False - sgr = fake.get_sgr_obj(direction='egress') - os_net.security_group_rules.side_effect = ([], [sgr]) - self.new_sgs = [] - CONF.set_override('pod_security_groups', [], group='neutron_defaults') - self.addCleanup(CONF.clear_override, 'pod_security_groups', - group='neutron_defaults') - - cls._apply_members_security_groups(m_driver, self.lb, self.port, - self.target_port, self.protocol, - self.sg_rule_name, - self.listener_id, self.new_sgs) - - m_driver._get_vip_port.assert_called_once_with(self.lb) - os_net.security_group_rules.assert_has_calls([ - mock.call(security_group_id=self.vip.security_group_ids[0], - project_id=self.lb['project_id']), - mock.call(security_group_id=self.lb['security_groups'][0])]) - os_net.create_security_group_rule.assert_not_called() - - def test__apply_members_security_groups_no_delete_lbaas_rules(self): - os_net = self.useFixture(k_fix.MockNetworkClient()).client - cls = d_lbaasv2.LBaaSv2Driver - m_driver = mock.Mock(spec=d_lbaasv2.LBaaSv2Driver) - m_driver._get_vip_port.return_value = self.vip - m_driver._octavia_acls = False - self.lb['security_groups'] = [] - self.new_sgs = [] - sgr = fake.get_sgr_obj() - os_net.security_group_rules.return_value = [sgr] - - cls._apply_members_security_groups(m_driver, self.lb, self.port, - self.target_port, self.protocol, - self.sg_rule_name, - self.listener_id, self.new_sgs) - - m_driver._get_vip_port.assert_called_once_with(self.lb) - os_net.security_group_rules.assert_called_once_with( - security_group_id=self.vip.security_group_ids[0], - project_id=self.lb['project_id']) - os_net.create_security_group_rule.assert_not_called() - - def test__apply_members_security_groups_delete_matched_lbaas_rules(self): - os_net = self.useFixture(k_fix.MockNetworkClient()).client - cls = d_lbaasv2.LBaaSv2Driver - m_driver = mock.Mock(spec=d_lbaasv2.LBaaSv2Driver) - m_driver._get_vip_port.return_value = self.vip - m_driver._octavia_acls = False - sgr = fake.get_sgr_obj() - os_net.security_group_rules.side_effect = ([sgr], [sgr]) - self.new_sgs = [] - CONF.set_override('pod_security_groups', [], group='neutron_defaults') - self.addCleanup(CONF.clear_override, 'pod_security_groups', - group='neutron_defaults') - - cls._apply_members_security_groups(m_driver, self.lb, self.port, - self.target_port, self.protocol, - self.sg_rule_name, - self.listener_id, self.new_sgs) - - m_driver._get_vip_port.assert_called_once_with(self.lb) - os_net.security_group_rules.assert_has_calls([ - mock.call(security_group_id=self.vip.security_group_ids[0], - project_id=self.lb['project_id']), - mock.call(security_group_id=self.lb['security_groups'][0])]) - os_net.create_security_group_rule.assert_called_once_with( - direction='ingress', - ether_type=k_const.IPv4, - port_range_min=self.port, - port_range_max=self.port, - protocol=self.protocol, - remote_ip_prefix=sgr.remote_ip_prefix, - security_group_id=sgr.security_group_id, - description=self.sg_rule_name) - os_net.delete_security_group_rule.assert_called_once_with(sgr.id) - - def test__apply_members_security_groups_delete_unmatched_lbaas_rules(self): - os_net = self.useFixture(k_fix.MockNetworkClient()).client - cls = d_lbaasv2.LBaaSv2Driver - m_driver = mock.Mock(spec=d_lbaasv2.LBaaSv2Driver) - m_driver._get_vip_port.return_value = self.vip - m_driver._octavia_acls = False - sgr = fake.get_sgr_obj() - os_net.security_group_rules.side_effect = ([sgr], [sgr]) - self.new_sgs = [] - CONF.set_override('pod_security_groups', [], group='neutron_defaults') - self.addCleanup(CONF.clear_override, 'pod_security_groups', - group='neutron_defaults') - self.port = 8080 - - cls._apply_members_security_groups(m_driver, self.lb, self.port, - self.target_port, self.protocol, - self.sg_rule_name, - self.listener_id, self.new_sgs) - - m_driver._get_vip_port.assert_called_once_with(self.lb) - os_net.security_group_rules.assert_has_calls([ - mock.call(security_group_id=self.vip.security_group_ids[0], - project_id=self.lb['project_id']), - mock.call(security_group_id=self.lb['security_groups'][0])]) - os_net.create_security_group_rule.assert_called_once_with( - direction='ingress', - ether_type=k_const.IPv4, - port_range_min=self.port, - port_range_max=self.port, - protocol=self.protocol, - remote_ip_prefix=sgr.remote_ip_prefix, - security_group_id=sgr.security_group_id, - description=self.sg_rule_name) - m_driver._delete_rule_if_no_match.assert_called_once_with(sgr, [sgr]) - - def test__apply_members_security_groups_delete_no_default_lbaas_rules( - self): - os_net = self.useFixture(k_fix.MockNetworkClient()).client - cls = d_lbaasv2.LBaaSv2Driver - m_driver = mock.Mock(spec=d_lbaasv2.LBaaSv2Driver) - m_driver._get_vip_port.return_value = self.vip - m_driver._octavia_acls = False - sgr = fake.get_sgr_obj() - os_net.security_group_rules.side_effect = ([sgr], [sgr]) - self.new_sgs = [] - CONF.set_override('pod_security_groups', [], group='neutron_defaults') - self.addCleanup(CONF.clear_override, 'pod_security_groups', - group='neutron_defaults') - m_driver._is_default_rule.return_value = False - - cls._apply_members_security_groups(m_driver, self.lb, self.port, - self.target_port, self.protocol, - self.sg_rule_name, - self.listener_id, self.new_sgs) - - m_driver._get_vip_port.assert_called_once_with(self.lb) - os_net.security_group_rules.assert_has_calls([ - mock.call(security_group_id=self.vip.security_group_ids[0], - project_id=self.lb['project_id']), - mock.call(security_group_id=self.lb['security_groups'][0])]) - os_net.create_security_group_rule.assert_called_once_with( - direction='ingress', - ether_type=k_const.IPv4, - port_range_min=self.port, - port_range_max=self.port, - protocol=self.protocol, - remote_ip_prefix=sgr.remote_ip_prefix, - security_group_id=sgr.security_group_id, - description=self.sg_rule_name) diff --git a/kuryr_kubernetes/tests/unit/controller/drivers/test_multi_vif.py b/kuryr_kubernetes/tests/unit/controller/drivers/test_multi_vif.py deleted file mode 100644 index cde0e1235..000000000 --- a/kuryr_kubernetes/tests/unit/controller/drivers/test_multi_vif.py +++ /dev/null @@ -1,206 +0,0 @@ -# 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 unittest import mock - -from kuryr_kubernetes import constants -from kuryr_kubernetes.controller.drivers import base as drivers -from kuryr_kubernetes.controller.drivers import multi_vif -from kuryr_kubernetes import exceptions -from kuryr_kubernetes.tests import base as test_base -from oslo_serialization import jsonutils - - -def get_pod_obj(): - return { - 'status': { - 'qosClass': 'BestEffort', - 'hostIP': '192.168.1.2', - }, - 'kind': 'Pod', - 'spec': { - 'schedulerName': 'default-scheduler', - 'containers': [{ - 'name': 'busybox', - 'image': 'busybox', - 'resources': {} - }], - 'nodeName': 'kuryr-devstack' - }, - 'metadata': { - 'name': 'busybox-sleep1', - 'namespace': 'default', - 'resourceVersion': '53808', - 'uid': '452176db-4a85-11e7-80bd-fa163e29dbbb', - 'annotations': { - 'openstack.org/kuryr-vif': {}, - 'k8s.v1.cni.cncf.io/networks': - "net-a,net-b,other-ns/net-c" - } - } - } - - -def get_nets(): - return [ - {"name": "net-a"}, - {"name": "net-b"}, - { - "name": "net-c", - "namespace": "other-ns" - } - ] - - -def get_crd_objs(): - return [ - { - 'name': 'net-a', - 'metadata': { - 'annotations': { - 'openstack.org/kuryr-config': - '''{"subnetId": "subnet-a"}''' - } - } - }, - { - 'name': 'net-b', - 'metadata': { - 'annotations': { - 'openstack.org/kuryr-config': - '''{"subnetId": "subnet-b"}''' - } - } - }, - { - 'name': 'net-c', - 'metadata': { - 'annotations': { - 'openstack.org/kuryr-config': - '''{"subnetId": "subnet-c"}''' - } - } - } - ] - - -def get_subnet_objs(): - return [ - {'subnet-a': mock.sentinel.subneta}, - {'subnet-b': mock.sentinel.subnetb}, - {'subnet-c': mock.sentinel.subnetc}, - ] - - -class TestNPWGMultiVIFDriver(test_base.TestCase): - - def setUp(self): - super(TestNPWGMultiVIFDriver, self).setUp() - self._project_id = mock.sentinel.project_id - self._subnet = mock.sentinel.subnet - self._vif = mock.sentinel.vif - self._subnets = [self._subnet] - self._security_groups = mock.sentinel.security_groups - self._pod = get_pod_obj() - self._vif_pool_drv = mock.Mock(spec=drivers.VIFPoolDriver) - self._request_vif = self._vif_pool_drv.request_vif - self._request_vif.return_value = self._vif - - self._cls = multi_vif.NPWGMultiVIFDriver - self._drv = mock.Mock(spec=self._cls) - self._drv._get_networks = mock.Mock() - self._drv._drv_vif_pool = self._vif_pool_drv - - @mock.patch.object(drivers.VIFPoolDriver, 'set_vif_driver') - @mock.patch.object(drivers.VIFPoolDriver, 'get_instance') - def test_init(self, m_get_vif_pool_driver, m_set_vifs_driver): - m_get_vif_pool_driver.return_value = self._vif_pool_drv - self._vif_pool_drv.set_vif_driver = m_set_vifs_driver - - m_drv = multi_vif.NPWGMultiVIFDriver() - self.assertEqual(self._vif_pool_drv, m_drv._drv_vif_pool) - m_get_vif_pool_driver.assert_called_once_with( - specific_driver='multi_pool') - m_set_vifs_driver.assert_called_once() - - @mock.patch('kuryr_kubernetes.utils.get_subnet') - @mock.patch('kuryr_kubernetes.clients.get_kubernetes_client') - def test_request_additional_vifs(self, m_get_client, m_get_subnet): - vifs = [mock.sentinel.vif_a, mock.sentinel.vif_b, mock.sentinel.vif_c] - self._request_vif.side_effect = vifs - net_crds = get_crd_objs() - client = mock.Mock() - m_get_client.return_value = client - m_get_subnet.side_effect = [mock.sentinel.subneta, - mock.sentinel.subnetb, - mock.sentinel.subnetc] - client.get = mock.Mock() - client.get.side_effect = net_crds - self._drv._get_networks.return_value = get_nets() - - self.assertEqual(vifs, self._cls.request_additional_vifs( - self._drv, self._pod, self._project_id, self._security_groups)) - - def test_get_networks_str(self): - networks = get_nets() - self.assertEqual(networks, - self._cls._get_networks(self._drv, self._pod)) - - def test_get_networks_json(self): - networks = get_nets() - self._pod['metadata']['annotations'][ - 'kubernetes.v1.cni.cncf.io/networks'] = jsonutils.dumps(networks) - self.assertEqual(networks, - self._cls._get_networks(self._drv, self._pod)) - - def test_get_networks_with_invalid_annotation(self): - self._pod['metadata']['annotations'][ - constants.K8S_ANNOTATION_NPWG_NETWORK] = 'ns/net-a/invalid' - self.assertRaises(exceptions.InvalidKuryrNetworkAnnotation, - self._cls._get_networks, self._drv, self._pod) - - def test_get_networks_without_annotation(self): - pod = { - 'metadata': { - 'annotations': { - } - } - } - - self.assertEqual([], self._cls._get_networks(self._drv, pod)) - - @mock.patch('kuryr_kubernetes.clients.get_kubernetes_client') - def test_request_additional_vifs_without_networks(self, m_get_client): - self._drv._get_networks.return_value = [] - - self.assertEqual([], - self._cls.request_additional_vifs( - self._drv, self._pod, self._project_id, - self._security_groups)) - m_get_client.assert_not_called() - - @mock.patch('kuryr_kubernetes.clients.get_kubernetes_client') - def test_request_additional_vifs_with_invalid_network(self, m_get_client): - net_crds = get_crd_objs() - client = mock.Mock() - m_get_client.return_value = client - client.get = mock.Mock() - client.get.side_effects = net_crds - networks = [{'invalid_key': 'net-x'}] - self._drv._get_networks.return_value = networks - - self.assertRaises(exceptions.InvalidKuryrNetworkAnnotation, - self._cls.request_additional_vifs, - self._drv, self._pod, self._project_id, - self._security_groups) diff --git a/kuryr_kubernetes/tests/unit/controller/drivers/test_namespace_subnet.py b/kuryr_kubernetes/tests/unit/controller/drivers/test_namespace_subnet.py deleted file mode 100644 index dbc596fdd..000000000 --- a/kuryr_kubernetes/tests/unit/controller/drivers/test_namespace_subnet.py +++ /dev/null @@ -1,296 +0,0 @@ -# Copyright (c) 2018 Red Hat, 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. - -from unittest import mock - -from openstack import exceptions as os_exc -from openstack.network.v2 import network as os_network -from openstack.network.v2 import subnet as os_subnet -from oslo_config import cfg as oslo_cfg - -from kuryr_kubernetes.controller.drivers import namespace_subnet as subnet_drv -from kuryr_kubernetes import exceptions as k_exc -from kuryr_kubernetes.tests import base as test_base -from kuryr_kubernetes.tests.unit import kuryr_fixtures as k_fix - - -def get_pod_obj(): - return { - 'status': { - 'qosClass': 'BestEffort', - 'hostIP': '192.168.1.2', - }, - 'kind': 'Pod', - 'spec': { - 'schedulerName': 'default-scheduler', - 'containers': [{ - 'name': 'busybox', - 'image': 'busybox', - 'resources': {} - }], - 'nodeName': 'kuryr-devstack' - }, - 'metadata': { - 'name': 'busybox-sleep1', - 'namespace': 'default', - 'resourceVersion': '53808', - 'uid': '452176db-4a85-11e7-80bd-fa163e29dbbb', - 'annotations': { - 'openstack.org/kuryr-vif': {} - } - }} - - -class TestNamespacePodSubnetDriver(test_base.TestCase): - - @mock.patch('kuryr_kubernetes.utils.get_subnet') - def test_get_subnets(self, m_get_subnet): - pod = get_pod_obj() - pod_namespace = pod['metadata']['namespace'] - subnet_id = mock.sentinel.subnet_id - subnet = mock.sentinel.subnet - - cls = subnet_drv.NamespacePodSubnetDriver - m_driver = mock.MagicMock(spec=cls) - - m_driver._get_namespace_subnet_id.return_value = subnet_id - m_get_subnet.return_value = subnet - - subnets = cls.get_namespace_subnet(m_driver, pod_namespace) - - self.assertEqual({subnet_id: subnet}, subnets) - m_driver._get_namespace_subnet_id.assert_called_once_with( - pod_namespace) - m_get_subnet.assert_called_once_with(subnet_id) - - @mock.patch('kuryr_kubernetes.utils.get_subnet') - def test_get_subnets_namespace_not_ready(self, m_get_subnet): - pod = get_pod_obj() - pod_namespace = pod['metadata']['namespace'] - - cls = subnet_drv.NamespacePodSubnetDriver - m_driver = mock.MagicMock(spec=cls) - - m_driver._get_namespace_subnet_id.side_effect = ( - k_exc.ResourceNotReady(pod_namespace)) - - self.assertRaises(k_exc.ResourceNotReady, cls.get_namespace_subnet, - m_driver, pod_namespace) - - m_driver._get_namespace_subnet_id.assert_called_once_with( - pod_namespace) - m_get_subnet.assert_not_called() - - def test__get_namespace_subnet_id(self): - cls = subnet_drv.NamespacePodSubnetDriver - m_driver = mock.MagicMock(spec=cls) - - namespace = mock.sentinel.namespace - subnet_id = mock.sentinel.subnet_id - crd = { - 'status': { - 'subnetId': subnet_id - } - } - - kubernetes = self.useFixture(k_fix.MockK8sClient()).client - kubernetes.get.return_value = crd - - subnet_id_resp = cls._get_namespace_subnet_id(m_driver, namespace) - kubernetes.get.assert_called() - self.assertEqual(subnet_id, subnet_id_resp) - - def test__get_namespace_subnet_id_get_crd_exception(self): - cls = subnet_drv.NamespacePodSubnetDriver - m_driver = mock.MagicMock(spec=cls) - - namespace = mock.sentinel.namespace - - kubernetes = self.useFixture(k_fix.MockK8sClient()).client - kubernetes.get.side_effect = k_exc.K8sClientException - - self.assertRaises(k_exc.K8sClientException, - cls._get_namespace_subnet_id, m_driver, namespace) - kubernetes.get.assert_called() - - def test_delete_namespace_subnet(self): - cls = subnet_drv.NamespacePodSubnetDriver - m_driver = mock.MagicMock(spec=cls) - - net_id = mock.sentinel.net_id - subnet_id = mock.sentinel.subnet_id - os_net = self.useFixture(k_fix.MockNetworkClient()).client - os_net.ports.return_value = [] - os_net.remove_interface_from_router.return_value = {} - - cls._delete_namespace_network_resources(m_driver, subnet_id, net_id) - - os_net.remove_interface_from_router.assert_called_once() - os_net.delete_network.assert_called_once_with(net_id) - - def test_delete_namespace_subnet_openstacksdk_error(self): - cls = subnet_drv.NamespacePodSubnetDriver - m_driver = mock.MagicMock(spec=cls) - - net_id = mock.sentinel.net_id - subnet_id = mock.sentinel.subnet_id - os_net = self.useFixture(k_fix.MockNetworkClient()).client - os_net.delete_network.side_effect = os_exc.ConflictException - os_net.ports.return_value = [] - os_net.remove_interface_from_router.return_value = {} - - self.assertRaises(k_exc.ResourceNotReady, - cls._delete_namespace_network_resources, m_driver, - subnet_id, net_id) - - os_net.remove_interface_from_router.assert_called_once() - os_net.delete_network.assert_called_once_with(net_id) - os_net.ports.assert_called_with(network_id=net_id) - - def test_create_network(self): - cls = subnet_drv.NamespacePodSubnetDriver - m_driver = mock.MagicMock(spec=cls) - - ns_uid = 'e65542a5-7e82-4b59-b3c5-c04b485d19eb' - namespace = {'metadata': {'name': 'test', 'uid': ns_uid}} - project_id = mock.sentinel.project_id - os_net = self.useFixture(k_fix.MockNetworkClient()).client - os_net.networks.return_value = iter([]) - net = os_network.Network(id=mock.sentinel.net) - os_net.create_network.return_value = net - - net_id_resp = cls.create_network(m_driver, namespace, project_id) - - self.assertEqual(net_id_resp, net['id']) - os_net.create_network.assert_called_once() - os_net.networks.assert_called_once() - - def test_create_network_existing(self): - cls = subnet_drv.NamespacePodSubnetDriver - m_driver = mock.MagicMock(spec=cls) - - ns_uid = '4f7ea026-3ae4-4baa-84df-1942977fe1be' - namespace = {'metadata': {'name': 'test', 'uid': ns_uid}} - project_id = mock.sentinel.project_id - os_net = self.useFixture(k_fix.MockNetworkClient()).client - net = os_network.Network( - id=mock.sentinel.net, - description=ns_uid, - name='test', - ) - os_net.networks.return_value = iter([net]) - - net_id_resp = cls.create_network(m_driver, namespace, project_id) - - self.assertEqual(net_id_resp, net['id']) - os_net.create_network.assert_not_called() - os_net.networks.assert_called_once() - - def test_create_subnet(self): - cls = subnet_drv.NamespacePodSubnetDriver - m_driver = mock.MagicMock(spec=cls) - - ns_uid = '95e2a3c5-f723-4936-b598-cf3a59861bcf' - namespace = {'metadata': {'name': 'test', 'uid': ns_uid}} - project_id = mock.sentinel.project_id - net_id = mock.sentinel.net_id - subnet = os_subnet.Subnet( - id=mock.sentinel.subnet, - cidr=mock.sentinel.cidr, - ) - os_net = self.useFixture(k_fix.MockNetworkClient()).client - os_net.subnets.return_value = iter([]) - os_net.create_subnet.return_value = subnet - - subnet_id, subnet_cidr = cls.create_subnet(m_driver, namespace, - project_id, net_id) - - self.assertEqual(subnet_id, subnet['id']) - self.assertEqual(subnet_cidr, subnet['cidr']) - os_net.create_subnet.assert_called_once() - os_net.subnets.assert_called_once() - - def test_create_subnet_existing(self): - cls = subnet_drv.NamespacePodSubnetDriver - m_driver = mock.MagicMock(spec=cls) - - ns_uid = '7f3a59b4-dd81-490d-9904-8294a6c93326' - namespace = {'metadata': {'name': 'test', 'uid': ns_uid}} - project_id = mock.sentinel.project_id - net_id = mock.sentinel.net_id - subnet = os_subnet.Subnet( - id=mock.sentinel.subnet, - cidr=mock.sentinel.cidr, - ) - os_net = self.useFixture(k_fix.MockNetworkClient()).client - os_net.subnets.return_value = iter([subnet]) - - subnet_id, subnet_cidr = cls.create_subnet(m_driver, namespace, - project_id, net_id) - - self.assertEqual(subnet_id, subnet['id']) - self.assertEqual(subnet_cidr, subnet['cidr']) - os_net.create_subnet.assert_not_called() - os_net.subnets.assert_called_once() - - def test_add_subnet_to_router(self): - cls = subnet_drv.NamespacePodSubnetDriver - m_driver = mock.MagicMock(spec=cls) - - subnet_id = mock.sentinel.subnet_id - os_net = self.useFixture(k_fix.MockNetworkClient()).client - os_net.add_interface_to_router.return_value = {} - router_id = 'router1' - oslo_cfg.CONF.set_override('pod_router', - router_id, - group='namespace_subnet') - - router_id_resp = cls.add_subnet_to_router(m_driver, subnet_id) - self.assertEqual(router_id_resp, router_id) - os_net.add_interface_to_router.assert_called_once() - - def test_add_subnet_to_router_already_connected(self): - cls = subnet_drv.NamespacePodSubnetDriver - m_driver = mock.MagicMock(spec=cls) - - subnet_id = mock.sentinel.subnet_id - os_net = self.useFixture(k_fix.MockNetworkClient()).client - os_net.add_interface_to_router.side_effect = ( - os_exc.BadRequestException) - router_id = 'router1' - oslo_cfg.CONF.set_override('pod_router', - router_id, - group='namespace_subnet') - - router_id_resp = cls.add_subnet_to_router(m_driver, subnet_id) - self.assertEqual(router_id_resp, router_id) - os_net.add_interface_to_router.assert_called_once() - - def test_add_subnet_to_router_exception(self): - cls = subnet_drv.NamespacePodSubnetDriver - m_driver = mock.MagicMock(spec=cls) - - subnet_id = mock.sentinel.subnet_id - os_net = self.useFixture(k_fix.MockNetworkClient()).client - os_net.add_interface_to_router.side_effect = ( - os_exc.SDKException) - router_id = 'router1' - oslo_cfg.CONF.set_override('pod_router', - router_id, - group='namespace_subnet') - - self.assertRaises(os_exc.SDKException, - cls.add_subnet_to_router, m_driver, subnet_id) - os_net.add_interface_to_router.assert_called_once() diff --git a/kuryr_kubernetes/tests/unit/controller/drivers/test_nested_dpdk.py b/kuryr_kubernetes/tests/unit/controller/drivers/test_nested_dpdk.py deleted file mode 100644 index 8f0fd4eee..000000000 --- a/kuryr_kubernetes/tests/unit/controller/drivers/test_nested_dpdk.py +++ /dev/null @@ -1,227 +0,0 @@ -# Copyright (C) 2020 Intel Corporation -# 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. - -from unittest import mock - -import ddt - -from kuryr_kubernetes.controller.drivers import nested_dpdk_vif -from kuryr_kubernetes.tests import base as test_base -from kuryr_kubernetes.tests.unit import kuryr_fixtures as k_fix - -from openstack import exceptions as o_exc - - -@ddt.ddt -class TestNestedDpdkVIFDriver(test_base.TestCase): - - @mock.patch( - 'kuryr_kubernetes.os_vif_util.neutron_to_osvif_vif_dpdk') - @mock.patch('kuryr_kubernetes.controller.drivers.utils.get_network_id') - def test_request_vif(self, m_get_network_id, m_to_vif): - cls = nested_dpdk_vif.NestedDpdkPodVIFDriver - m_driver = mock.Mock(spec=cls) - os_net = self.useFixture(k_fix.MockNetworkClient()).client - compute = self.useFixture(k_fix.MockComputeClient()).client - - pod = mock.sentinel.pod - project_id = mock.sentinel.project_id - subnets = mock.sentinel.subnets - security_groups = mock.sentinel.security_groups - vm_id = mock.sentinel.parent_port_id - net_id = mock.sentinel.net_id - port_id = mock.sentinel.port_id - port = mock.sentinel.port - - parent_port = mock.MagicMock() - vif = mock.Mock() - result = mock.Mock() - - parent_port.device_id = vm_id - result.port_id = port_id - compute.create_server_interface.return_value = result - m_to_vif.return_value = vif - m_driver._get_parent_port.return_value = parent_port - m_get_network_id.return_value = net_id - os_net.get_port.return_value = port - - self.assertEqual(vif, cls.request_vif(m_driver, pod, project_id, - subnets, security_groups)) - - m_driver._get_parent_port.assert_called_once_with(pod) - m_get_network_id.assert_called_once_with(subnets) - compute.create_server_interface.assert_called_once_with( - vm_id, net_id=net_id) - os_net.get_port.assert_called_once_with(result.port_id) - m_to_vif.assert_called_once_with(port, subnets, pod) - - @mock.patch( - 'kuryr_kubernetes.os_vif_util.neutron_to_osvif_vif_dpdk') - @mock.patch('kuryr_kubernetes.controller.drivers.utils.get_network_id') - def test_request_vif_parent_not_found(self, m_get_network_id, m_to_vif): - cls = nested_dpdk_vif.NestedDpdkPodVIFDriver - m_driver = mock.Mock(spec=cls) - os_net = self.useFixture(k_fix.MockNetworkClient()).client - compute = self.useFixture(k_fix.MockComputeClient()).client - - pod = mock.sentinel.pod - project_id = mock.sentinel.project_id - subnets = mock.sentinel.subnets - security_groups = mock.sentinel.security_groups - vm_id = mock.sentinel.parent_port_id - net_id = mock.sentinel.net_id - port_id = mock.sentinel.port_id - port = mock.sentinel.port - - parent_port = mock.MagicMock() - vif = mock.Mock() - result = mock.Mock() - - parent_port.__getitem__.return_value = vm_id - result.port_id = port_id - compute.create_server_interface.return_value = result - m_to_vif.return_value = vif - m_driver._get_parent_port.side_effect = \ - o_exc.SDKException - m_get_network_id.return_value = net_id - os_net.get_port.return_value = port - - self.assertRaises(o_exc.SDKException, cls.request_vif, - m_driver, pod, project_id, subnets, security_groups) - - m_driver._get_parent_port.assert_called_once_with(pod) - m_get_network_id.assert_not_called() - compute.create_server_interface.assert_not_called() - os_net.get_port.assert_not_called() - m_to_vif.assert_not_called() - - @mock.patch( - 'kuryr_kubernetes.os_vif_util.neutron_to_osvif_vif_dpdk') - @mock.patch('kuryr_kubernetes.controller.drivers.utils.get_network_id') - def test_request_vif_attach_failed(self, m_get_network_id, m_to_vif): - cls = nested_dpdk_vif.NestedDpdkPodVIFDriver - m_driver = mock.Mock(spec=cls) - os_net = self.useFixture(k_fix.MockNetworkClient()).client - compute = self.useFixture(k_fix.MockComputeClient()).client - - pod = mock.sentinel.pod - project_id = mock.sentinel.project_id - subnets = mock.sentinel.subnets - security_groups = mock.sentinel.security_groups - vm_id = mock.sentinel.parent_port_id - net_id = mock.sentinel.net_id - port_id = mock.sentinel.port_id - port = mock.sentinel.port - - parent_port = mock.MagicMock() - vif = mock.Mock() - result = mock.Mock() - - parent_port.device_id = vm_id - result.port_id = port_id - m_to_vif.return_value = vif - m_driver._get_parent_port.return_value = parent_port - m_get_network_id.return_value = net_id - os_net.get_port.return_value = port - compute.create_server_interface.side_effect = o_exc.SDKException - - self.assertRaises(o_exc.SDKException, cls.request_vif, - m_driver, pod, project_id, subnets, security_groups) - - m_driver._get_parent_port.assert_called_once_with(pod) - m_get_network_id.assert_called_once_with(subnets) - compute.create_server_interface.assert_called_once_with( - vm_id, net_id=net_id) - os_net.get_port.assert_not_called() - m_to_vif.assert_not_called() - - def test_release_vif(self): - cls = nested_dpdk_vif.NestedDpdkPodVIFDriver - m_driver = mock.Mock(spec=cls) - compute = self.useFixture(k_fix.MockComputeClient()).client - - port_id = mock.sentinel.port_id - pod = mock.sentinel.pod - vif = mock.Mock() - vif.id = port_id - - vm_id = mock.sentinel.vm_id - vm_port = mock.MagicMock() - vm_port.device_id = vm_id - - m_driver._get_parent_port.return_value = vm_port - - cls.release_vif(m_driver, pod, vif) - - m_driver._get_parent_port.assert_called_once_with(pod) - compute.delete_server_interface.assert_called_once_with( - vif.id, server=vm_id) - - def test_release_parent_not_found(self): - cls = nested_dpdk_vif.NestedDpdkPodVIFDriver - m_driver = mock.Mock(spec=cls) - compute = self.useFixture(k_fix.MockComputeClient()).client - - pod = mock.sentinel.pod - vif = mock.Mock() - vif.id = mock.sentinel.vif_id - - vm_id = mock.sentinel.parent_port_id - parent_port = mock.MagicMock() - parent_port.__getitem__.return_value = vm_id - - m_driver._get_parent_port.side_effect = \ - o_exc.SDKException - - self.assertRaises(o_exc.SDKException, cls.release_vif, - m_driver, pod, vif) - - m_driver._get_parent_port.assert_called_once_with(pod) - compute.delete_server_interface.assert_not_called() - - def test_release_detach_failed(self): - cls = nested_dpdk_vif.NestedDpdkPodVIFDriver - m_driver = mock.Mock(spec=cls) - compute = self.useFixture(k_fix.MockComputeClient()).client - - pod = mock.sentinel.pod - vif = mock.Mock() - vif.id = mock.sentinel.vif_id - - vm_id = mock.sentinel.parent_port_id - parent_port = mock.MagicMock() - parent_port.device_id = vm_id - - compute.delete_server_interface.side_effect = o_exc.SDKException - - m_driver._get_parent_port.return_value = parent_port - - self.assertRaises(o_exc.SDKException, cls.release_vif, - m_driver, pod, vif) - - m_driver._get_parent_port.assert_called_once_with(pod) - compute.delete_server_interface.assert_called_once_with( - vif.id, server=vm_id) - - @ddt.data((False), (True)) - def test_activate_vif(self, active_value): - cls = nested_dpdk_vif.NestedDpdkPodVIFDriver - m_driver = mock.Mock(spec=cls) - vif = mock.Mock() - vif.active = active_value - - cls.activate_vif(m_driver, vif) - - self.assertEqual(vif.active, True) diff --git a/kuryr_kubernetes/tests/unit/controller/drivers/test_nested_macvlan_vif.py b/kuryr_kubernetes/tests/unit/controller/drivers/test_nested_macvlan_vif.py deleted file mode 100644 index 0c3dde09d..000000000 --- a/kuryr_kubernetes/tests/unit/controller/drivers/test_nested_macvlan_vif.py +++ /dev/null @@ -1,510 +0,0 @@ -# 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 unittest import mock - -import ddt -import threading - -from kuryr.lib import utils as lib_utils -from openstack import exceptions as o_exc - -from kuryr_kubernetes.controller.drivers import nested_macvlan_vif -from kuryr_kubernetes import exceptions as k_exc -from kuryr_kubernetes.tests import base as test_base -from kuryr_kubernetes.tests import fake -from kuryr_kubernetes.tests.unit import kuryr_fixtures as k_fix - - -@ddt.ddt -class TestNestedMacvlanPodVIFDriver(test_base.TestCase): - - @mock.patch( - 'kuryr_kubernetes.os_vif_util.neutron_to_osvif_vif_nested_macvlan') - def test_request_vif(self, m_to_vif): - cls = nested_macvlan_vif.NestedMacvlanPodVIFDriver - cls._tag_on_creation = True - m_driver = mock.Mock(spec=cls) - os_net = self.useFixture(k_fix.MockNetworkClient()).client - - pod = mock.sentinel.pod - project_id = mock.sentinel.project_id - subnets = mock.sentinel.subnets - security_groups = mock.sentinel.security_groups - container_mac = mock.sentinel.mac_address - container_ip = mock.sentinel.ip_address - container_port = fake.get_port_obj(mac_address=container_mac, - ip_address=container_ip) - - vif = mock.Mock() - port_request = {'foo': mock.sentinel.port_request} - vm_port = fake.get_port_obj() - - m_to_vif.return_value = vif - m_driver._get_port_request.return_value = port_request - m_driver._get_parent_port.return_value = vm_port - m_driver._try_update_port.return_value = 0 - m_driver.lock = mock.MagicMock(spec=threading.Lock()) - os_net.create_port.return_value = container_port - - self.assertEqual(vif, cls.request_vif(m_driver, pod, project_id, - subnets, security_groups)) - - m_driver._get_port_request.assert_called_once_with( - pod, project_id, subnets, security_groups) - os_net.create_port.assert_called_once_with(**port_request) - m_driver._get_parent_port.assert_called_once_with(pod) - m_driver._try_update_port.assert_called_once() - m_to_vif.assert_called_once_with(container_port, subnets) - - @mock.patch( - 'kuryr_kubernetes.os_vif_util.neutron_to_osvif_vif_nested_macvlan') - def test_request_vif_port_create_failed(self, m_to_vif): - cls = nested_macvlan_vif.NestedMacvlanPodVIFDriver - cls._tag_on_creation = True - m_driver = mock.Mock(spec=cls) - os_net = self.useFixture(k_fix.MockNetworkClient()).client - - pod = mock.sentinel.pod - project_id = mock.sentinel.project_id - subnets = mock.sentinel.subnets - security_groups = mock.sentinel.security_groups - - port_request = {'foo': mock.sentinel.port_request} - m_driver._get_port_request.return_value = port_request - os_net.create_port.side_effect = o_exc.SDKException - - self.assertRaises(o_exc.SDKException, cls.request_vif, - m_driver, pod, project_id, subnets, security_groups) - m_driver._get_port_request.assert_called_once_with( - pod, project_id, subnets, security_groups) - os_net.create_port.assert_called_once_with(**port_request) - m_driver._try_update_port.assert_not_called() - m_to_vif.assert_not_called() - - @mock.patch( - 'kuryr_kubernetes.os_vif_util.neutron_to_osvif_vif_nested_macvlan') - def test_request_vif_parent_not_found(self, m_to_vif): - cls = nested_macvlan_vif.NestedMacvlanPodVIFDriver - cls._tag_on_creation = True - m_driver = mock.Mock(spec=cls) - os_net = self.useFixture(k_fix.MockNetworkClient()).client - - pod = mock.sentinel.pod - project_id = mock.sentinel.project_id - subnets = mock.sentinel.subnets - security_groups = mock.sentinel.security_groups - container_mac = mock.sentinel.mac_address - container_ip = mock.sentinel.ip_address - container_port = fake.get_port_obj(mac_address=container_mac, - ip_address=container_ip) - - port_request = mock.sentinel.port_request - m_driver._get_port_request.return_value = port_request - m_driver.lock = mock.MagicMock(spec=threading.Lock()) - os_net.create_port.return_value = container_port - m_driver._get_parent_port.side_effect = o_exc.SDKException - - self.assertRaises(o_exc.SDKException, cls.request_vif, - m_driver, pod, project_id, subnets, security_groups) - m_driver._get_port_request.assert_called_once_with( - pod, project_id, subnets, security_groups) - os_net.create_port.assert_not_called() - m_driver._get_parent_port.assert_called_once_with(pod) - m_driver._try_update_port.assert_not_called() - m_to_vif.assert_not_called() - - def test_release_vif(self): - cls = nested_macvlan_vif.NestedMacvlanPodVIFDriver - m_driver = mock.Mock(spec=cls) - os_net = self.useFixture(k_fix.MockNetworkClient()).client - - port_id = lib_utils.get_hash() - pod = mock.sentinel.pod - vif = mock.Mock() - vif.id = port_id - - container_mac = mock.sentinel.mac_address - container_ip = mock.sentinel.ip_address - container_port = fake.get_port_obj( - port_id=port_id, ip_address=container_ip, - mac_address=container_mac) - os_net.get_port.return_value = container_port - - vm_port = fake.get_port_obj() - m_driver._get_parent_port.return_value = vm_port - m_driver._try_update_port.return_value = 0 - m_driver.lock = mock.MagicMock(spec=threading.Lock()) - - cls.release_vif(m_driver, pod, vif) - - os_net.get_port.assert_called_once_with(port_id) - m_driver._get_parent_port.assert_called_once_with(pod) - m_driver._try_update_port.assert_called_once() - os_net.delete_port.assert_called_once_with(vif.id, - ignore_missing=False) - - def test_release_vif_not_found(self): - cls = nested_macvlan_vif.NestedMacvlanPodVIFDriver - m_driver = mock.Mock(spec=cls) - os_net = self.useFixture(k_fix.MockNetworkClient()).client - - pod = mock.sentinel.pod - vif = mock.Mock() - vif.id = lib_utils.get_hash() - - os_net.get_port.side_effect = o_exc.NotFoundException - - self.assertRaises(o_exc.NotFoundException, cls.release_vif, - m_driver, pod, vif) - m_driver._remove_from_allowed_address_pairs.assert_not_called() - os_net.delete_port.assert_not_called() - - def test_release_vif_parent_not_found(self): - cls = nested_macvlan_vif.NestedMacvlanPodVIFDriver - m_driver = mock.Mock(spec=cls) - os_net = self.useFixture(k_fix.MockNetworkClient()).client - - port_id = lib_utils.get_hash() - pod = mock.sentinel.pod - vif = mock.Mock() - vif.id = port_id - - container_mac = mock.sentinel.mac_address - container_ip = mock.sentinel.ip_address - container_port = fake.get_port_obj( - port_id=port_id, ip_address=container_ip, - mac_address=container_mac) - os_net.get_port.return_value = container_port - - m_driver.lock = mock.MagicMock(spec=threading.Lock()) - m_driver._get_parent_port.side_effect = o_exc.SDKException - - self.assertRaises(o_exc.SDKException, cls.release_vif, - m_driver, pod, vif) - os_net.get_port.assert_called_with(port_id) - self.assertEqual(os_net.get_port.call_count, 1) - m_driver._get_parent_port.assert_called_with(pod) - self.assertEqual(m_driver._get_parent_port.call_count, 1) - m_driver._remove_from_allowed_address_pairs.assert_not_called() - os_net.delete_port.assert_not_called() - - def test_release_vif_delete_failed(self): - cls = nested_macvlan_vif.NestedMacvlanPodVIFDriver - m_driver = mock.Mock(spec=cls) - os_net = self.useFixture(k_fix.MockNetworkClient()).client - - port_id = lib_utils.get_hash() - pod = mock.sentinel.pod - vif = mock.Mock() - vif.id = port_id - - container_mac = mock.sentinel.mac_address - container_ip = mock.sentinel.ip_addresses - container_port = fake.get_port_obj( - port_id=port_id, ip_address=container_ip, - mac_address=container_mac) - os_net.get_port.return_value = container_port - os_net.delete_port.side_effect = o_exc.NotFoundException - - vm_port = fake.get_port_obj() - m_driver._get_parent_port.return_value = vm_port - m_driver._try_update_port.return_value = 0 - m_driver.lock = mock.MagicMock(spec=threading.Lock()) - - cls.release_vif(m_driver, pod, vif) - - os_net.get_port.assert_called_once_with(port_id) - m_driver._get_parent_port.assert_called_once_with(pod) - m_driver._try_update_port.assert_called_once() - os_net.delete_port.assert_called_once_with(vif.id, - ignore_missing=False) - - @ddt.data((False), (True)) - def test_activate_vif(self, active_value): - cls = nested_macvlan_vif.NestedMacvlanPodVIFDriver - m_driver = mock.Mock(spec=cls) - vif = mock.Mock() - vif.active = active_value - - cls.activate_vif(m_driver, vif) - - self.assertEqual(vif.active, True) - - @ddt.data((None), ('fa:16:3e:71:cb:80')) - def test_add_to_allowed_address_pairs(self, m_mac): - cls = nested_macvlan_vif.NestedMacvlanPodVIFDriver - m_driver = mock.Mock(spec=cls) - self.useFixture(k_fix.MockNetworkClient()).client - - port_id = lib_utils.get_hash() - vm_port = fake.get_port_obj(port_id) - - mac_addr = 'fa:16:3e:1b:30:00' if m_mac else vm_port['mac_address'] - address_pairs = [ - {'ip_address': '10.0.0.30', - 'mac_address': mac_addr}, - {'ip_address': 'fe80::f816:3eff:fe1c:36a9', - 'mac_address': mac_addr}, - ] - vm_port['allowed_address_pairs'].extend(address_pairs) - - ip_addr = '10.0.0.29' - address_pairs.append( - {'ip_address': ip_addr, - 'mac_address': m_mac if m_mac else vm_port['mac_address']} - ) - - cls._add_to_allowed_address_pairs(m_driver, vm_port, - frozenset([ip_addr]), m_mac) - - m_driver._update_port_address_pairs.assert_called_once_with( - port_id, address_pairs, revision_number=9) - - def test_add_to_allowed_address_pairs_no_ip_addresses(self): - cls = nested_macvlan_vif.NestedMacvlanPodVIFDriver - m_driver = mock.Mock(spec=cls) - self.useFixture(k_fix.MockNetworkClient()).client - - port_id = lib_utils.get_hash() - vm_port = fake.get_port_obj(port_id) - - self.assertRaises(k_exc.IntegrityError, - cls._add_to_allowed_address_pairs, m_driver, - vm_port, frozenset()) - - def test_add_to_allowed_address_pairs_same_ip(self): - cls = nested_macvlan_vif.NestedMacvlanPodVIFDriver - m_driver = mock.Mock(spec=cls) - self.useFixture(k_fix.MockNetworkClient()).client - - port_id = lib_utils.get_hash() - vm_port = fake.get_port_obj(port_id) - address_pairs = [ - {'ip_address': '10.0.0.30', - 'mac_address': 'fa:16:3e:1b:30:00'}, - {'ip_address': 'fe80::f816:3eff:fe1c:36a9', - 'mac_address': 'fa:16:3e:1b:30:00'}, - ] - vm_port['allowed_address_pairs'].extend(address_pairs) - - mac_addr = 'fa:16:3e:71:cb:80' - ip_addr = '10.0.0.30' - address_pairs.append({'ip_address': ip_addr, 'mac_address': mac_addr}) - - cls._add_to_allowed_address_pairs(m_driver, vm_port, - frozenset([ip_addr]), mac_addr) - - m_driver._update_port_address_pairs.assert_called_once_with( - port_id, address_pairs, revision_number=9) - - def test_add_to_allowed_address_pairs_already_present(self): - cls = nested_macvlan_vif.NestedMacvlanPodVIFDriver - m_driver = mock.Mock(spec=cls) - self.useFixture(k_fix.MockNetworkClient()).client - - port_id = lib_utils.get_hash() - vm_port = fake.get_port_obj(port_id) - address_pairs = [ - {'ip_address': '10.0.0.30', - 'mac_address': 'fa:16:3e:1b:30:00'}, - {'ip_address': 'fe80::f816:3eff:fe1c:36a9', - 'mac_address': 'fa:16:3e:1b:30:00'}, - ] - vm_port['allowed_address_pairs'].extend(address_pairs) - - mac_addr = 'fa:16:3e:1b:30:00' - ip_addr = '10.0.0.30' - - self.assertRaises(k_exc.AllowedAddressAlreadyPresent, - cls._add_to_allowed_address_pairs, m_driver, - vm_port, frozenset([ip_addr]), mac_addr) - - @ddt.data((None), ('fa:16:3e:71:cb:80')) - def test_remove_from_allowed_address_pairs(self, m_mac): - cls = nested_macvlan_vif.NestedMacvlanPodVIFDriver - m_driver = mock.Mock(spec=cls) - self.useFixture(k_fix.MockNetworkClient()).client - - port_id = lib_utils.get_hash() - vm_port = fake.get_port_obj(port_id) - - mac_addr = 'fa:16:3e:1b:30:00' if m_mac else vm_port['mac_address'] - address_pairs = [ - {'ip_address': '10.0.0.30', - 'mac_address': mac_addr}, - {'ip_address': 'fe80::f816:3eff:fe1c:36a9', - 'mac_address': mac_addr}, - ] - vm_port['allowed_address_pairs'].extend(address_pairs) - - ip_addr = '10.0.0.29' - vm_port['allowed_address_pairs'].append( - {'ip_address': ip_addr, - 'mac_address': m_mac if m_mac else vm_port['mac_address']} - ) - - cls._remove_from_allowed_address_pairs( - m_driver, vm_port, frozenset([ip_addr]), m_mac) - - m_driver._update_port_address_pairs.assert_called_once_with( - port_id, address_pairs, revision_number=9) - - def test_remove_from_allowed_address_pairs_no_ip_addresses(self): - cls = nested_macvlan_vif.NestedMacvlanPodVIFDriver - m_driver = mock.Mock(spec=cls) - self.useFixture(k_fix.MockNetworkClient()).client - - port_id = lib_utils.get_hash() - vm_port = fake.get_port_obj(port_id) - - self.assertRaises(k_exc.IntegrityError, - cls._remove_from_allowed_address_pairs, m_driver, - vm_port, frozenset()) - - @ddt.data((None), ('fa:16:3e:71:cb:80')) - def test_remove_from_allowed_address_pairs_missing(self, m_mac): - cls = nested_macvlan_vif.NestedMacvlanPodVIFDriver - m_driver = mock.Mock(spec=cls) - self.useFixture(k_fix.MockNetworkClient()).client - - port_id = lib_utils.get_hash() - vm_port = fake.get_port_obj(port_id) - - mac_addr = 'fa:16:3e:1b:30:00' if m_mac else vm_port['mac_address'] - address_pairs = [ - {'ip_address': '10.0.0.30', - 'mac_address': mac_addr}, - {'ip_address': 'fe80::f816:3eff:fe1c:36a9', - 'mac_address': mac_addr}, - ] - mac_addr = m_mac if m_mac else vm_port['mac_address'] - vm_port['allowed_address_pairs'].extend(address_pairs) - vm_port['allowed_address_pairs'].append({'ip_address': '10.0.0.28', - 'mac_address': mac_addr}) - ip_addr = ['10.0.0.29', '10.0.0.28'] - - cls._remove_from_allowed_address_pairs( - m_driver, vm_port, frozenset(ip_addr), m_mac) - - m_driver._update_port_address_pairs.assert_called_once_with( - port_id, address_pairs, revision_number=9) - - @ddt.data((None), ('fa:16:3e:71:cb:80')) - def test_remove_from_allowed_address_pairs_no_update(self, m_mac): - cls = nested_macvlan_vif.NestedMacvlanPodVIFDriver - m_driver = mock.Mock(spec=cls) - self.useFixture(k_fix.MockNetworkClient()).client - - port_id = lib_utils.get_hash() - vm_port = fake.get_port_obj(port_id) - - mac_addr = 'fa:16:3e:1b:30:00' if m_mac else vm_port['mac_address'] - address_pairs = [ - {'ip_address': '10.0.0.30', - 'mac_address': mac_addr}, - {'ip_address': 'fe80::f816:3eff:fe1c:36a9', - 'mac_address': mac_addr}, - ] - vm_port['allowed_address_pairs'].extend(address_pairs) - - ip_addr = ['10.0.0.29'] - - cls._remove_from_allowed_address_pairs( - m_driver, vm_port, frozenset(ip_addr), m_mac) - - m_driver._update_port_address_pairs.assert_not_called() - - def test_update_port_address_pairs(self): - cls = nested_macvlan_vif.NestedMacvlanPodVIFDriver - m_driver = mock.Mock(spec=cls) - os_net = self.useFixture(k_fix.MockNetworkClient()).client - - port_id = lib_utils.get_hash() - pairs = mock.sentinel.allowed_address_pairs - - cls._update_port_address_pairs(m_driver, port_id, pairs, - revision_number=9) - - os_net.update_port.assert_called_with( - port_id, allowed_address_pairs=pairs, if_revision=9) - - def test_update_port_address_pairs_failure(self): - cls = nested_macvlan_vif.NestedMacvlanPodVIFDriver - m_driver = mock.Mock(spec=cls) - os_net = self.useFixture(k_fix.MockNetworkClient()).client - - port_id = lib_utils.get_hash() - pairs = mock.sentinel.allowed_address_pairs - os_net.update_port.side_effect = o_exc.SDKException - - self.assertRaises(o_exc.SDKException, - cls._update_port_address_pairs, m_driver, - port_id, pairs, revision_number=9) - - os_net.update_port.assert_called_with( - port_id, allowed_address_pairs=pairs, if_revision=9) - - @mock.patch('kuryr_kubernetes.controller.drivers.nested_macvlan_vif.' - 'NestedMacvlanPodVIFDriver._add_to_allowed_address_pairs') - def test_try_update_port(self, aaapf_mock): - cls = nested_macvlan_vif.NestedMacvlanPodVIFDriver - m_driver = mock.Mock(spec=cls) - m_driver.lock = mock.MagicMock(spec=threading.Lock()) - self.useFixture(k_fix.MockNetworkClient()).client - - port_id = lib_utils.get_hash() - vm_port = fake.get_port_obj(port_id) - - mac_addr = 'fa:16:3e:1b:30:00' - address_pairs = [ - {'ip_address': '10.0.0.30', - 'mac_address': mac_addr}, - {'ip_address': 'fe80::f816:3eff:fe1c:36a9', - 'mac_address': mac_addr}, - ] - vm_port['allowed_address_pairs'].extend(address_pairs) - - ip_addr = ['10.0.0.29'] - attempts = cls._try_update_port(m_driver, 3, - cls._add_to_allowed_address_pairs, - vm_port, frozenset(ip_addr), mac_addr) - self.assertEqual(attempts, 0) - aaapf_mock.assert_called_once() - - @mock.patch('kuryr_kubernetes.controller.drivers.nested_macvlan_vif.' - 'NestedMacvlanPodVIFDriver._add_to_allowed_address_pairs') - def test_try_update_port_failure(self, aaapf_mock): - cls = nested_macvlan_vif.NestedMacvlanPodVIFDriver - m_driver = mock.Mock(spec=cls) - m_driver.lock = mock.MagicMock(spec=threading.Lock()) - self.useFixture(k_fix.MockNetworkClient()).client - - port_id = lib_utils.get_hash() - vm_port = fake.get_port_obj(port_id) - - mac_addr = 'fa:16:3e:1b:30:00' - address_pairs = [ - {'ip_address': '10.0.0.30', - 'mac_address': mac_addr}, - {'ip_address': 'fe80::f816:3eff:fe1c:36a9', - 'mac_address': mac_addr}, - ] - vm_port['allowed_address_pairs'].extend(address_pairs) - - ip_addr = ['10.0.0.29'] - - aaapf_mock.side_effect = o_exc.SDKException - self.assertRaises(o_exc.SDKException, - cls._try_update_port, m_driver, 1, - cls._add_to_allowed_address_pairs, - vm_port, frozenset(ip_addr), mac_addr) diff --git a/kuryr_kubernetes/tests/unit/controller/drivers/test_nested_vif.py b/kuryr_kubernetes/tests/unit/controller/drivers/test_nested_vif.py deleted file mode 100644 index 9ed004d27..000000000 --- a/kuryr_kubernetes/tests/unit/controller/drivers/test_nested_vif.py +++ /dev/null @@ -1,129 +0,0 @@ -# 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 unittest import mock - -from kuryr.lib import exceptions as kl_exc -from oslo_config import cfg as oslo_cfg - -from kuryr_kubernetes.controller.drivers import nested_vif -from kuryr_kubernetes.controller.drivers import node_subnets -from kuryr_kubernetes.tests import base as test_base -from kuryr_kubernetes.tests.unit import kuryr_fixtures as k_fix - - -class TestNestedPodVIFDriver(test_base.TestCase): - - def test_get_parent_port(self): - cls = nested_vif.NestedPodVIFDriver - m_driver = mock.Mock(spec=cls) - self.useFixture(k_fix.MockNetworkClient()).client - - node_fixed_ip = mock.sentinel.node_fixed_ip - pod_status = mock.MagicMock() - pod_status.__getitem__.return_value = node_fixed_ip - - pod = mock.MagicMock() - pod.__getitem__.return_value = pod_status - parent_port = mock.sentinel.parent_port - - m_driver._get_parent_port_by_host_ip.return_value = parent_port - - cls._get_parent_port(m_driver, pod) - m_driver._get_parent_port_by_host_ip.assert_called_once() - - def test_get_parent_port_by_host_ip(self): - cls = nested_vif.NestedPodVIFDriver - m_driver = mock.Mock( - spec=cls, nodes_subnets_driver=node_subnets.ConfigNodesSubnets()) - os_net = self.useFixture(k_fix.MockNetworkClient()).client - - node_subnet_id1 = 'node_subnet_id1' - node_subnet_id2 = 'node_subnet_id2' - oslo_cfg.CONF.set_override('worker_nodes_subnets', - [node_subnet_id2], - group='pod_vif_nested') - - node_fixed_ip = mock.sentinel.node_fixed_ip - - ports = [ - mock.Mock(fixed_ips=[{'subnet_id': node_subnet_id1}]), - mock.Mock(fixed_ips=[{'subnet_id': node_subnet_id2}]), - ] - os_net.ports.return_value = iter(ports) - - self.assertEqual(ports[1], cls._get_parent_port_by_host_ip( - m_driver, node_fixed_ip)) - fixed_ips = ['ip_address=%s' % str(node_fixed_ip)] - os_net.ports.assert_called_once_with(fixed_ips=fixed_ips) - - def test_get_parent_port_by_host_ip_multiple(self): - cls = nested_vif.NestedPodVIFDriver - m_driver = mock.Mock( - spec=cls, nodes_subnets_driver=node_subnets.ConfigNodesSubnets()) - os_net = self.useFixture(k_fix.MockNetworkClient()).client - - node_subnet_id1 = 'node_subnet_id1' - node_subnet_id2 = 'node_subnet_id2' - node_subnet_id3 = 'node_subnet_id3' - oslo_cfg.CONF.set_override('worker_nodes_subnets', - [node_subnet_id3, node_subnet_id2], - group='pod_vif_nested') - - node_fixed_ip = mock.sentinel.node_fixed_ip - - ports = [ - mock.Mock(fixed_ips=[{'subnet_id': node_subnet_id1}]), - mock.Mock(fixed_ips=[{'subnet_id': node_subnet_id2}]), - ] - os_net.ports.return_value = (p for p in ports) - - self.assertEqual(ports[1], cls._get_parent_port_by_host_ip( - m_driver, node_fixed_ip)) - fixed_ips = ['ip_address=%s' % str(node_fixed_ip)] - os_net.ports.assert_called_with(fixed_ips=fixed_ips) - - def test_get_parent_port_by_host_ip_subnet_id_not_configured(self): - cls = nested_vif.NestedPodVIFDriver - m_driver = mock.Mock( - spec=cls, nodes_subnets_driver=node_subnets.ConfigNodesSubnets()) - self.useFixture(k_fix.MockNetworkClient()).client - oslo_cfg.CONF.set_override('worker_nodes_subnets', - '', - group='pod_vif_nested') - node_fixed_ip = mock.sentinel.node_fixed_ip - self.assertRaises(oslo_cfg.RequiredOptError, - cls._get_parent_port_by_host_ip, - m_driver, node_fixed_ip) - - def test_get_parent_port_by_host_ip_trunk_not_found(self): - cls = nested_vif.NestedPodVIFDriver - m_driver = mock.Mock( - spec=cls, nodes_subnets_driver=node_subnets.ConfigNodesSubnets()) - os_net = self.useFixture(k_fix.MockNetworkClient()).client - - node_subnet_id = 'node_subnet_id' - - oslo_cfg.CONF.set_override('worker_nodes_subnets', - [node_subnet_id], - group='pod_vif_nested') - - node_fixed_ip = mock.sentinel.node_fixed_ip - - ports = (p for p in []) - os_net.ports.return_value = ports - - self.assertRaises(kl_exc.NoResourceException, - cls._get_parent_port_by_host_ip, m_driver, - node_fixed_ip) - fixed_ips = ['ip_address=%s' % str(node_fixed_ip)] - os_net.ports.assert_called_once_with(fixed_ips=fixed_ips) diff --git a/kuryr_kubernetes/tests/unit/controller/drivers/test_nested_vlan_vif.py b/kuryr_kubernetes/tests/unit/controller/drivers/test_nested_vlan_vif.py deleted file mode 100644 index b01c4c363..000000000 --- a/kuryr_kubernetes/tests/unit/controller/drivers/test_nested_vlan_vif.py +++ /dev/null @@ -1,651 +0,0 @@ -# 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 eventlet -from unittest import mock - -from kuryr.lib import constants as kl_const -from kuryr.lib import exceptions as kl_exc -from openstack import exceptions as os_exc -from openstack.network.v2 import port as os_port -from openstack.network.v2 import trunk as os_trunk -from oslo_config import cfg as oslo_cfg - -from kuryr_kubernetes import constants -from kuryr_kubernetes.controller.drivers import nested_vlan_vif -from kuryr_kubernetes import exceptions as k_exc -from kuryr_kubernetes.tests import base as test_base -from kuryr_kubernetes.tests.unit import kuryr_fixtures as k_fix - - -class TestNestedVlanPodVIFDriver(test_base.TestCase): - - @mock.patch( - 'kuryr_kubernetes.os_vif_util.neutron_to_osvif_vif_nested_vlan') - def test_request_vif(self, m_to_vif): - cls = nested_vlan_vif.NestedVlanPodVIFDriver - cls._tag_on_creation = True - m_driver = mock.Mock(spec=cls) - os_net = self.useFixture(k_fix.MockNetworkClient()).client - - pod = mock.sentinel.pod - project_id = mock.sentinel.project_id - subnets = mock.sentinel.subnets - security_groups = mock.sentinel.security_groups - - parent_port = mock.sentinel.parent_port - trunk_id = mock.sentinel.trunk_id - port_id = mock.sentinel.port_id - port = os_port.Port(id=port_id) - port_request = {'project_id': project_id, - 'name': mock.sentinel.name, - 'network_id': mock.sentinel.network_id, - 'fixed_ips': mock.sentinel.fixed_ips, - 'admin_state_up': True} - vlan_id = mock.sentinel.vlan_id - - vif = mock.Mock() - - m_to_vif.return_value = vif - m_driver._get_parent_port.return_value = parent_port - m_driver._get_trunk_id.return_value = trunk_id - m_driver._get_port_request.return_value = port_request - m_driver._add_subport.return_value = vlan_id - os_net.ports.return_value = (p for p in [parent_port]) - os_net.create_port.return_value = port - - self.assertEqual(vif, cls.request_vif(m_driver, pod, project_id, - subnets, security_groups)) - - m_driver._get_parent_port.assert_called_once_with(pod) - m_driver._get_trunk_id.assert_called_once_with(parent_port) - m_driver._get_port_request.assert_called_once_with( - pod, project_id, subnets, security_groups) - os_net.create_port.assert_called_once_with(**port_request) - m_driver._add_subport.assert_called_once_with(trunk_id, port_id) - m_to_vif.assert_called_once_with(port, subnets, vlan_id) - - @mock.patch( - 'kuryr_kubernetes.os_vif_util.neutron_to_osvif_vif_nested_vlan') - def test_request_vifs(self, m_to_vif): - cls = nested_vlan_vif.NestedVlanPodVIFDriver - cls._tag_on_creation = True - m_driver = mock.Mock(spec=cls) - os_net = self.useFixture(k_fix.MockNetworkClient()).client - - pod = mock.sentinel.pod - project_id = mock.sentinel.project_id - subnets = mock.sentinel.subnets - security_groups = mock.sentinel.security_groups - num_ports = 2 - - parent_port = mock.sentinel.parent_port - trunk_id = mock.sentinel.trunk_id - port_request = mock.sentinel.port_request - subports_info = [{'segmentation_id': 1, - 'port_id': '', - 'segmentation_type': 'vlan'}, - {'segmentation_id': 2, - 'port_id': '', - 'segmentation_type': 'vlan'}] - port = os_port.Port(id=mock.sentinel.id) - vif = mock.sentinel.vif - - bulk_rq = [port_request for _ in range(len(subports_info))] - - m_driver._get_parent_port.return_value = parent_port - m_driver._get_trunk_id.return_value = trunk_id - m_driver._create_subports_info.return_value = (port_request, - subports_info) - os_net.create_ports.return_value = (p for p in [port, port]) - m_to_vif.return_value = vif - semaphore = mock.MagicMock(spec=eventlet.semaphore.Semaphore(20)) - - self.assertEqual([vif, vif], cls.request_vifs( - m_driver, pod, project_id, subnets, security_groups, num_ports, - semaphore)) - - m_driver._get_parent_port.assert_called_once_with(pod) - m_driver._get_trunk_id.assert_called_once_with(parent_port) - m_driver._create_subports_info.assert_called_once_with( - pod, project_id, subnets, security_groups, trunk_id, num_ports, - unbound=True) - os_net.create_ports.assert_called_once_with(bulk_rq) - os_net.add_trunk_subports.assert_called_once_with(trunk_id, - subports_info) - os_net.delete_port.assert_not_called() - - calls = [mock.call(port, subnets, info['segmentation_id']) - for info in subports_info] - m_to_vif.assert_has_calls(calls) - - def test_request_vifs_no_vlans(self): - cls = nested_vlan_vif.NestedVlanPodVIFDriver - cls._tag_on_creation = False - m_driver = mock.Mock(spec=cls) - self.useFixture(k_fix.MockNetworkClient()).client - - pod = mock.sentinel.pod - project_id = mock.sentinel.project_id - subnets = mock.sentinel.subnets - security_groups = mock.sentinel.security_groups - num_ports = 2 - - parent_port = mock.sentinel.parent_port - trunk_id = mock.sentinel.trunk_id - port_request = mock.sentinel.port_request - subports_info = [] - - m_driver._get_parent_port.return_value = parent_port - m_driver._get_trunk_id.return_value = trunk_id - m_driver._create_subports_info.return_value = (port_request, - subports_info) - semaphore = mock.MagicMock(spec=eventlet.semaphore.Semaphore(20)) - - self.assertEqual([], cls.request_vifs(m_driver, pod, project_id, - subnets, security_groups, - num_ports, semaphore)) - - m_driver._get_parent_port.assert_called_once_with(pod) - m_driver._get_trunk_id.assert_called_once_with(parent_port) - m_driver._create_subports_info.assert_called_once_with( - pod, project_id, subnets, security_groups, - trunk_id, num_ports, unbound=True) - - def test_request_vifs_bulk_creation_exception(self): - cls = nested_vlan_vif.NestedVlanPodVIFDriver - cls._tag_on_creation = True - m_driver = mock.Mock(spec=cls) - os_net = self.useFixture(k_fix.MockNetworkClient()).client - - pod = mock.sentinel.pod - project_id = mock.sentinel.project_id - subnets = mock.sentinel.subnets - security_groups = mock.sentinel.security_groups - num_ports = 2 - - parent_port = mock.sentinel.parent_port - trunk_id = mock.sentinel.trunk_id - port_request = mock.sentinel.port_request - subports_info = [{'segmentation_id': 1, - 'port_id': '', - 'segmentation_type': 'vlan'}, - {'segmentation_id': 2, - 'port_id': '', - 'segmentation_type': 'vlan'}] - - bulk_rq = [port_request for _ in range(len(subports_info))] - - m_driver._get_parent_port.return_value = parent_port - m_driver._get_trunk_id.return_value = trunk_id - m_driver._create_subports_info.return_value = (port_request, - subports_info) - os_net.create_ports.side_effect = os_exc.SDKException - semaphore = mock.MagicMock(spec=eventlet.semaphore.Semaphore(20)) - - self.assertRaises( - os_exc.SDKException, cls.request_vifs, - m_driver, pod, project_id, subnets, security_groups, num_ports, - semaphore) - - m_driver._get_parent_port.assert_called_once_with(pod) - m_driver._get_trunk_id.assert_called_once_with(parent_port) - m_driver._create_subports_info.assert_called_once_with( - pod, project_id, subnets, security_groups, - trunk_id, num_ports, unbound=True) - os_net.create_ports.assert_called_once_with(bulk_rq) - - @mock.patch('kuryr_kubernetes.controller.drivers.utils.delete_ports') - def test_request_vifs_trunk_subports_conflict(self, m_del_ports): - cls = nested_vlan_vif.NestedVlanPodVIFDriver - cls._tag_on_creation = True - m_driver = mock.Mock(spec=cls) - os_net = self.useFixture(k_fix.MockNetworkClient()).client - - pod = mock.sentinel.pod - project_id = mock.sentinel.project_id - subnets = mock.sentinel.subnets - security_groups = mock.sentinel.security_groups - num_ports = 2 - - parent_port = mock.sentinel.parent_port - trunk_id = mock.sentinel.trunk_id - port_request = mock.sentinel.port_request - subports_info = [{'segmentation_id': 1, - 'port_id': '', - 'segmentation_type': 'vlan'}, - {'segmentation_id': 2, - 'port_id': '', - 'segmentation_type': 'vlan'}] - port = os_port.Port(id=mock.sentinel.id) - - bulk_rq = [port_request for _ in range(len(subports_info))] - - m_driver._get_parent_port.return_value = parent_port - m_driver._get_trunk_id.return_value = trunk_id - m_driver._create_subports_info.return_value = (port_request, - subports_info) - os_net.create_ports.return_value = (p for p in [port, port]) - os_net.add_trunk_subports.side_effect = os_exc.ConflictException - semaphore = mock.MagicMock(spec=eventlet.semaphore.Semaphore(20)) - - self.assertEqual([], cls.request_vifs(m_driver, pod, project_id, - subnets, security_groups, num_ports, semaphore)) - - m_driver._get_parent_port.assert_called_once_with(pod) - m_driver._get_trunk_id.assert_called_once_with(parent_port) - m_driver._create_subports_info.assert_called_once_with( - pod, project_id, subnets, security_groups, - trunk_id, num_ports, unbound=True) - os_net.create_ports.assert_called_once_with(bulk_rq) - os_net.add_trunk_subports.assert_called_once_with(trunk_id, - subports_info) - m_del_ports.assert_called_once_with([port, port]) - - @mock.patch('kuryr_kubernetes.controller.drivers.utils.delete_ports') - def test_request_vifs_trunk_subports_exception(self, m_del_ports): - cls = nested_vlan_vif.NestedVlanPodVIFDriver - cls._tag_on_creation = False - m_driver = mock.Mock(spec=cls) - os_net = self.useFixture(k_fix.MockNetworkClient()).client - - pod = mock.sentinel.pod - project_id = mock.sentinel.project_id - subnets = mock.sentinel.subnets - security_groups = mock.sentinel.security_groups - num_ports = 2 - - parent_port = mock.sentinel.parent_port - trunk_id = mock.sentinel.trunk_id - port_request = mock.sentinel.port_request - subports_info = [{'segmentation_id': 1, - 'port_id': '', - 'segmentation_type': 'vlan'}, - {'segmentation_id': 2, - 'port_id': '', - 'segmentation_type': 'vlan'}] - port = os_port.Port(id=mock.sentinel.id) - - bulk_rq = [port_request for _ in range(len(subports_info))] - - m_driver._get_parent_port.return_value = parent_port - m_driver._get_trunk_id.return_value = trunk_id - m_driver._create_subports_info.return_value = (port_request, - subports_info) - os_net.create_ports.return_value = (p for p in [port, port]) - os_net.add_trunk_subports.side_effect = os_exc.SDKException - semaphore = mock.MagicMock(spec=eventlet.semaphore.Semaphore(20)) - - self.assertEqual([], cls.request_vifs(m_driver, pod, project_id, - subnets, security_groups, num_ports, semaphore)) - - m_driver._get_parent_port.assert_called_once_with(pod) - m_driver._get_trunk_id.assert_called_once_with(parent_port) - m_driver._create_subports_info.assert_called_once_with( - pod, project_id, subnets, security_groups, - trunk_id, num_ports, unbound=True) - os_net.create_ports.assert_called_once_with(bulk_rq) - os_net.add_trunk_subports.assert_called_once_with(trunk_id, - subports_info) - m_del_ports.assert_called_once_with([port, port]) - - def test_release_vif(self): - cls = nested_vlan_vif.NestedVlanPodVIFDriver - m_driver = mock.Mock(spec=cls) - os_net = self.useFixture(k_fix.MockNetworkClient()).client - parent_port = mock.sentinel.parent_port - trunk_id = mock.sentinel.trunk_id - - m_driver._get_parent_port.return_value = parent_port - m_driver._get_trunk_id.return_value = trunk_id - pod = mock.sentinel.pod - vif = mock.Mock() - - cls.release_vif(m_driver, pod, vif) - - m_driver._get_parent_port.assert_called_once_with(pod) - m_driver._get_trunk_id.assert_called_once_with(parent_port) - m_driver._remove_subport.assert_called_once_with(trunk_id, vif.id) - os_net.delete_port.assert_called_once_with(vif.id) - - def test_release_vif_not_found(self): - cls = nested_vlan_vif.NestedVlanPodVIFDriver - m_driver = mock.Mock(spec=cls) - os_net = self.useFixture(k_fix.MockNetworkClient()).client - parent_port = mock.sentinel.parent_port - trunk_id = mock.sentinel.trunk_id - - m_driver._get_parent_port.return_value = parent_port - m_driver._get_trunk_id.return_value = trunk_id - pod = mock.sentinel.pod - vlan_id = mock.sentinel.vlan_id - vif = mock.Mock() - m_driver._port_vlan_mapping = {vif.id: vlan_id} - self.assertTrue(vif.id in m_driver._port_vlan_mapping) - - cls.release_vif(m_driver, pod, vif) - - m_driver._get_parent_port.assert_called_once_with(pod) - m_driver._get_trunk_id.assert_called_once_with(parent_port) - m_driver._remove_subport.assert_called_once_with(trunk_id, vif.id) - os_net.delete_port.assert_called_once_with(vif.id) - - def _test_get_port_request(self, m_to_fips, security_groups, - m_get_network_id, m_get_port_name, - unbound=False): - cls = nested_vlan_vif.NestedVlanPodVIFDriver - cls._tag_on_creation = True - m_driver = mock.Mock(spec=cls) - - pod = mock.sentinel.pod - project_id = mock.sentinel.project_id - subnets = mock.sentinel.subnets - port_name = mock.sentinel.port_name - network_id = mock.sentinel.project_id - fixed_ips = mock.sentinel.fixed_ips - - m_get_port_name.return_value = port_name - m_get_network_id.return_value = network_id - m_to_fips.return_value = fixed_ips - - oslo_cfg.CONF.set_override('port_debug', - True, - group='kubernetes') - - expected = {'project_id': project_id, - 'name': port_name, - 'network_id': network_id, - 'fixed_ips': fixed_ips, - 'device_owner': kl_const.DEVICE_OWNER, - 'admin_state_up': True} - - if security_groups: - expected['security_groups'] = security_groups - - if unbound: - expected['name'] = constants.KURYR_PORT_NAME - - ret = cls._get_port_request(m_driver, pod, project_id, subnets, - security_groups, unbound) - - self.assertEqual(expected, ret) - if unbound: - m_get_port_name.assert_not_called() - else: - m_get_port_name.assert_called_once_with(pod) - m_get_network_id.assert_called_once_with(subnets) - m_to_fips.assert_called_once_with(subnets) - - @mock.patch('kuryr_kubernetes.controller.drivers.utils.get_port_name') - @mock.patch('kuryr_kubernetes.controller.drivers.utils.get_network_id') - @mock.patch('kuryr_kubernetes.os_vif_util.osvif_to_neutron_fixed_ips') - def test_get_port_request(self, m_to_fips, m_get_network_id, - m_get_port_name): - security_groups = mock.sentinel.security_groups - self._test_get_port_request(m_to_fips, security_groups, - m_get_network_id, m_get_port_name) - - @mock.patch('kuryr_kubernetes.controller.drivers.utils.get_port_name') - @mock.patch('kuryr_kubernetes.controller.drivers.utils.get_network_id') - @mock.patch('kuryr_kubernetes.os_vif_util.osvif_to_neutron_fixed_ips') - def test_get_port_request_no_sg(self, m_to_fips, m_get_network_id, - m_get_port_name): - security_groups = [] - self._test_get_port_request(m_to_fips, security_groups, - m_get_network_id, m_get_port_name) - - @mock.patch('kuryr_kubernetes.controller.drivers.utils.get_port_name') - @mock.patch('kuryr_kubernetes.controller.drivers.utils.get_network_id') - @mock.patch('kuryr_kubernetes.os_vif_util.osvif_to_neutron_fixed_ips') - def test_get_port_request_unbound(self, m_to_fips, m_get_network_id, - m_get_port_name): - security_groups = mock.sentinel.security_groups - self._test_get_port_request(m_to_fips, security_groups, - m_get_network_id, m_get_port_name, - unbound=True) - - @mock.patch('kuryr.lib.segmentation_type_drivers.allocate_segmentation_id') - def test__create_subports_info(self, m_allocate_seg_id): - cls = nested_vlan_vif.NestedVlanPodVIFDriver - m_driver = mock.Mock(spec=cls) - - pod = mock.sentinel.pod - project_id = mock.sentinel.project_id - subnets = mock.sentinel.subnets - security_groups = mock.sentinel.security_groups - trunk_id = mock.sentinel.trunk_id - num_ports = 2 - in_use_vlan = set([1]) - port = mock.sentinel.port - subports_info = [{'segmentation_id': i + 2, - 'port_id': '', - 'segmentation_type': 'vlan'} - for i in range(num_ports)] - - m_driver._get_in_use_vlan_ids_set.return_value = in_use_vlan - m_driver._get_port_request.return_value = port - m_allocate_seg_id.side_effect = [2, 3] - - port_res, subports_res = cls._create_subports_info( - m_driver, pod, project_id, subnets, security_groups, trunk_id, - num_ports, unbound=False) - - self.assertEqual(port_res, port) - self.assertEqual(subports_res, subports_info) - - m_driver._get_in_use_vlan_ids_set.assert_called_once_with(trunk_id) - m_driver._get_port_request.assert_called_once_with( - pod, project_id, subnets, security_groups, False) - self.assertEqual(m_allocate_seg_id.call_count, 2) - - @mock.patch('kuryr.lib.segmentation_type_drivers.allocate_segmentation_id') - def test__create_subports_info_not_enough_vlans(self, m_allocate_seg_id): - cls = nested_vlan_vif.NestedVlanPodVIFDriver - m_driver = mock.Mock(spec=cls) - - pod = mock.sentinel.pod - project_id = mock.sentinel.project_id - subnets = mock.sentinel.subnets - security_groups = mock.sentinel.security_groups - trunk_id = mock.sentinel.trunk_id - num_ports = 2 - in_use_vlan = set([1]) - port = mock.sentinel.port - subports_info = [{'segmentation_id': 2, - 'port_id': '', - 'segmentation_type': 'vlan'}] - - m_driver._get_in_use_vlan_ids_set.return_value = in_use_vlan - m_driver._get_port_request.return_value = port - m_allocate_seg_id.side_effect = [ - 2, kl_exc.SegmentationIdAllocationFailure - ] - - port_res, subports_res = cls._create_subports_info( - m_driver, pod, project_id, subnets, security_groups, trunk_id, - num_ports, unbound=False) - - self.assertEqual(port_res, port) - self.assertEqual(subports_res, subports_info) - - m_driver._get_in_use_vlan_ids_set.assert_called_once_with(trunk_id) - m_driver._get_port_request.assert_called_once_with( - pod, project_id, subnets, security_groups, False) - self.assertEqual(m_allocate_seg_id.call_count, 2) - - @mock.patch('kuryr.lib.segmentation_type_drivers.allocate_segmentation_id') - def test__create_subports_info_no_vlans(self, m_allocate_seg_id): - cls = nested_vlan_vif.NestedVlanPodVIFDriver - m_driver = mock.Mock(spec=cls) - - pod = mock.sentinel.pod - project_id = mock.sentinel.project_id - subnets = mock.sentinel.subnets - security_groups = mock.sentinel.security_groups - trunk_id = mock.sentinel.trunk_id - num_ports = 2 - in_use_vlan = set([1]) - port = mock.sentinel.port - - m_driver._get_in_use_vlan_ids_set.return_value = in_use_vlan - m_driver._get_port_request.return_value = port - m_allocate_seg_id.side_effect = kl_exc.SegmentationIdAllocationFailure - - port_res, subports_res = cls._create_subports_info( - m_driver, pod, project_id, subnets, security_groups, trunk_id, - num_ports, unbound=False) - - self.assertEqual(port_res, port) - self.assertEqual(subports_res, []) - - m_driver._get_in_use_vlan_ids_set.assert_called_once_with(trunk_id) - m_driver._get_port_request.assert_called_once_with( - pod, project_id, subnets, security_groups, False) - self.assertEqual(m_allocate_seg_id.call_count, 1) - - def test_get_trunk_id(self): - cls = nested_vlan_vif.NestedVlanPodVIFDriver - m_driver = mock.Mock(spec=cls) - trunk_id = mock.sentinel.trunk_id - port = {'trunk_details': {'trunk_id': trunk_id}} - - self.assertEqual(trunk_id, cls._get_trunk_id(m_driver, port)) - - def test_get_trunk_id_details_missing(self): - cls = nested_vlan_vif.NestedVlanPodVIFDriver - m_driver = mock.Mock(spec=cls) - trunk_id = mock.sentinel.trunk_id - port = {'trunk_details_missing': {'trunk_id_missing': trunk_id}} - self.assertRaises(k_exc.K8sNodeTrunkPortFailure, - cls._get_trunk_id, m_driver, port) - - def test_add_subport(self): - cls = nested_vlan_vif.NestedVlanPodVIFDriver - m_driver = mock.Mock(spec=cls) - os_net = self.useFixture(k_fix.MockNetworkClient()).client - trunk_id = mock.sentinel.trunk_id - subport = mock.sentinel.subport - vlan_id = mock.sentinel.vlan_id - m_driver._get_vlan_id.return_value = vlan_id - subport_dict = [{'segmentation_id': vlan_id, - 'port_id': subport, - 'segmentation_type': 'vlan'}] - nested_vlan_vif.DEFAULT_MAX_RETRY_COUNT = 1 - self.assertEqual(vlan_id, cls._add_subport(m_driver, trunk_id, - subport)) - m_driver._get_vlan_id.assert_called_once_with(trunk_id) - os_net.add_trunk_subports.assert_called_once_with(trunk_id, - subport_dict) - - def test_add_subport_get_vlanid_failure(self): - cls = nested_vlan_vif.NestedVlanPodVIFDriver - m_driver = mock.Mock(spec=cls) - self.useFixture(k_fix.MockNetworkClient()).client - trunk_id = mock.sentinel.trunk_id - subport = mock.sentinel.subport - m_driver._get_vlan_id.side_effect = os_exc.SDKException - nested_vlan_vif.DEFAULT_MAX_RETRY_COUNT = 1 - self.assertRaises(os_exc.SDKException, cls._add_subport, m_driver, - trunk_id, subport) - - m_driver._get_vlan_id.assert_called_once_with(trunk_id) - - def test_add_subport_with_vlan_id_conflict(self): - cls = nested_vlan_vif.NestedVlanPodVIFDriver - m_driver = mock.Mock(spec=cls) - os_net = self.useFixture(k_fix.MockNetworkClient()).client - trunk_id = mock.sentinel.trunk_id - subport = mock.sentinel.subport - vlan_id = mock.sentinel.vlan_id - m_driver._get_vlan_id.return_value = vlan_id - subport_dict = [{'segmentation_id': vlan_id, - 'port_id': subport, - 'segmentation_type': 'vlan'}] - os_net.add_trunk_subports.side_effect = os_exc.ConflictException - nested_vlan_vif.DEFAULT_MAX_RETRY_COUNT = 1 - self.assertRaises(os_exc.ConflictException, cls._add_subport, m_driver, - trunk_id, subport) - - os_net.add_trunk_subports.assert_called_once_with(trunk_id, - subport_dict) - - def test__remove_subports(self): - cls = nested_vlan_vif.NestedVlanPodVIFDriver - m_driver = mock.Mock(spec=cls) - os_net = self.useFixture(k_fix.MockNetworkClient()).client - trunk_id = mock.sentinel.trunk_id - subport_id = mock.sentinel.subport_id - subportid_dict = [{'port_id': subport_id}] - cls._remove_subports(m_driver, trunk_id, [subport_id]) - - os_net.delete_trunk_subports.assert_called_once_with(trunk_id, - subportid_dict) - - def test__remove_subports_duplicate(self): - cls = nested_vlan_vif.NestedVlanPodVIFDriver - m_driver = mock.Mock(spec=cls) - os_net = self.useFixture(k_fix.MockNetworkClient()).client - trunk_id = mock.sentinel.trunk_id - subport_id = mock.sentinel.subport_id - subportid_dict = [{'port_id': subport_id}] - cls._remove_subports(m_driver, trunk_id, [subport_id, subport_id]) - - os_net.delete_trunk_subports.assert_called_once_with(trunk_id, - subportid_dict) - - @mock.patch('kuryr.lib.segmentation_type_drivers.allocate_segmentation_id') - def test_get_vlan_id(self, mock_alloc_seg_id): - cls = nested_vlan_vif.NestedVlanPodVIFDriver - m_driver = mock.Mock(spec=cls) - vlanid_set = mock.sentinel.vlanid_set - trunk_id = mock.sentinel.trunk_id - m_driver._get_in_use_vlan_ids_set.return_value = vlanid_set - cls._get_vlan_id(m_driver, trunk_id) - - mock_alloc_seg_id.assert_called_once_with(vlanid_set) - - @mock.patch('kuryr.lib.segmentation_type_drivers.allocate_segmentation_id') - def test_get_vlan_id_exhausted(self, mock_alloc_seg_id): - cls = nested_vlan_vif.NestedVlanPodVIFDriver - m_driver = mock.Mock(spec=cls) - vlanid_set = mock.sentinel.vlanid_set - trunk_id = mock.sentinel.trunk_id - m_driver._get_in_use_vlan_ids_set.return_value = vlanid_set - mock_alloc_seg_id.side_effect = kl_exc.SegmentationIdAllocationFailure - self.assertRaises(kl_exc.SegmentationIdAllocationFailure, - cls._get_vlan_id, m_driver, trunk_id) - - mock_alloc_seg_id.assert_called_once_with(vlanid_set) - - @mock.patch('kuryr.lib.segmentation_type_drivers.release_segmentation_id') - def test_release_vlan_id(self, mock_rel_seg_id): - cls = nested_vlan_vif.NestedVlanPodVIFDriver - m_driver = mock.Mock(spec=cls) - vlanid = mock.sentinel.vlanid - cls._release_vlan_id(m_driver, vlanid) - - mock_rel_seg_id.assert_called_once_with(vlanid) - - def test_get_in_use_vlan_ids_set(self): - cls = nested_vlan_vif.NestedVlanPodVIFDriver - m_driver = mock.Mock(spec=cls) - os_net = self.useFixture(k_fix.MockNetworkClient()).client - - vlan_ids = set() - trunk_id = mock.sentinel.trunk_id - vlan_ids.add('100') - - port = {"segmentation_id": '100'} # Trunk.sub_ports is a list of dicts - trunk_obj = os_trunk.Trunk(sub_ports=[port]) - os_net.get_trunk.return_value = trunk_obj - self.assertEqual(vlan_ids, - cls._get_in_use_vlan_ids_set(m_driver, trunk_id)) diff --git a/kuryr_kubernetes/tests/unit/controller/drivers/test_network_policy.py b/kuryr_kubernetes/tests/unit/controller/drivers/test_network_policy.py deleted file mode 100644 index 328aedaa5..000000000 --- a/kuryr_kubernetes/tests/unit/controller/drivers/test_network_policy.py +++ /dev/null @@ -1,644 +0,0 @@ -# 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 unittest import mock - -from oslo_config import cfg - -from kuryr_kubernetes.controller.drivers import network_policy -from kuryr_kubernetes import exceptions -from kuryr_kubernetes.tests import base as test_base -from kuryr_kubernetes.tests.unit import kuryr_fixtures as k_fix -from kuryr_kubernetes import utils - -CONF = cfg.CONF - - -def get_pod_obj(): - return { - 'status': { - 'qosClass': 'BestEffort', - 'hostIP': '192.168.1.2', - }, - 'kind': 'Pod', - 'spec': { - 'schedulerName': 'default-scheduler', - 'containers': [{ - 'name': 'busybox', - 'image': 'busybox', - 'resources': {} - }], - 'nodeName': 'kuryr-devstack' - }, - 'metadata': { - 'name': 'busybox-sleep1', - 'namespace': 'default', - 'resourceVersion': '53808', - 'uid': '452176db-4a85-11e7-80bd-fa163e29dbbb', - 'annotations': { - 'openstack.org/kuryr-vif': {} - } - }} - - -def get_namespace_obj(): - return { - "kind": "Namespace", - "metadata": { - "annotations": { - "openstack.org/kuryr-namespace-label": - "{\"projetc\": \"myproject\"}", - "openstack.org/kuryr-net-crd": "ns-myproject" - }, - "labels": { - "project": "myproject" - }, - "name": "myproject"}} - - -class TestNetworkPolicyDriver(test_base.TestCase): - - def setUp(self): - super(TestNetworkPolicyDriver, self).setUp() - self._project_id = mock.sentinel.project_id - self._policy_name = 'np-test' - self._policy_uid = mock.sentinel.policy_uid - self._policy_link = mock.sentinel.policy_link - self._sg_id = mock.sentinel.sg_id - self._i_rules = [{'sgRule': {'id': ''}}] - self._e_rules = [{'sgRule': {'id': ''}}] - - self._policy = { - 'apiVersion': 'networking.k8s.io/v1', - 'kind': 'NetworkPolicy', - 'metadata': { - 'name': self._policy_name, - 'resourceVersion': '2259309', - 'generation': 1, - 'creationTimestamp': '2018-09-18T14:09:51Z', - 'namespace': 'default', - 'annotations': {}, - 'uid': self._policy_uid - }, - 'spec': { - 'egress': [{'ports': - [{'port': 5978, 'protocol': 'TCP'}], - 'to': - [{'namespaceSelector': { - 'matchLabels': { - 'project': 'myproject'}}}]}], - 'ingress': [{'ports': - [{'port': 6379, 'protocol': 'TCP'}], - 'from': - [{'namespaceSelector': { - 'matchLabels': { - 'project': 'myproject'}}}]}], - 'policyTypes': ['Ingress', 'Egress'], - 'podSelector': {}, - } - } - - self.crd = { - 'metadata': {'name': 'foobar', - 'namespace': 'default'}, - 'spec': { - 'egressSgRules': [ - {'sgRule': - {'description': 'Kuryr-Kubernetes NetPolicy SG rule', - 'direction': 'egress', - 'ethertype': 'IPv4', - 'port_range_max': 5978, - 'port_range_min': 5978, - 'protocol': 'tcp', - }}], - 'ingressSgRules': [ - {'sgRule': - {'description': 'Kuryr-Kubernetes NetPolicy SG rule', - 'direction': 'ingress', - 'ethertype': 'IPv4', - 'port_range_max': 6379, - 'port_range_min': 6379, - 'protocol': 'tcp', - }}], - 'podSelector': {}, - 'policyTypes': self._policy['spec']['policyTypes'] - }, - 'status': { - 'securityGroupId': self._sg_id, - 'securityGroupRules': [], - 'podSelector': {}, - } - } - - self.old_crd = { - 'metadata': {'name': 'np-foobar', - 'namespace': 'default'}, - 'spec': { - 'egressSgRules': [ - {'security_group_rule': - {'description': 'Kuryr-Kubernetes NetPolicy SG rule', - 'direction': 'egress', - 'ethertype': 'IPv4', - 'port_range_max': 5978, - 'port_range_min': 5978, - 'protocol': 'tcp', - 'security_group_id': self._sg_id, - 'id': mock.sentinel.id - }}], - 'ingressSgRules': [ - {'security_group_rule': - {'description': 'Kuryr-Kubernetes NetPolicy SG rule', - 'direction': 'ingress', - 'ethertype': 'IPv4', - 'port_range_max': 6379, - 'port_range_min': 6379, - 'protocol': 'tcp', - 'security_group_id': self._sg_id, - 'id': mock.sentinel.id - }}], - 'podSelector': {}, - 'networkpolicy_spec': self._policy['spec'], - 'securityGroupId': self._sg_id, - 'securityGroupName': mock.sentinel.sg_name}} - - self.neutron = self.useFixture(k_fix.MockNetworkClient()).client - self.kubernetes = self.useFixture(k_fix.MockK8sClient()).client - self._driver = network_policy.NetworkPolicyDriver() - - @mock.patch.object(network_policy.NetworkPolicyDriver, - '_get_default_np_rules') - @mock.patch.object(network_policy.NetworkPolicyDriver, - '_get_knp_crd', return_value=False) - @mock.patch.object(network_policy.NetworkPolicyDriver, - '_create_knp_crd') - @mock.patch.object(network_policy.NetworkPolicyDriver, - '_parse_network_policy_rules') - @mock.patch.object(utils, 'get_subnet_cidr') - def test_ensure_network_policy(self, m_utils, m_parse, m_add_crd, - m_get_crd, m_get_default): - m_utils.get_subnet_cidr.return_value = mock.sentinel.cidr - m_parse.return_value = (self._i_rules, self._e_rules) - self.kubernetes.get = mock.Mock(return_value={}) - self._driver.ensure_network_policy(self._policy) - m_get_crd.assert_called_once() - m_add_crd.assert_called_once() - m_get_default.assert_called_once() - - @mock.patch('kuryr_kubernetes.controller.drivers.utils.' - 'create_security_group_rule_body') - @mock.patch.object(network_policy.NetworkPolicyDriver, - '_get_default_np_rules') - @mock.patch.object(network_policy.NetworkPolicyDriver, - '_get_knp_crd', return_value=False) - @mock.patch.object(network_policy.NetworkPolicyDriver, - '_create_knp_crd') - @mock.patch.object(network_policy.NetworkPolicyDriver, - '_parse_network_policy_rules') - @mock.patch.object(utils, 'get_subnet_cidr') - def test_ensure_network_policy_services(self, m_utils, m_parse, m_add_crd, - m_get_crd, m_get_default, - m_create_sgr): - CONF.set_override('enforce_sg_rules', False, group='octavia_defaults') - self.addCleanup(CONF.set_override, 'enforce_sg_rules', True, - group='octavia_defaults') - m_utils.get_subnet_cidr.return_value = mock.sentinel.cidr - m_parse.return_value = (self._i_rules, self._e_rules) - svcs = [ - {'metadata': {'name': 'foo', 'deletionTimestamp': 'foobar'}}, - {'metadata': {'name': 'bar'}, 'spec': {'clusterIP': 'None'}}, - {'metadata': {'name': 'baz'}, 'spec': {'clusterIP': None}}, - {'metadata': {'name': ''}, 'spec': {'clusterIP': '192.168.0.130'}}, - ] - self.kubernetes.get = mock.Mock(return_value={'items': svcs}) - self._driver.ensure_network_policy(self._policy) - m_create_sgr.assert_called_once_with('ingress', cidr='192.168.0.130', - description=mock.ANY) - m_get_crd.assert_called_once() - m_add_crd.assert_called_once() - m_get_default.assert_called_once() - - @mock.patch.object(network_policy.NetworkPolicyDriver, - '_get_default_np_rules') - @mock.patch.object(network_policy.NetworkPolicyDriver, - '_get_knp_crd') - @mock.patch.object(network_policy.NetworkPolicyDriver, - '_parse_network_policy_rules') - @mock.patch.object(utils, 'get_subnet_cidr') - def test_ensure_network_policy_with_k8s_exc(self, m_utils, m_parse, - m_get_crd, m_get_default): - m_utils.get_subnet_cidr.return_value = mock.sentinel.cidr - m_parse.return_value = (self._i_rules, self._e_rules) - m_get_crd.side_effect = exceptions.K8sClientException - self.kubernetes.get = mock.Mock(return_value={}) - self.assertRaises(exceptions.K8sClientException, - self._driver.ensure_network_policy, self._policy) - m_get_default.assert_called_once() - - @mock.patch.object(network_policy.NetworkPolicyDriver, - '_get_default_np_rules') - @mock.patch.object(network_policy.NetworkPolicyDriver, - '_get_knp_crd', return_value=None) - @mock.patch.object(network_policy.NetworkPolicyDriver, '_create_knp_crd') - @mock.patch.object(network_policy.NetworkPolicyDriver, - '_parse_network_policy_rules') - @mock.patch.object(utils, 'get_subnet_cidr') - def test_ensure_network_policy_error_add_crd( - self, m_utils, m_parse, m_add_crd, m_get_crd, m_get_default): - m_utils.get_subnet_cidr.return_value = mock.sentinel.cidr - m_parse.return_value = (self._i_rules, self._e_rules) - m_add_crd.side_effect = exceptions.K8sClientException - self.kubernetes.get = mock.Mock(return_value={}) - self.assertRaises(exceptions.K8sClientException, - self._driver.ensure_network_policy, self._policy) - m_get_crd.assert_called() - m_get_default.assert_called_once() - - def test_get_namespaces(self): - namespace_selector = {'namespaceSelector': { - 'matchLabels': {'project': 'myproject'}}} - self.kubernetes.get.side_effect = [{'items': [get_namespace_obj()]}] - - resp = self._driver._get_namespaces(namespace_selector) - self.assertEqual([get_namespace_obj()], resp) - self.kubernetes.get.assert_called() - - def test_get_namespaces_no_matches(self): - namespace_selector = {'matchLabels': {'test': 'test'}} - self.kubernetes.get.return_value = {'items': []} - - resp = self._driver._get_namespaces(namespace_selector) - self.assertEqual([], resp) - self.kubernetes.get.assert_called_once() - - @mock.patch('kuryr_kubernetes.controller.drivers.utils.get_services') - @mock.patch.object(network_policy.NetworkPolicyDriver, - '_get_resource_details') - @mock.patch.object(network_policy.NetworkPolicyDriver, - '_get_namespaces') - @mock.patch('kuryr_kubernetes.controller.drivers.utils.' - 'create_security_group_rule_body') - def test_parse_network_policy_rules_with_rules( - self, m_create, m_get_namespaces, - m_get_resource_details, m_get_svcs): - subnet_cidr = '10.10.0.0/24' - namespace = 'myproject' - m_get_namespaces.return_value = [get_namespace_obj()] - m_get_resource_details.return_value = subnet_cidr, namespace - self._driver._parse_network_policy_rules(self._policy) - m_get_namespaces.assert_called() - m_get_resource_details.assert_called() - m_create.assert_called() - - @mock.patch.object(network_policy.NetworkPolicyDriver, - '_get_namespaces') - @mock.patch('kuryr_kubernetes.controller.drivers.utils.' - 'create_security_group_rule_body') - def test_parse_network_policy_rules_with_no_rules(self, m_create, - m_get_ns): - policy = self._policy.copy() - policy['spec']['ingress'] = [{}] - policy['spec']['egress'] = [{}] - self._driver._parse_network_policy_rules(policy) - m_get_ns.assert_not_called() - calls = [mock.call('ingress', ethertype='IPv4'), - mock.call('ingress', ethertype='IPv6'), - mock.call('egress', ethertype='IPv4'), - mock.call('egress', ethertype='IPv6')] - m_create.assert_has_calls(calls) - - @mock.patch.object(network_policy.NetworkPolicyDriver, - '_create_all_pods_sg_rules') - def test_parse_network_policy_rules_with_no_pod_selector( - self, m_create_all_pods_sg_rules): - policy = self._policy.copy() - policy['spec']['ingress'] = [{'ports': - [{'port': 6379, 'protocol': 'TCP'}]}] - policy['spec']['egress'] = [{'ports': - [{'port': 6379, 'protocol': 'TCP'}]}] - self._driver._parse_network_policy_rules(policy) - m_create_all_pods_sg_rules.assert_called() - - @mock.patch.object(network_policy.NetworkPolicyDriver, - '_create_sg_rule_on_number_port') - @mock.patch.object(network_policy.NetworkPolicyDriver, - '_get_namespaces') - def test_parse_network_policy_rules_with_ipblock(self, - m_get_namespaces, - m_create_sg_rule): - policy = self._policy.copy() - policy['spec']['ingress'] = [{'from': - [{'ipBlock': - {'cidr': '172.17.0.0/16', - 'except': ['172.17.1.0/24']}}], - 'ports': [{'port': 6379, - 'protocol': 'TCP'}]}] - policy['spec']['egress'] = [{'ports': [{'port': 5978, 'protocol': - 'TCP'}], - 'to': [{'ipBlock': - {'cidr': '10.0.0.0/24'}}]}] - self._driver._parse_network_policy_rules(policy) - m_create_sg_rule.assert_called() - - @mock.patch('kuryr_kubernetes.controller.drivers.utils.get_services') - @mock.patch.object(network_policy.NetworkPolicyDriver, - '_get_resource_details') - @mock.patch.object(network_policy.NetworkPolicyDriver, - '_get_namespaces') - @mock.patch('kuryr_kubernetes.controller.drivers.utils.' - 'create_security_group_rule_body') - def test_parse_network_policy_rules_with_no_ports( - self, m_create, m_get_namespaces, m_get_resource_details, - m_get_svcs): - subnet_cidr = '10.10.0.0/24' - namespace = 'myproject' - m_get_namespaces.return_value = [get_namespace_obj()] - m_get_resource_details.return_value = subnet_cidr, namespace - policy = self._policy.copy() - selectors = {'namespaceSelector': { - 'matchLabels': { - 'project': 'myproject'}}} - policy['spec']['egress'] = [{'to': [selectors]}] - policy['spec']['ingress'] = [{'from': [selectors]}] - self._driver._parse_network_policy_rules(policy) - m_get_namespaces.assert_called() - m_get_resource_details.assert_called() - calls = [mock.call('ingress', cidr=subnet_cidr, namespace=namespace), - mock.call('egress', cidr=subnet_cidr, namespace=namespace)] - m_create.assert_has_calls(calls) - - @mock.patch.object(network_policy.NetworkPolicyDriver, 'namespaced_pods') - def test_affected_pods(self, m_namespaced): - self._driver.affected_pods(self._policy) - m_namespaced.assert_called_once_with(self._policy) - self.kubernetes.assert_not_called() - - @mock.patch.object(network_policy.NetworkPolicyDriver, 'namespaced_pods') - def test_affected_pods_with_podselector(self, m_namespaced): - self.kubernetes.get.return_value = {'items': []} - selector = {'matchLabels': {'test': 'test'}} - self._driver.affected_pods(self._policy, selector) - m_namespaced.assert_not_called() - - @mock.patch.object(network_policy.NetworkPolicyDriver, 'namespaced_pods') - def test_affected_pods_with_empty_podselector(self, m_namespaced): - m_namespaced.return_value = [] - pod_selector = {} - self._driver.affected_pods(self._policy, pod_selector) - m_namespaced.assert_called_with(self._policy) - - def test_namespaced_pods(self): - self.kubernetes.get.return_value = {'items': []} - - resp = self._driver.namespaced_pods(self._policy) - self.assertEqual([], resp) - - @mock.patch.object(network_policy.NetworkPolicyDriver, - '_del_knp_crd', return_value=False) - def test_release_network_policy(self, m_del_crd): - self._driver.release_network_policy(self.crd) - m_del_crd.assert_called_once_with(self.crd) - - @mock.patch.object(network_policy.NetworkPolicyDriver, - '_create_sg_rules_with_container_ports') - @mock.patch('kuryr_kubernetes.controller.drivers.utils.get_ports') - @mock.patch('kuryr_kubernetes.controller.drivers.utils.get_pods') - def test__create_sg_rule_body_on_text_port_ingress(self, - m_get_pods, - m_get_ports, - m_create_sgr_cont): - pod = mock.sentinel.pod - port = mock.sentinel.port - container_ports = mock.sentinel.ports - resources = [mock.sentinel.resource] - crd_rules = mock.sentinel.crd_rules - pod_selector = {} - namespace = mock.sentinel.namespace - direction = 'ingress' - - m_get_pods.return_value = {'items': [pod]} - m_get_ports.return_value = container_ports - - self._driver._create_sg_rule_body_on_text_port(direction, - port, - resources, - crd_rules, - pod_selector, - namespace) - - m_get_pods.assert_called_with(pod_selector, namespace) - m_get_ports.assert_called_with(pod, port) - - @mock.patch('kuryr_kubernetes.controller.drivers.utils.' - 'create_security_group_rule_body') - @mock.patch.object(network_policy.NetworkPolicyDriver, - '_create_sg_rules_with_container_ports') - @mock.patch('kuryr_kubernetes.controller.drivers.utils.get_ports') - @mock.patch('kuryr_kubernetes.controller.drivers.utils.get_pods') - def test__create_sg_rule_body_on_text_port_ingress_all(self, - m_get_pods, - m_get_ports, - m_create_sgr_cont, - m_create_sgr): - pod = mock.sentinel.pod - port = mock.sentinel.port - container_ports = mock.sentinel.ports - resources = [mock.sentinel.resource] - crd_rules = mock.sentinel.crd_rules - pod_selector = {} - namespace = mock.sentinel.namespace - direction = 'ingress' - cidrs = ['0.0.0.0/0'] - - m_get_pods.return_value = {'items': [pod]} - m_get_ports.return_value = container_ports - - self._driver._create_sg_rule_body_on_text_port(direction, - port, - resources, - crd_rules, - pod_selector, - namespace, - allowed_cidrs=cidrs) - - m_get_pods.assert_called_with(pod_selector, namespace) - m_get_ports.assert_called_with(pod, port) - m_create_sgr.assert_not_called() - - @mock.patch('kuryr_kubernetes.controller.drivers.utils.' - 'create_security_group_rule_body') - @mock.patch('kuryr_kubernetes.controller.drivers.utils.get_ports') - @mock.patch('kuryr_kubernetes.controller.drivers.utils.get_pods') - def test__create_sg_rule_body_on_text_port_ingress_match(self, - m_get_pods, - m_get_ports, - m_create_sgr): - - def _create_sgr_cont(container_ports, allow_all, resource, - matched_pods, crd_rules, direction, port, - pod_selector=None, policy_namespace=None): - matched_pods[container_ports[0][1]] = 'foo' - - pod = mock.sentinel.pod - port = {'protocol': 'TCP', 'port': 22} - container_ports = [("pod", mock.sentinel.container_port)] - resources = [mock.sentinel.resource] - crd_rules = [] - pod_selector = {} - namespace = mock.sentinel.namespace - direction = 'ingress' - cidrs = ['0.0.0.0/0'] - self._driver._create_sg_rules_with_container_ports = _create_sgr_cont - - m_get_pods.return_value = {'items': [pod]} - m_get_ports.return_value = container_ports - - self._driver._create_sg_rule_body_on_text_port(direction, - port, - resources, - crd_rules, - pod_selector, - namespace, - allowed_cidrs=cidrs) - - m_get_pods.assert_called_with(pod_selector, namespace) - m_get_ports.assert_called_with(pod, port) - - m_create_sgr.assert_called_once_with(direction, container_ports[0][1], - protocol=port['protocol'], - cidr=cidrs[0], - pods='foo') - - @mock.patch.object(network_policy.NetworkPolicyDriver, - '_create_sg_rules_with_container_ports') - @mock.patch('kuryr_kubernetes.controller.drivers.utils.get_ports') - @mock.patch('kuryr_kubernetes.controller.drivers.utils.get_pods') - def test__create_sg_rule_body_on_text_port_egress(self, - m_get_pods, - m_get_ports, - m_create_sgr_cont): - pod = mock.sentinel.pod - port = mock.sentinel.port - container_ports = mock.sentinel.ports - resources = [{'spec': 'foo'}] - crd_rules = mock.sentinel.crd_rules - pod_selector = {} - namespace = mock.sentinel.namespace - direction = 'egress' - - m_get_pods.return_value = {'items': [pod]} - m_get_ports.return_value = container_ports - - self._driver._create_sg_rule_body_on_text_port(direction, - port, - resources, - crd_rules, - pod_selector, - namespace) - - m_get_ports.assert_called_with(resources[0], port) - - @mock.patch.object(network_policy.NetworkPolicyDriver, - '_create_sg_rules_with_container_ports') - @mock.patch('kuryr_kubernetes.controller.drivers.utils.get_ports') - def test__create_sg_rule_body_on_text_port_egress_all(self, - m_get_ports, - m_create_sgr_cont): - port = {'protocol': 'TCP', 'port': 22} - container_ports = mock.sentinel.ports - resources = [{'spec': 'foo'}] - crd_rules = [] - pod_selector = {} - namespace = mock.sentinel.namespace - direction = 'egress' - cidrs = ['0.0.0.0/0'] - - m_get_ports.return_value = container_ports - - self._driver._create_sg_rule_body_on_text_port(direction, - port, - resources, - crd_rules, - pod_selector, - namespace, - allowed_cidrs=cidrs) - - m_get_ports.assert_called_with(resources[0], port) - self.assertEqual(len(crd_rules), 0) - - @mock.patch('kuryr_kubernetes.utils.get_subnet_cidr') - @mock.patch('kuryr_kubernetes.controller.drivers.utils.' - 'create_security_group_rule_body') - @mock.patch('kuryr_kubernetes.controller.drivers.utils.get_ports') - @mock.patch('kuryr_kubernetes.controller.drivers.utils.get_pods') - def test__create_sg_rule_body_on_text_port_egress_match(self, - m_get_pods, - m_get_ports, - m_create_sgr, - m_get_subnet_cidr): - - def _create_sgr_cont(container_ports, allow_all, resource, - matched_pods, crd_rules, sg_id, direction, port, - pod_selector=None, policy_namespace=None): - matched_pods[container_ports[0][1]] = 'foo' - - pod = mock.sentinel.pod - port = {'protocol': 'TCP', 'port': 22} - container_ports = [("pod", mock.sentinel.container_port)] - resources = [{'spec': 'foo'}] - crd_rules = [] - pod_selector = {} - namespace = mock.sentinel.namespace - direction = 'egress' - cidrs = ['0.0.0.0/0'] - self._driver._create_sg_rules_with_container_ports = _create_sgr_cont - m_get_subnet_cidr.return_value = '10.0.0.128/26' - m_create_sgr.side_effect = [mock.sentinel.sgr1, mock.sentinel.sgr2, - mock.sentinel.sgr3] - - m_get_pods.return_value = {'items': [pod]} - m_get_ports.return_value = container_ports - - self._driver._create_sg_rule_body_on_text_port(direction, - port, - resources, - crd_rules, - pod_selector, - namespace, - allowed_cidrs=cidrs) - - m_get_ports.assert_called_with(resources[0], port) - m_create_sgr.assert_called_once_with(direction, container_ports[0][1], - protocol=port['protocol'], - cidr=cidrs[0], pods='foo') - - def test__create_all_pods_sg_rules(self): - port = {'protocol': 'TCP', 'port': 22} - direction = 'ingress' - rules = [] - - self._driver._create_all_pods_sg_rules(port, direction, rules, '', - None) - self.assertEqual(len(rules), 2) - - def test__create_default_sg_rule(self): - for direction in ('ingress', 'egress'): - rules = [] - - self._driver._create_default_sg_rule(direction, rules) - self.assertEqual(len(rules), 2) - self.assertListEqual(rules, [{'sgRule': { - 'ethertype': e, - 'direction': direction, - 'description': 'Kuryr-Kubernetes NetPolicy SG rule' - }} for e in ('IPv4', 'IPv6')]) diff --git a/kuryr_kubernetes/tests/unit/controller/drivers/test_network_policy_security_groups.py b/kuryr_kubernetes/tests/unit/controller/drivers/test_network_policy_security_groups.py deleted file mode 100644 index b41bc4fd1..000000000 --- a/kuryr_kubernetes/tests/unit/controller/drivers/test_network_policy_security_groups.py +++ /dev/null @@ -1,616 +0,0 @@ -# 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 unittest import mock - -from kuryr_kubernetes.controller.drivers import network_policy_security_groups -from kuryr_kubernetes.tests import base as test_base -from kuryr_kubernetes.tests.unit import kuryr_fixtures as k_fix - -from oslo_config import cfg - - -def get_no_match_crd_namespace_obj(): - return { - "kind": "Namespace", - "metadata": { - "annotations": { - "openstack.org/kuryr-namespace-label": '{"name": "dev"}', - "openstack.org/kuryr-net-crd": "ns-dev" - }, - "labels": {"name": "prod"}, - "name": "prod"}} - - -def get_match_crd_namespace_obj(): - return { - "kind": "Namespace", - "metadata": { - "annotations": { - "openstack.org/kuryr-namespace-label": '{"name": "dev"}', - "openstack.org/kuryr-net-crd": "ns-dev" - }, - "labels": { - "name": "dev" - }, - "name": "dev"}} - - -def get_match_crd_pod_obj(): - return { - 'kind': 'Pod', - 'metadata': { - 'name': mock.sentinel.pod_name, - 'namespace': 'dev', - 'labels': { - 'tier': 'backend'}, - 'annotations': { - 'openstack.org/kuryr-pod-label': '{"tier": "backend"}'}}, - 'status': {'podIP': mock.sentinel.podIP}} - - -def get_sg_rule(): - pod_ip = get_match_crd_pod_obj()['status'].get('podIP') - return { - "namespace": 'dev', - "sgRule": { - "description": "Kuryr-Kubernetes NetPolicy SG rule", - "direction": "ingress", - "ethertype": "IPv4", - "id": 'f15ff50a-e8a4-4872-81bf-a04cbb8cb388', - "port_range_max": 6379, - "port_range_min": 6379, - "protocol": "tcp", - "remote_ip_prefix": pod_ip, - "security_group_id": '36923e76-026c-422b-8dfd-7292e7c88228'}} - - -def get_matched_crd_obj(): - return { - "kind": "KuryrNetworkPolicy", - "metadata": {"name": "np-test-network-policy", - "namespace": "default"}, - "spec": { - "egressSgRules": [], - "ingressSgRules": [get_sg_rule()], - "networkpolicy_spec": { - "ingress": [ - {"from": [ - {"namespaceSelector": { - "matchLabels": {"name": "dev"}}}], - "ports": [ - {"port": 6379, - "protocol": "TCP"}]}], - "podSelector": {"matchLabels": {"app": "demo"}}, - "policyTypes": ["Ingress"]}, - "podSelector": {"matchLabels": {"app": "demo"}}, - "securityGroupId": '36923e76-026c-422b-8dfd-7292e7c88228'}} - - -def get_crd_obj_no_match(): - return { - "kind": "KuryrNetworkPolicy", - "metadata": {"name": "np-test-network-policy", - "namespace": "default"}, - "spec": { - "egressSgRules": [], - "ingressSgRules": [], - "networkpolicy_spec": { - "ingress": [ - {"from": [ - {"namespaceSelector": { - "matchLabels": {"name": "dev"}}}], - "ports": [ - {"port": 6379, - "protocol": "TCP"}]}], - "podSelector": {"matchLabels": {"app": "demo"}}, - "policyTypes": ["Ingress"]}, - "podSelector": {"matchLabels": {"app": "demo"}}, - "securityGroupId": '36923e76-026c-422b-8dfd-7292e7c88228'}} - - -def get_crd_obj_with_all_selectors(): - return { - "kind": "KuryrNetworkPolicy", - "metadata": {"name": "np-test-network-policy", - "namespace": "default"}, - "spec": { - "egressSgRules": [], - "ingressSgRules": [], - "networkpolicy_spec": { - "ingress": [ - {"from": [ - {"namespaceSelector": { - "matchLabels": {"name": "dev"}}, - "podSelector": { - "matchLabels": {"tier": "backend"}}}], - "ports": [ - {"port": 6379, - "protocol": "TCP"}]}], - "podSelector": {"matchLabels": {"app": "demo"}}, - "policyTypes": ["Ingress"]}, - "podSelector": {"matchLabels": {"app": "demo"}}, - "securityGroupId": '36923e76-026c-422b-8dfd-7292e7c88228'}} - - -class TestNetworkPolicySecurityGroupsDriver(test_base.TestCase): - - def setUp(self): - super(TestNetworkPolicySecurityGroupsDriver, self).setUp() - self._project_id = mock.sentinel.project_id - self._sg_id = mock.sentinel.sg_id - self._sg_id2 = mock.sentinel._sg_id2 - self._namespace = 'default' - self._crd = { - 'metadata': {'name': mock.sentinel.name}, - 'spec': { - 'egressSgRules': [ - {'sgRule': - {'description': 'Kuryr-Kubernetes NetPolicy SG rule', - 'direction': 'egress', - 'ethertype': 'IPv4', - 'port_range_max': 5978, - 'port_range_min': 5978, - 'protocol': 'tcp', - 'security_group_id': self._sg_id, - 'id': mock.sentinel.id - }}], - 'ingressSgRules': [ - {'sgRule': - {'description': 'Kuryr-Kubernetes NetPolicy SG rule', - 'direction': 'ingress', - 'ethertype': 'IPv4', - 'port_range_max': 6379, - 'port_range_min': 6379, - 'protocol': 'tcp', - 'security_group_id': self._sg_id, - 'id': mock.sentinel.id - }}], - 'podSelector': { - 'matchExpressions': [ - { - 'key': 'environment', - 'operator': 'In', - 'values': [ - 'production']}], - 'matchLabels': { - 'run': 'demo' - }}}, - 'status': { - 'securityGroupId': self._sg_id, - }, - } - - self._crd2 = { - 'metadata': {'name': mock.sentinel.name3}, - 'spec': { - 'ingressSgRules': [ - {'sgRule': - {'description': 'Kuryr-Kubernetes NetPolicy SG rule', - 'direction': 'ingress', - 'ethertype': 'IPv4', - 'port_range_max': 8080, - 'port_range_min': 8080, - 'protocol': 'tcp', - 'security_group_id': self._sg_id2, - 'id': mock.sentinel.id - }}], - 'podSelector': {}}, - 'status': { - 'securityGroupId': self._sg_id2, - 'securityGroupName': mock.sentinel.sg_name}} - - self._crds = [self._crd] - - self._multiple_crds = [self._crd, self._crd2] - - self._pod = { - 'apiVersion': 'v1', - 'kind': 'Pod', - 'metadata': { - 'name': mock.sentinel.pod_name, - 'namespace': self._namespace, - 'labels': { - 'run': 'demo', - 'environment': 'production'}}, - 'spec': { - 'containers': [{ - 'image': 'quay.io/kuryr/demo', - 'imagePullPolicy': 'Always', - 'name': mock.sentinel.pod_name - }] - }} - - self._pod2 = { - 'apiVersion': 'v1', - 'kind': 'Pod', - 'metadata': { - 'name': mock.sentinel.pod_name, - 'namespace': self._namespace, - 'labels': { - 'run': 'demo', - 'environment': 'development'}, - 'annotations': { - 'openstack.org/kuryr-pod-label': '{' - '"run": "demo","environment": "development"}'}}, - 'spec': { - 'containers': [{ - 'image': 'quay.io/kuryr/demo', - 'imagePullPolicy': 'Always', - 'name': mock.sentinel.pod_name - }] - }} - - self._pod_without_label = { - 'apiVersion': 'v1', - 'kind': 'Pod', - 'metadata': { - 'name': mock.sentinel.pod_name, - 'namespace': self._namespace}, - 'spec': { - 'containers': [{ - 'image': 'quay.io/kuryr/demo', - 'imagePullPolicy': 'Always', - 'name': mock.sentinel.pod_name - }] - }} - - self.kubernetes = self.useFixture(k_fix.MockK8sClient()).client - self._driver = ( - network_policy_security_groups.NetworkPolicySecurityGroupsDriver()) - - self._pod_ip = mock.sentinel.pod_ip - self._pod_dev_namespace = { - 'apiVersion': 'v1', - 'kind': 'Pod', - 'metadata': { - 'name': mock.sentinel.pod_name, - 'namespace': 'dev', - 'labels': { - 'tier': 'backend'}, - 'annotations': { - 'openstack.org/kuryr-pod-label': '{"tier": "backend"}'}}, - 'spec': { - 'containers': [{ - 'image': 'quay.io/kuryr/demo', - 'imagePullPolicy': 'Always', - 'name': mock.sentinel.pod_name - }]}, - 'status': {'podIP': self._pod_ip}} - - self._crd_sg_id = mock.sentinel.crd_sg_id - self._sg_rule_body = { - 'sgRule': { - 'direction': 'ingress', - 'protocol': 'tcp', - 'description': 'Kuryr-Kubernetes NetPolicy SG rule', - 'ethertype': 'IPv4', - 'port_range_max': 6379, - 'security_group_id': self._crd_sg_id, - 'port_range_min': 6379, - 'remote_ip_prefix': self._pod_ip}} - - self._new_rule_id = mock.sentinel.id - self._crd_with_rule = { - "apiVersion": "openstack.org/v1", - "kind": "KuryrNetworkPolicy", - "metadata": {"name": "np-test-network-policy", - "namespace": "default"}, - "spec": { - "egressSgRules": [], - "ingressSgRules": [{ - "sgRule": { - "description": "Kuryr-Kubernetes NetPolicy SG rule", - "direction": "ingress", - "ethertype": "IPv4", - "id": self._new_rule_id, - "port_range_max": 6379, - "port_range_min": 6379, - "protocol": "tcp", - "remote_ip_prefix": self._pod_ip, - "security_group_id": self._crd_sg_id}}], - "networkpolicy_spec": { - "ingress": [ - {"from": [ - {"namespaceSelector": { - "matchLabels": {"name": "dev"}}, - "podSelector": { - "matchLabels": {"tier": "backend"}}}], - "ports": [ - {"port": 6379, - "protocol": "TCP"}]}], - "podSelector": {"matchLabels": {"app": "demo"}}, - "policyTypes": ["Ingress"]}, - "podSelector": {"matchLabels": {"app": "demo"}}, - "securityGroupId": self._crd_sg_id}} - - @mock.patch('kuryr_kubernetes.controller.drivers.utils.' - 'match_selector', return_value=True) - @mock.patch('kuryr_kubernetes.controller.drivers.utils.get_pod_ip') - def test__create_sg_rules(self, m_get_pod_ip, - m_match_selector): - sgr_id = mock.sentinel.sgr_id - crd = get_crd_obj_with_all_selectors() - pod = get_match_crd_pod_obj() - m_get_pod_ip.return_value = pod['status'].get('podIP') - matched = False - new_sg_rule = self._sg_rule_body - - policy = crd['spec']['networkpolicy_spec'] - rule_list = policy.get('ingress', None) - pod_ns = pod['metadata']['namespace'] - - for rule_block in rule_list: - for rule in rule_block.get('from', []): - pod_selector = rule.get('podSelector') - matched = network_policy_security_groups._create_sg_rules( - crd, pod, pod_selector, rule_block, 'ingress', matched) - new_sg_rule['namespace'] = pod_ns - new_sg_rule['sgRule']['id'] = sgr_id - m_match_selector.assert_called_once_with( - pod_selector, pod['metadata']['labels']) - m_get_pod_ip.assert_called_once_with(pod) - self.assertEqual(matched, True) - - @mock.patch('kuryr_kubernetes.controller.drivers.utils.' - 'get_pod_ip') - @mock.patch('kuryr_kubernetes.controller.drivers.utils.' - 'match_selector', return_value=False) - def test__create_sg_rules_no_match(self, m_match_selector, m_get_pod_ip): - crd = get_crd_obj_with_all_selectors() - pod = self._pod2 - - policy = crd['spec']['networkpolicy_spec'] - rule_list = policy.get('ingress', None) - - for rule_block in rule_list: - for rule in rule_block.get('from', []): - pod_selector = rule.get('podSelector') - matched = network_policy_security_groups._create_sg_rules( - crd, pod, pod_selector, rule_block, 'ingress', False) - self.assertEqual(matched, False) - - @mock.patch('kuryr_kubernetes.controller.drivers.utils.bump_networkpolicy') - @mock.patch('kuryr_kubernetes.controller.drivers.utils.' - 'get_kuryrnetworkpolicy_crds') - @mock.patch('kuryr_kubernetes.controller.drivers.utils.get_pod_ip') - def test_delete_sg_rules(self, m_get_pod_ip, m_get_knp_crds, m_bump): - crd = self._crd_with_rule - m_get_pod_ip.return_value = self._pod_ip - m_get_knp_crds.return_value = [crd] - pod = self._pod_dev_namespace - - self._driver.delete_sg_rules(pod) - - m_get_knp_crds.assert_called_once() - m_get_pod_ip.assert_called_once_with(pod) - m_bump.assert_called_once() - - @mock.patch('kuryr_kubernetes.config.CONF') - @mock.patch('kuryr_kubernetes.controller.drivers.utils.' - 'get_kuryrnetworkpolicy_crds') - def test_get_sgs_for_pod_without_label(self, m_get_crds, m_cfg): - m_get_crds.return_value = self._crds - sg_list = [str(mock.sentinel.sg_id)] - m_cfg.neutron_defaults.pod_security_groups = sg_list - - sgs = self._driver.get_security_groups(self._pod_without_label, - self._project_id) - - m_get_crds.assert_called_once_with(namespace=self._namespace) - self.assertEqual(sg_list, sgs) - - @mock.patch('kuryr_kubernetes.controller.drivers.utils.' - 'match_expressions') - @mock.patch('kuryr_kubernetes.controller.drivers.utils.' - 'match_labels') - @mock.patch('kuryr_kubernetes.controller.drivers.utils.' - 'get_kuryrnetworkpolicy_crds') - def test_get_sgs_for_pod_with_label(self, m_get_crds, m_match_labels, - m_match_expressions): - m_get_crds.return_value = self._crds - m_match_expressions.return_value = True - m_match_labels.return_value = True - pod_labels = self._pod['metadata']['labels'] - resp = self._driver.get_security_groups(self._pod, self._project_id) - - m_get_crds.assert_called_once_with(namespace=self._namespace) - m_match_expressions.assert_called_once_with( - self._crd['spec']['podSelector']['matchExpressions'], pod_labels) - m_match_labels.assert_called_once_with( - self._crd['spec']['podSelector']['matchLabels'], pod_labels) - self.assertEqual(resp, [self._sg_id]) - - @mock.patch('kuryr_kubernetes.config.CONF') - @mock.patch('kuryr_kubernetes.controller.drivers.utils.' - 'match_expressions') - @mock.patch('kuryr_kubernetes.controller.drivers.utils.' - 'match_labels') - @mock.patch('kuryr_kubernetes.controller.drivers.utils.' - 'get_kuryrnetworkpolicy_crds') - def test_get_sgs_for_pod_with_label_no_match(self, m_get_crds, - m_match_labels, - m_match_expressions, m_cfg): - m_get_crds.return_value = self._crds - m_match_expressions.return_value = False - m_match_labels.return_value = True - sg_list = [mock.sentinel.sg_id] - m_cfg.neutron_defaults.pod_security_groups = sg_list - pod_labels = self._pod2['metadata']['labels'] - - sgs = self._driver.get_security_groups(self._pod2, self._project_id) - - m_get_crds.assert_called_once_with(namespace=self._namespace) - m_match_expressions.assert_called_once_with( - self._crd['spec']['podSelector']['matchExpressions'], pod_labels) - m_match_labels.assert_called_once_with( - self._crd['spec']['podSelector']['matchLabels'], pod_labels) - self.assertEqual(sg_list, sgs) - - @mock.patch('kuryr_kubernetes.controller.drivers.utils.' - 'get_kuryrnetworkpolicy_crds') - def test_get_sgs_no_crds(self, m_get_crds): - m_get_crds.return_value = [] - cfg.CONF.set_override('pod_security_groups', [], - group='neutron_defaults') - - self.assertRaises(cfg.RequiredOptError, - self._driver.get_security_groups, self._pod, - self._project_id) - m_get_crds.assert_called_with(namespace=self._namespace) - - @mock.patch('kuryr_kubernetes.controller.drivers.utils.' - 'match_expressions') - @mock.patch('kuryr_kubernetes.controller.drivers.utils.' - 'match_labels') - @mock.patch('kuryr_kubernetes.controller.drivers.utils.' - 'get_kuryrnetworkpolicy_crds') - def test_get_sgs_multiple_crds(self, m_get_crds, m_match_labels, - m_match_expressions): - m_match_expressions.return_value = True - m_match_labels.return_value = True - m_get_crds.return_value = self._multiple_crds - - resp = self._driver.get_security_groups(self._pod, self._project_id) - - m_get_crds.assert_called_once_with(namespace=self._namespace) - self.assertEqual([self._sg_id, self._sg_id2], resp) - - @mock.patch('kuryr_kubernetes.controller.drivers.utils.bump_networkpolicy') - @mock.patch('kuryr_kubernetes.controller.drivers.utils.' - 'get_kuryrnetworkpolicy_crds') - def test_delete_namespace_sg_rule(self, m_get_knp_crd, m_bump): - cls = network_policy_security_groups.NetworkPolicySecurityGroupsDriver - m_driver = mock.MagicMock(spec=cls) - - m_get_knp_crd.return_value = [get_matched_crd_obj()] - - cls.delete_namespace_sg_rules(m_driver, get_match_crd_namespace_obj()) - - m_get_knp_crd.assert_called_once() - m_bump.assert_called_once() - - @mock.patch('kuryr_kubernetes.controller.drivers.utils.bump_networkpolicy') - @mock.patch('kuryr_kubernetes.controller.drivers.utils.' - 'delete_security_group_rule') - @mock.patch('kuryr_kubernetes.controller.drivers.utils.' - 'get_kuryrnetworkpolicy_crds') - def test_delete_namespace_sg_rule_no_match( - self, m_get_knp_crd, m_delete_sg_rule, m_bump): - cls = network_policy_security_groups.NetworkPolicySecurityGroupsDriver - m_driver = mock.MagicMock(spec=cls) - - m_get_knp_crd.return_value = [get_matched_crd_obj()] - - cls.delete_namespace_sg_rules(m_driver, - get_no_match_crd_namespace_obj()) - - m_get_knp_crd.assert_called_once() - m_delete_sg_rule.assert_not_called() - m_bump.assert_not_called() - - @mock.patch('kuryr_kubernetes.controller.drivers.utils.get_pods') - @mock.patch('kuryr_kubernetes.controller.drivers.utils.match_selector') - def test__parse_rules(self, m_match_selector, m_get_pods): - crd = get_crd_obj_no_match() - policy = crd['spec']['networkpolicy_spec'] - i_rule = policy.get('ingress')[0] - ns_selector = i_rule['from'][0].get('namespaceSelector') - ns = get_match_crd_namespace_obj() - - m_match_selector.return_value = True - - matched = network_policy_security_groups._parse_rules( - 'ingress', crd, policy, namespace=ns) - - m_match_selector.assert_called_once_with(ns_selector, - ns['metadata']['labels']) - - self.assertEqual(matched, True) - - @mock.patch('kuryr_kubernetes.controller.drivers.utils.match_selector') - def test__parse_rules_no_match(self, m_match_selector): - crd = get_crd_obj_no_match() - policy = crd['spec']['networkpolicy_spec'] - i_rule = policy.get('ingress')[0] - ns_selector = i_rule['from'][0].get('namespaceSelector') - ns = get_no_match_crd_namespace_obj() - - m_match_selector.return_value = False - - matched = network_policy_security_groups._parse_rules( - 'ingress', crd, policy, namespace=ns) - - m_match_selector.assert_called_once_with(ns_selector, - ns['metadata']['labels']) - - self.assertEqual(matched, False) - - @mock.patch('kuryr_kubernetes.controller.drivers.utils.get_pods') - @mock.patch('kuryr_kubernetes.controller.drivers.utils.get_pod_ip') - @mock.patch('kuryr_kubernetes.controller.drivers.utils.match_selector') - def test__parse_rules_all_selectors(self, m_match_selector, m_get_pod_ip, - m_get_pods): - crd = get_crd_obj_with_all_selectors() - policy = crd['spec']['networkpolicy_spec'] - i_rule = policy.get('ingress')[0] - ns_selector = i_rule['from'][0].get('namespaceSelector') - pod_selector = i_rule['from'][0].get('podSelector') - ns = get_match_crd_namespace_obj() - pod = get_match_crd_pod_obj() - - m_match_selector.return_value = True - m_get_pod_ip.return_value = pod['status']['podIP'] - m_get_pods.return_value = {"items": [pod]} - - matched = network_policy_security_groups._parse_rules( - 'ingress', crd, policy, namespace=ns) - - m_match_selector.assert_called_once_with(ns_selector, - ns['metadata']['labels']) - m_get_pods.assert_called_once_with(pod_selector, - ns['metadata']['name']) - m_get_pod_ip.assert_called_once_with(pod) - - self.assertEqual(matched, True) - - @mock.patch('kuryr_kubernetes.controller.drivers.' - 'network_policy_security_groups._parse_selectors_on_pod') - def test__parse_rules_multiple_selectors(self, m_parse_selectors_on_pod): - no_selector = None - matched_selector = True - pod = mock.sentinel.pod - m_parse_selectors_on_pod.side_effect = [matched_selector]*2 - - direction = "ingress" - pod_selector = mock.sentinel.pod_selector - namespace_selector = mock.sentinel.namespace_selector - rule_block = {'from': [{'podSelector': pod_selector}, - {'namespaceSelector': namespace_selector}]} - policy = { - "ingress": [rule_block], - "policyTypes": ["Ingress"] - } - crd = {"spec": {"ingressSgRules": []}} - - matched = network_policy_security_groups._parse_rules( - direction, crd, policy, pod=pod) - - calls = [mock.call(crd, pod, pod_selector, no_selector, rule_block, - direction, not matched_selector), - mock.call(crd, pod, no_selector, namespace_selector, - rule_block, direction, matched_selector)] - m_parse_selectors_on_pod.assert_has_calls(calls) - - self.assertEqual(matched, matched_selector) diff --git a/kuryr_kubernetes/tests/unit/controller/drivers/test_neutron_vif.py b/kuryr_kubernetes/tests/unit/controller/drivers/test_neutron_vif.py deleted file mode 100644 index 81905cdd5..000000000 --- a/kuryr_kubernetes/tests/unit/controller/drivers/test_neutron_vif.py +++ /dev/null @@ -1,346 +0,0 @@ -# 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 eventlet -from unittest import mock - -from kuryr.lib import constants as kl_const -from openstack import exceptions as os_exc -from openstack.network.v2 import port as os_port -from oslo_config import cfg as oslo_cfg - -from kuryr_kubernetes import constants -from kuryr_kubernetes.controller.drivers import neutron_vif -from kuryr_kubernetes.controller.drivers import utils -from kuryr_kubernetes import exceptions as k_exc -from kuryr_kubernetes.tests import base as test_base -from kuryr_kubernetes.tests.unit import kuryr_fixtures as k_fix - - -class NeutronPodVIFDriver(test_base.TestCase): - - @mock.patch('kuryr_kubernetes.os_vif_util.neutron_to_osvif_vif') - def test_request_vif(self, m_to_vif): - cls = neutron_vif.NeutronPodVIFDriver - cls._tag_on_creation = True - m_driver = mock.Mock(spec=cls) - os_net = self.useFixture(k_fix.MockNetworkClient()).client - - pod = mock.sentinel.pod - project_id = mock.sentinel.project_id - subnets = mock.sentinel.subnets - security_groups = mock.sentinel.security_groups - port = os_port.Port(id='910b1183-1f4a-450a-a298-0e80ad06ec8b') - port_request = {'fake_req': mock.sentinel.port_request} - vif = mock.sentinel.vif - vif_plugin = mock.sentinel.vif_plugin - port.binding_vif_type = vif_plugin - - m_to_vif.return_value = vif - m_driver._get_port_request.return_value = port_request - os_net.create_port.return_value = port - - self.assertEqual(vif, cls.request_vif(m_driver, pod, project_id, - subnets, security_groups)) - - m_driver._get_port_request.assert_called_once_with( - pod, project_id, subnets, security_groups) - os_net.create_port.assert_called_once_with(**port_request) - m_to_vif.assert_called_once_with(vif_plugin, port, subnets) - - @mock.patch('kuryr_kubernetes.os_vif_util.neutron_to_osvif_vif') - def test_request_vifs(self, m_to_vif): - cls = neutron_vif.NeutronPodVIFDriver - cls._tag_on_creation = True - m_driver = mock.Mock(spec=cls) - os_net = self.useFixture(k_fix.MockNetworkClient()).client - - pod = mock.sentinel.pod - project_id = mock.sentinel.project_id - subnets = mock.sentinel.subnets - security_groups = mock.sentinel.security_groups - num_ports = 2 - - port_request = mock.sentinel.port_request - m_driver._get_port_request.return_value = port_request - port = os_port.Port(id='910b1183-1f4a-450a-a298-0e80ad06ec8b') - vif_plugin = mock.sentinel.vif_plugin - port.binding_vif_type = vif_plugin - vif = mock.sentinel.vif - bulk_rq = [port_request for _ in range(num_ports)] - - os_net.create_ports.return_value = (p for p in [port, port]) - m_to_vif.return_value = vif - semaphore = mock.MagicMock(spec=eventlet.semaphore.Semaphore(20)) - - self.assertEqual([vif, vif], cls.request_vifs( - m_driver, pod, project_id, subnets, security_groups, num_ports, - semaphore)) - - m_driver._get_port_request.assert_called_once_with( - pod, project_id, subnets, security_groups, unbound=True) - os_net.create_ports.assert_called_once_with(bulk_rq) - calls = [mock.call(vif_plugin, port, subnets), - mock.call(vif_plugin, port, subnets)] - m_to_vif.assert_has_calls(calls) - - @mock.patch('kuryr_kubernetes.os_vif_util.neutron_to_osvif_vif') - def test_request_vifs_unbound(self, m_to_vif): - cls = neutron_vif.NeutronPodVIFDriver - cls._tag_on_creation = True - m_driver = mock.Mock(spec=cls) - os_net = self.useFixture(k_fix.MockNetworkClient()).client - - pod = mock.sentinel.pod - project_id = mock.sentinel.project_id - subnets = mock.sentinel.subnets - security_groups = mock.sentinel.security_groups - num_ports = 2 - - port_request = mock.sentinel.port_request - m_driver._get_port_request.return_value = port_request - port_id = mock.sentinel.port_id - port1 = os_port.Port(id=port_id, binding_vif_type='unbound') - vif_plugin = mock.sentinel.vif_plugin - port2 = os_port.Port(id=port_id, binding_vif_type=vif_plugin) - port1_1 = os_port.Port(id=port_id, binding_vif_type=vif_plugin) - vif = mock.sentinel.vif - bulk_rq = [port_request for _ in range(num_ports)] - semaphore = mock.MagicMock(spec=eventlet.semaphore.Semaphore(20)) - - os_net.create_ports.return_value = (p for p in [port1, port2]) - os_net.get_port.return_value = port1_1 - m_to_vif.return_value = vif - - self.assertEqual([vif, vif], cls.request_vifs( - m_driver, pod, project_id, subnets, security_groups, num_ports, - semaphore)) - - m_driver._get_port_request.assert_called_once_with( - pod, project_id, subnets, security_groups, unbound=True) - os_net.create_ports.assert_called_once_with(bulk_rq) - os_net.get_port.assert_called_once_with(port_id) - calls = [mock.call(vif_plugin, port1, subnets), - mock.call(vif_plugin, port2, subnets)] - m_to_vif.assert_has_calls(calls) - - @mock.patch('kuryr_kubernetes.os_vif_util.neutron_to_osvif_vif') - def test_request_vifs_exception(self, m_to_vif): - cls = neutron_vif.NeutronPodVIFDriver - cls._tag_on_creation = False - m_driver = mock.Mock(spec=cls) - os_net = self.useFixture(k_fix.MockNetworkClient()).client - - pod = mock.sentinel.pod - project_id = mock.sentinel.project_id - subnets = mock.sentinel.subnets - security_groups = mock.sentinel.security_groups - num_ports = 2 - - port_request = mock.sentinel.port_request - m_driver._get_port_request.return_value = port_request - bulk_rq = [port_request for _ in range(num_ports)] - - semaphore = mock.MagicMock(spec=eventlet.semaphore.Semaphore(20)) - os_net.create_ports.side_effect = os_exc.SDKException - - self.assertRaises(os_exc.SDKException, cls.request_vifs, - m_driver, pod, project_id, subnets, - security_groups, num_ports, semaphore) - - m_driver._get_port_request.assert_called_once_with( - pod, project_id, subnets, security_groups, unbound=True) - os_net.create_ports.assert_called_once_with(bulk_rq) - m_to_vif.assert_not_called() - - def test_release_vif(self): - cls = neutron_vif.NeutronPodVIFDriver - m_driver = mock.Mock(spec=cls) - os_net = self.useFixture(k_fix.MockNetworkClient()).client - - pod = mock.sentinel.pod - vif = mock.Mock() - - cls.release_vif(m_driver, pod, vif) - - os_net.delete_port.assert_called_once_with(vif.id) - - def test_release_vif_not_found(self): - cls = neutron_vif.NeutronPodVIFDriver - m_driver = mock.Mock(spec=cls) - os_net = self.useFixture(k_fix.MockNetworkClient()).client - - pod = mock.sentinel.pod - vif = mock.Mock() - - cls.release_vif(m_driver, pod, vif) - - os_net.delete_port.assert_called_once_with(vif.id) - - def test_activate_vif(self): - cls = neutron_vif.NeutronPodVIFDriver - m_driver = mock.Mock(spec=cls) - os_net = self.useFixture(k_fix.MockNetworkClient()).client - - vif = mock.Mock() - vif.active = False - port = mock.MagicMock() - - port.__getitem__.return_value = kl_const.PORT_STATUS_ACTIVE - os_net.get_port.return_value = port - - cls.activate_vif(m_driver, vif) - - os_net.get_port.assert_called_once_with(vif.id) - self.assertTrue(vif.active) - - def test_activate_vif_active(self): - cls = neutron_vif.NeutronPodVIFDriver - m_driver = mock.Mock(spec=cls) - os_net = self.useFixture(k_fix.MockNetworkClient()).client - - vif = mock.Mock() - vif.active = True - - cls.activate_vif(m_driver, vif) - - os_net.get_port.assert_not_called() - - def test_activate_vif_not_ready(self): - cls = neutron_vif.NeutronPodVIFDriver - m_driver = mock.Mock(spec=cls) - os_net = self.useFixture(k_fix.MockNetworkClient()).client - - vif = mock.Mock() - vif.active = False - port = mock.MagicMock() - - port.__getitem__.return_value = kl_const.PORT_STATUS_DOWN - os_net.get_port.return_value = port - - self.assertRaises(k_exc.ResourceNotReady, cls.activate_vif, - m_driver, vif) - - def _test_get_port_request(self, m_to_fips, security_groups, - m_get_device_id, m_get_port_name, m_get_host_id, - m_get_network_id, unbound=False): - cls = neutron_vif.NeutronPodVIFDriver - cls._tag_on_creation = True - m_driver = mock.Mock(spec=cls) - - pod = mock.sentinel.pod - project_id = mock.sentinel.project_id - subnets = mock.sentinel.subnets - port_name = mock.sentinel.port_name - network_id = mock.sentinel.network_id - fixed_ips = mock.sentinel.fixed_ips - device_id = mock.sentinel.device_id - host_id = mock.sentinel.host_id - - m_get_port_name.return_value = port_name - m_get_network_id.return_value = network_id - m_to_fips.return_value = fixed_ips - m_get_device_id.return_value = device_id - m_get_host_id.return_value = host_id - - oslo_cfg.CONF.set_override('port_debug', - True, - group='kubernetes') - - expected = {'project_id': project_id, - 'name': port_name, - 'network_id': network_id, - 'fixed_ips': fixed_ips, - 'device_owner': kl_const.DEVICE_OWNER, - 'admin_state_up': True, - 'binding_host_id': host_id} - - if security_groups: - expected['security_groups'] = security_groups - - tags = oslo_cfg.CONF.neutron_defaults.resource_tags - if cls._tag_on_creation and tags: - expected['tags'] = tags - - if unbound: - expected['name'] = constants.KURYR_PORT_NAME - else: - expected['device_id'] = device_id - - ret = cls._get_port_request(m_driver, pod, project_id, subnets, - security_groups, unbound) - - self.assertEqual(expected, ret) - m_get_network_id.assert_called_once_with(subnets) - m_to_fips.assert_called_once_with(subnets) - if not unbound: - m_get_port_name.assert_called_once_with(pod) - m_get_device_id.assert_called_once_with(pod) - m_get_host_id.assert_called_once_with(pod) - - @mock.patch('kuryr_kubernetes.os_vif_util.osvif_to_neutron_fixed_ips') - @mock.patch('kuryr_kubernetes.controller.drivers.utils.get_device_id') - @mock.patch('kuryr_kubernetes.controller.drivers.utils.get_port_name') - @mock.patch('kuryr_kubernetes.controller.drivers.utils.get_host_id') - @mock.patch('kuryr_kubernetes.controller.drivers.utils.get_network_id') - def test_get_port_request(self, m_get_network_id, m_get_host_id, - m_get_port_name, m_get_dev_id, m_to_fips): - security_groups = mock.sentinel.security_groups - self._test_get_port_request(m_to_fips, security_groups, m_get_dev_id, - m_get_port_name, m_get_host_id, - m_get_network_id) - - @mock.patch('kuryr_kubernetes.os_vif_util.osvif_to_neutron_fixed_ips') - @mock.patch('kuryr_kubernetes.controller.drivers.utils.get_device_id') - @mock.patch('kuryr_kubernetes.controller.drivers.utils.get_port_name') - @mock.patch('kuryr_kubernetes.controller.drivers.utils.get_host_id') - @mock.patch('kuryr_kubernetes.controller.drivers.utils.get_network_id') - def test_get_port_request_no_sg(self, m_get_network_id, m_get_host_id, - m_get_port_name, m_get_dev_id, m_to_fips): - security_groups = [] - self._test_get_port_request(m_to_fips, security_groups, m_get_dev_id, - m_get_port_name, m_get_host_id, - m_get_network_id) - - @mock.patch('kuryr_kubernetes.os_vif_util.osvif_to_neutron_fixed_ips') - @mock.patch('kuryr_kubernetes.controller.drivers.utils.get_device_id') - @mock.patch('kuryr_kubernetes.controller.drivers.utils.get_port_name') - @mock.patch('kuryr_kubernetes.controller.drivers.utils.get_host_id') - @mock.patch('kuryr_kubernetes.controller.drivers.utils.get_network_id') - def test_get_port_request_unbound(self, m_get_network_id, m_get_host_id, - m_get_port_name, m_get_dev_id, - m_to_fips): - security_groups = mock.sentinel.security_groups - self._test_get_port_request(m_to_fips, security_groups, m_get_dev_id, - m_get_port_name, m_get_host_id, - m_get_network_id, unbound=True) - - def test_get_port_name(self): - pod_name = mock.sentinel.pod_name - port_name = 'default/' + str(pod_name) - pod = {'metadata': {'name': pod_name, 'namespace': 'default'}} - - self.assertEqual(port_name, utils.get_port_name(pod)) - - def test_get_device_id(self): - pod_uid = mock.sentinel.pod_uid - pod = {'metadata': {'uid': pod_uid}} - - self.assertEqual(pod_uid, utils.get_device_id(pod)) - - def test_get_host_id(self): - node = mock.sentinel.pod_uid - pod = {'spec': {'nodeName': node}} - - self.assertEqual(node, utils.get_host_id(pod)) diff --git a/kuryr_kubernetes/tests/unit/controller/drivers/test_node_subnets.py b/kuryr_kubernetes/tests/unit/controller/drivers/test_node_subnets.py deleted file mode 100644 index 2b6fb567a..000000000 --- a/kuryr_kubernetes/tests/unit/controller/drivers/test_node_subnets.py +++ /dev/null @@ -1,418 +0,0 @@ -# Copyright 2020 Red Hat, 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. - -from unittest import mock - -from openstack import exceptions as os_exc -from openstack.network.v2 import subnet as os_subnet -from oslo_config import cfg - -from kuryr_kubernetes.controller.drivers import node_subnets -from kuryr_kubernetes import exceptions -from kuryr_kubernetes.tests import base as test_base - - -class TestConfigNodesSubnetsDriver(test_base.TestCase): - - def test_get_nodes_subnets(self): - subnets = ['subnet1', 'subnet2'] - cfg.CONF.set_override('worker_nodes_subnets', subnets, - group='pod_vif_nested') - driver = node_subnets.ConfigNodesSubnets() - - self.assertEqual(subnets, driver.get_nodes_subnets()) - - def test_get_project_not_set_raise(self): - cfg.CONF.set_override('worker_nodes_subnets', None, - group='pod_vif_nested') - driver = node_subnets.ConfigNodesSubnets() - - self.assertRaises(cfg.RequiredOptError, driver.get_nodes_subnets, - raise_on_empty=True) - - def test_get_project_not_set(self): - cfg.CONF.set_override('worker_nodes_subnets', None, - group='pod_vif_nested') - driver = node_subnets.ConfigNodesSubnets() - - self.assertEqual([], driver.get_nodes_subnets()) - - def test_add_node(self): - driver = node_subnets.ConfigNodesSubnets() - self.assertFalse(driver.add_node('node')) - - def test_delete_node(self): - driver = node_subnets.ConfigNodesSubnets() - self.assertFalse(driver.delete_node('node')) - - -class TestOpenShiftNodesSubnetsDriver(test_base.TestCase): - def setUp(self): - super().setUp() - self.machine = { - "apiVersion": "machine.openshift.io/v1beta1", - "kind": "Machine", - "metadata": { - "name": "foo-tv22d-master-2", - "namespace": "openshift-machine-api", - }, - "spec": { - "metadata": {}, - "providerSpec": { - "value": { - "cloudName": "openstack", - "cloudsSecret": { - "name": "openstack-cloud-credentials", - "namespace": "openshift-machine-api" - }, - "kind": "OpenstackProviderSpec", - "networks": [ - { - "filter": {}, - "subnets": [{ - "filter": { - "name": "foo-tv22d-nodes", - "tags": "openshiftClusterID=foo-tv22d" - }} - ] - } - ], - "trunk": True - } - } - }, - "status": {} - } - cfg.CONF.set_override('worker_nodes_subnets', [], - group='pod_vif_nested') - - def test_get_nodes_subnets(self): - subnets = ['subnet1', 'subnet2'] - driver = node_subnets.OpenShiftNodesSubnets() - for subnet in subnets: - driver.subnets.add(subnet) - self.assertCountEqual(subnets, driver.get_nodes_subnets()) - - def test_get_nodes_subnets_with_config(self): - subnets = ['subnet1', 'subnet2'] - cfg.CONF.set_override('worker_nodes_subnets', ['subnet3', 'subnet2'], - group='pod_vif_nested') - driver = node_subnets.OpenShiftNodesSubnets() - for subnet in subnets: - driver.subnets.add(subnet) - self.assertCountEqual(['subnet1', 'subnet2', 'subnet3'], - driver.get_nodes_subnets()) - - def test_get_nodes_subnets_not_raise(self): - driver = node_subnets.OpenShiftNodesSubnets() - self.assertEqual([], driver.get_nodes_subnets()) - - def test_get_nodes_subnets_raise(self): - driver = node_subnets.OpenShiftNodesSubnets() - self.assertRaises(exceptions.ResourceNotReady, - driver.get_nodes_subnets, raise_on_empty=True) - - @mock.patch('kuryr_kubernetes.clients.get_kubernetes_client') - @mock.patch('kuryr_kubernetes.utils.get_subnet_id') - def test_add_node(self, m_get_subnet_id, m_get_k8s): - driver = node_subnets.OpenShiftNodesSubnets() - m_get_subnet_id.return_value = 'foobar' - self.assertTrue(driver.add_node(self.machine)) - m_get_subnet_id.assert_called_once_with( - name='foo-tv22d-nodes', tags='openshiftClusterID=foo-tv22d') - self.assertEqual(['foobar'], driver.get_nodes_subnets()) - - @mock.patch('kuryr_kubernetes.clients.get_kubernetes_client') - @mock.patch('kuryr_kubernetes.utils.get_subnet_id') - def test_add_node_exists(self, m_get_subnet_id, m_get_k8s): - driver = node_subnets.OpenShiftNodesSubnets() - m_get_subnet_id.return_value = 'foobar' - driver.subnets.add('foobar') - self.assertFalse(driver.add_node(self.machine)) - m_get_subnet_id.assert_called_once_with( - name='foo-tv22d-nodes', tags='openshiftClusterID=foo-tv22d') - self.assertEqual(['foobar'], driver.get_nodes_subnets()) - - @mock.patch('kuryr_kubernetes.clients.get_kubernetes_client') - @mock.patch('kuryr_kubernetes.utils.get_subnet_id') - def test_add_node_uuid(self, m_get_subnet_id, m_get_k8s): - driver = node_subnets.OpenShiftNodesSubnets() - net = self.machine['spec']['providerSpec']['value']['networks'][0] - del net['subnets'][0]['filter'] - net['subnets'][0]['uuid'] = 'barfoo' - self.assertTrue(driver.add_node(self.machine)) - m_get_subnet_id.assert_not_called() - self.assertEqual(['barfoo'], driver.get_nodes_subnets()) - - @mock.patch('kuryr_kubernetes.clients.get_kubernetes_client') - @mock.patch('kuryr_kubernetes.utils.get_subnet_id') - def test_add_node_cannot(self, m_get_subnet_id, m_get_k8s): - driver = node_subnets.OpenShiftNodesSubnets() - net = self.machine['spec']['providerSpec']['value']['networks'][0] - del net['subnets'] - self.assertFalse(driver.add_node(self.machine)) - m_get_subnet_id.assert_not_called() - self.assertEqual([], driver.get_nodes_subnets()) - - @mock.patch('kuryr_kubernetes.utils.get_subnet_id') - @mock.patch('kuryr_kubernetes.clients.get_kubernetes_client') - def test_delete_node_cannot(self, m_get_k8s, m_get_subnet_id): - m_k8s = mock.Mock() - m_get_k8s.return_value = m_k8s - driver = node_subnets.OpenShiftNodesSubnets() - net = self.machine['spec']['providerSpec']['value']['networks'][0] - del net['subnets'] - self.assertFalse(driver.delete_node(self.machine)) - m_get_subnet_id.assert_not_called() - self.assertEqual([], driver.get_nodes_subnets()) - - @mock.patch('kuryr_kubernetes.utils.get_subnet_id') - @mock.patch('kuryr_kubernetes.clients.get_kubernetes_client') - def test_delete_node(self, m_get_k8s, m_get_subnet_id): - m_k8s = mock.Mock() - m_get_k8s.return_value = m_k8s - m_k8s.get.return_value = {'items': []} - - driver = node_subnets.OpenShiftNodesSubnets() - driver.subnets.add('foobar') - m_get_subnet_id.return_value = 'foobar' - self.assertTrue(driver.delete_node(self.machine)) - m_get_subnet_id.assert_called_once_with( - name='foo-tv22d-nodes', tags='openshiftClusterID=foo-tv22d') - self.assertEqual([], driver.get_nodes_subnets()) - - @mock.patch('kuryr_kubernetes.utils.get_subnet_id') - @mock.patch('kuryr_kubernetes.clients.get_kubernetes_client') - def test_delete_node_still_exists(self, m_get_k8s, m_get_subnet_id): - m_k8s = mock.Mock() - m_get_k8s.return_value = m_k8s - m_k8s.get.return_value = {'items': [self.machine]} - - driver = node_subnets.OpenShiftNodesSubnets() - driver.subnets.add('foobar') - m_get_subnet_id.return_value = 'foobar' - self.assertFalse(driver.delete_node(self.machine)) - m_get_subnet_id.assert_called_with( - name='foo-tv22d-nodes', tags='openshiftClusterID=foo-tv22d') - self.assertEqual(['foobar'], driver.get_nodes_subnets()) - - @mock.patch('kuryr_kubernetes.clients.get_kubernetes_client') - def test_get_subnet_from_machine_no_networks(self, m_get_k8s): - driver = node_subnets.OpenShiftNodesSubnets() - del self.machine['spec']['providerSpec']['value']['networks'] - - self.assertIsNone(driver._get_subnet_from_machine(self.machine)) - - @mock.patch('kuryr_kubernetes.clients.get_kubernetes_client') - @mock.patch('kuryr_kubernetes.utils.get_subnet_id') - def test_get_subnet_from_machine_networks_subnets(self, m_get_subnet_id, - m_get_k8s): - subnetid = 'd467451b-ab28-4578-882f-347f0dff4c9a' - m_get_subnet_id.return_value = subnetid - driver = node_subnets.OpenShiftNodesSubnets() - - self.assertEqual(subnetid, - driver._get_subnet_from_machine(self.machine)) - - @mock.patch('kuryr_kubernetes.clients.get_kubernetes_client') - def test_get_subnet_from_machine_networks_wo_filters(self, m_get_k8s): - driver = node_subnets.OpenShiftNodesSubnets() - nets = self.machine['spec']['providerSpec']['value']['networks'] - nets[0]['subnets'] = [{'uuid': 'f8a458e5-c280-47b7-9c8a-dbd4ecd65545'}] - self.machine['spec']['providerSpec']['value']['networks'] = nets - - result = driver._get_subnet_from_machine(self.machine) - - self.assertEqual(nets[0]['subnets'][0]['uuid'], result) - - @mock.patch('kuryr_kubernetes.clients.get_kubernetes_client') - @mock.patch('kuryr_kubernetes.clients.get_network_client') - def test_get_subnet_from_machine_primary_subnet(self, m_get_net, - m_get_k8s): - driver = node_subnets.OpenShiftNodesSubnets() - psub = '622c5fd4-804c-40e8-95ab-ecd1565ac8e2' - m_net = mock.Mock() - m_net.find_subnet.return_value = os_subnet.Subnet(id=psub) - m_get_net.return_value = m_net - self.machine['spec']['providerSpec']['value']['primarySubnet'] = psub - - result = driver._get_subnet_from_machine(self.machine) - - self.assertEqual(psub, result) - - @mock.patch('kuryr_kubernetes.clients.get_kubernetes_client') - def test_get_subnet_from_machine_ports(self, m_get_k8s): - driver = node_subnets.OpenShiftNodesSubnets() - subnet_id = '0530f763-899b-4acb-a2ca-deeedd760409' - ports = [{'fixedIPs': [{'subnetID': subnet_id}]}] - self.machine['spec']['providerSpec']['value']['ports'] = ports - del self.machine['spec']['providerSpec']['value']['networks'] - - result = driver._get_subnet_from_machine(self.machine) - - self.assertEqual(subnet_id, result) - - @mock.patch('kuryr_kubernetes.clients.get_kubernetes_client') - @mock.patch('kuryr_kubernetes.utils.get_subnet_id') - def test_get_subnet_from_machine_networks_and_ports(self, m_get_subnet_id, - m_get_k8s): - """Test both: networks and ports presence, but no primarySubnet. - - Precedence would have networks over ports. - """ - subnet_id = '7607a620-b706-478f-9481-7fdf11deeab2' - m_get_subnet_id.return_value = subnet_id - port_subnet_id = 'ec4c50ac-e3f6-426e-ad91-6ddc10b5c391' - ports = [{'fixedIPs': [{'subnetID': port_subnet_id}]}] - self.machine['spec']['providerSpec']['value']['ports'] = ports - driver = node_subnets.OpenShiftNodesSubnets() - - result = driver._get_subnet_from_machine(self.machine) - - self.assertEqual(subnet_id, result) - - @mock.patch('kuryr_kubernetes.clients.get_kubernetes_client') - def test_get_subnet_from_machine_empty_networks(self, m_get_k8s): - """Test both: networks and ports presence, but no primarySubnet. - - Precedence would have networks over ports. - """ - self.machine['spec']['providerSpec']['value']['networks'] = [] - driver = node_subnets.OpenShiftNodesSubnets() - - result = driver._get_subnet_from_machine(self.machine) - - self.assertIsNone(result) - - @mock.patch('kuryr_kubernetes.clients.get_kubernetes_client') - def test_get_subnet_from_machine_empty_ports(self, m_get_k8s): - """Test both: networks and ports presence, but no primarySubnet. - - Precedence would have networks over ports. - """ - del self.machine['spec']['providerSpec']['value']['networks'] - self.machine['spec']['providerSpec']['value']['ports'] = [] - driver = node_subnets.OpenShiftNodesSubnets() - - result = driver._get_subnet_from_machine(self.machine) - - self.assertIsNone(result) - - @mock.patch('kuryr_kubernetes.clients.get_kubernetes_client') - def test_get_subnet_from_machine_networks_no_trunk(self, m_get_k8s): - del self.machine['spec']['providerSpec']['value']['trunk'] - driver = node_subnets.OpenShiftNodesSubnets() - - self.assertIsNone(driver._get_subnet_from_machine(self.machine)) - - @mock.patch('kuryr_kubernetes.clients.get_kubernetes_client') - def test_get_subnet_from_machine_ports_no_trunk(self, m_get_k8s): - del self.machine['spec']['providerSpec']['value']['trunk'] - del self.machine['spec']['providerSpec']['value']['networks'] - subnet_id = '0530f763-899b-4acb-a2ca-deeedd760409' - ports = [{'fixedIPs': [{'subnetID': subnet_id}]}] - self.machine['spec']['providerSpec']['value']['ports'] = ports - driver = node_subnets.OpenShiftNodesSubnets() - - result = driver._get_subnet_from_machine(self.machine) - - self.assertIsNone(result) - - @mock.patch('kuryr_kubernetes.clients.get_kubernetes_client') - def test_get_subnet_from_machine_ports_no_trunk_one_with_trunk(self, - m_get_k8s): - del self.machine['spec']['providerSpec']['value']['trunk'] - del self.machine['spec']['providerSpec']['value']['networks'] - subnet_id = '0530f763-899b-4acb-a2ca-deeedd760409' - ports = [{'fixedIPs': [{'subnetID': 'foo'}]}, - {'fixedIPs': [{'subnetID': subnet_id}], 'trunk': True}] - self.machine['spec']['providerSpec']['value']['ports'] = ports - driver = node_subnets.OpenShiftNodesSubnets() - - result = driver._get_subnet_from_machine(self.machine) - - self.assertEqual(subnet_id, result) - - @mock.patch('kuryr_kubernetes.clients.get_kubernetes_client') - def test_get_subnet_from_machine_ports_both_with_trunk(self, m_get_k8s): - del self.machine['spec']['providerSpec']['value']['networks'] - subnet_id1 = '0530f763-899b-4acb-a2ca-deeedd760409' - subnet_id2 = 'ccfe75a8-c15e-4504-9596-02e397362abf' - ports = [{'fixedIPs': [{'subnetID': subnet_id1}], 'trunk': False}, - {'fixedIPs': [{'subnetID': subnet_id2}], 'trunk': True}] - self.machine['spec']['providerSpec']['value']['ports'] = ports - driver = node_subnets.OpenShiftNodesSubnets() - - result = driver._get_subnet_from_machine(self.machine) - - self.assertEqual(subnet_id2, result) - - @mock.patch('kuryr_kubernetes.clients.get_kubernetes_client') - def test_get_subnet_from_machine_ports_both_wrong(self, m_get_k8s): - del self.machine['spec']['providerSpec']['value']['networks'] - ports = [{'trunk': True}, - {'fixedIPs': [{'foo': 'bar'}], 'trunk': True}] - self.machine['spec']['providerSpec']['value']['ports'] = ports - driver = node_subnets.OpenShiftNodesSubnets() - - result = driver._get_subnet_from_machine(self.machine) - - self.assertIsNone(result) - - @mock.patch('kuryr_kubernetes.clients.get_kubernetes_client') - @mock.patch('kuryr_kubernetes.clients.get_network_client') - def test_get_subnet_from_machine_two_primary_subnet(self, m_get_net, - m_get_k8s): - driver = node_subnets.OpenShiftNodesSubnets() - sname = 'multiple subnets with the same name' - m_net = mock.Mock() - m_net.find_subnet.side_effect = os_exc.DuplicateResource - m_get_net.return_value = m_net - self.machine['spec']['providerSpec']['value']['primarySubnet'] = sname - - result = driver._get_subnet_from_machine(self.machine) - - self.assertIsNone(result) - - @mock.patch('kuryr_kubernetes.clients.get_kubernetes_client') - @mock.patch('kuryr_kubernetes.clients.get_network_client') - def test_get_subnet_from_machine_single_named_primary_subnet(self, - m_get_net, - m_get_k8s): - driver = node_subnets.OpenShiftNodesSubnets() - sname = 'single named subnet' - subnet_id = '9bcf85c8-1f15-4e3d-8e1e-0e2270ffd2b9' - m_net = mock.Mock() - m_net.find_subnet.return_value = os_subnet.Subnet(id=subnet_id) - m_get_net.return_value = m_net - self.machine['spec']['providerSpec']['value']['primarySubnet'] = sname - - result = driver._get_subnet_from_machine(self.machine) - - self.assertEqual(subnet_id, result) - - @mock.patch('kuryr_kubernetes.clients.get_kubernetes_client') - @mock.patch('kuryr_kubernetes.clients.get_network_client') - def test_get_subnet_from_machine_primary_subnet_exc(self, m_get_net, - m_get_k8s): - driver = node_subnets.OpenShiftNodesSubnets() - subnet = 'e621f2f5-38a4-4a9c-873f-1d447290939c' - m_net = mock.Mock() - m_net.find_subnet.side_effect = os_exc.SDKException - m_get_net.return_value = m_net - self.machine['spec']['providerSpec']['value']['primarySubnet'] = subnet - - self.assertRaises(exceptions.ResourceNotReady, - driver._get_subnet_from_machine, self.machine) diff --git a/kuryr_kubernetes/tests/unit/controller/drivers/test_public_ip.py b/kuryr_kubernetes/tests/unit/controller/drivers/test_public_ip.py deleted file mode 100644 index 350dd43aa..000000000 --- a/kuryr_kubernetes/tests/unit/controller/drivers/test_public_ip.py +++ /dev/null @@ -1,170 +0,0 @@ -# Copyright (c) 2017 RedHat, 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. - -from openstack import exceptions as os_exc -from openstack.network.v2 import floating_ip as os_fip -from unittest import mock - -from kuryr_kubernetes.controller.drivers import public_ip\ - as d_public_ip -from kuryr_kubernetes.tests import base as test_base -from kuryr_kubernetes.tests.unit import kuryr_fixtures as k_fix - - -class TestFipPubIpDriver(test_base.TestCase): - def setUp(self): - super(TestFipPubIpDriver, self).setUp() - self.driver = d_public_ip.FipPubIpDriver() - self.os_net = self.useFixture(k_fix.MockNetworkClient()).client - - def test_is_ip_available_none_param(self): - fip_id = self.driver.is_ip_available(None) - self.assertIsNone(fip_id) - - def test_is_ip_available_ip_not_exist(self): - fip = os_fip.FloatingIP( - floating_ip_address='1.2.3.4', - port_id=None, - id='a2a62ea7-e3bf-40df-8c09-aa0c29876a6b', - ) - self.os_net.ips.return_value = (ip for ip in [fip]) - - fip_ip_addr = '1.1.1.1' - fip_id = self.driver.is_ip_available(fip_ip_addr) - self.assertIsNone(fip_id) - - def test_is_ip_available_empty_fip_list(self): - self.os_net.ips.return_value = (ip for ip in []) - - fip_ip_addr = '1.1.1.1' - fip_id = self.driver.is_ip_available(fip_ip_addr) - self.assertIsNone(fip_id) - - def test_is_ip_available_occupied_fip(self): - fip = os_fip.FloatingIP( - floating_ip_address='1.2.3.4', - port_id='ec29d641-fec4-4f67-928a-124a76b3a8e6', - ) - self.os_net.ips.return_value = (ip for ip in [fip]) - fip_ip_addr = '1.2.3.4' - fip_id = self.driver.is_ip_available(fip_ip_addr) - self.assertIsNone(fip_id) - - def test_is_ip_available_ip_exist_and_available(self): - fip = os_fip.FloatingIP( - floating_ip_address='1.2.3.4', - port_id=None, - id='a2a62ea7-e3bf-40df-8c09-aa0c29876a6b', - ) - self.os_net.ips.return_value = (ip for ip in [fip]) - - fip_ip_addr = '1.2.3.4' - fip_id = self.driver.is_ip_available(fip_ip_addr) - self.assertEqual(fip_id, 'a2a62ea7-e3bf-40df-8c09-aa0c29876a6b') - - def test_allocate_ip_all_green(self): - pub_net_id = mock.sentinel.pub_net_id - pub_subnet_id = mock.sentinel.pub_subnet_id - project_id = mock.sentinel.project_id - description = mock.sentinel.description - - fip = os_fip.FloatingIP( - floating_ip_address='1.2.3.5', - id='ec29d641-fec4-4f67-928a-124a76b3a888', - ) - self.os_net.create_ip.return_value = fip - - fip_id, fip_addr = self.driver.allocate_ip(pub_net_id, project_id, - pub_subnet_id, description) - self.assertEqual(fip_id, fip.id) - self.assertEqual(fip_addr, fip.floating_ip_address) - - def test_allocate_ip_neutron_exception(self): - pub_net_id = mock.sentinel.pub_net_id - pub_subnet_id = mock.sentinel.pub_subnet_id - project_id = mock.sentinel.project_id - description = mock.sentinel.description - - self.os_net.create_ip.side_effect = os_exc.SDKException - - self.assertRaises(os_exc.SDKException, self.driver.allocate_ip, - pub_net_id, project_id, pub_subnet_id, description) - - def test_free_ip_neutron_exception(self): - res_id = mock.sentinel.res_id - - self.os_net.delete_ip.side_effect = os_exc.SDKException - rc = self.driver.free_ip(res_id) - self.assertIs(rc, False) - - def test_free_ip_succeeded(self): - res_id = mock.sentinel.res_id - - rc = self.driver.free_ip(res_id) - self.assertIs(rc, True) - - def test_associate_neutron_exception(self): - res_id = mock.sentinel.res_id - vip_port_id = mock.sentinel.vip_port_id - - uf = self.os_net.update_ip - uf.side_effect = os_exc.SDKException - self.assertRaises(os_exc.SDKException, self.driver.associate, - res_id, vip_port_id) - - def test_associate_conflict_correct(self): - driver = d_public_ip.FipPubIpDriver() - res_id = mock.sentinel.res_id - vip_port_id = mock.sentinel.vip_port_id - - os_net = self.useFixture(k_fix.MockNetworkClient()).client - os_net.update_ip.side_effect = os_exc.ConflictException - os_net.get_ip.return_value = os_fip.FloatingIP( - id=res_id, port_id=vip_port_id, - ) - self.assertIsNone(driver.associate(res_id, vip_port_id)) - - def test_associate_conflict_incorrect(self): - driver = d_public_ip.FipPubIpDriver() - res_id = mock.sentinel.res_id - vip_port_id = mock.sentinel.vip_port_id - - os_net = self.useFixture(k_fix.MockNetworkClient()).client - os_net.update_ip.side_effect = os_exc.ConflictException - os_net.get_ip.return_value = os_fip.FloatingIP( - id=res_id, port_id='foo', - ) - self.assertRaises(os_exc.ConflictException, driver.associate, res_id, - vip_port_id) - - def test_associate_succeeded(self): - res_id = mock.sentinel.res_id - vip_port_id = mock.sentinel.vip_port_id - - retcode = self.driver.associate(res_id, vip_port_id) - self.assertIsNone(retcode) - - def test_disassociate_neutron_exception(self): - res_id = mock.sentinel.res_id - - uf = self.os_net.update_ip - uf.side_effect = os_exc.SDKException - self.assertRaises(os_exc.SDKException, - self.driver.disassociate, res_id) - - def test_disassociate_succeeded(self): - res_id = mock.sentinel.res_id - - self.assertIsNone(self.driver.disassociate(res_id)) diff --git a/kuryr_kubernetes/tests/unit/controller/drivers/test_utils.py b/kuryr_kubernetes/tests/unit/controller/drivers/test_utils.py deleted file mode 100644 index bc7091d43..000000000 --- a/kuryr_kubernetes/tests/unit/controller/drivers/test_utils.py +++ /dev/null @@ -1,172 +0,0 @@ -# Copyright 2019 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 unittest import mock - -from oslo_config import cfg - -from kuryr_kubernetes import constants -from kuryr_kubernetes.controller.drivers import utils -from kuryr_kubernetes import exceptions -from kuryr_kubernetes.tests import base as test_base -from kuryr_kubernetes.tests.unit import kuryr_fixtures as k_fix - -CONF = cfg.CONF - - -class TestUtils(test_base.TestCase): - - def test_get_namespace_not_found(self): - namespace_name = mock.sentinel.namespace_name - kubernetes = self.useFixture(k_fix.MockK8sClient()).client - kubernetes.get.side_effect = exceptions.K8sResourceNotFound( - mock.sentinel.resource) - - resp = utils.get_namespace(namespace_name) - - self.assertIsNone(resp) - kubernetes.get.assert_called_once_with('{}/namespaces/{}'.format( - constants.K8S_API_BASE, namespace_name)) - - def test_get_network_id(self): - id_a = mock.sentinel.id_a - net1 = mock.Mock() - net1.id = id_a - net2 = mock.Mock() - net2.id = id_a - subnets = {1: net1, 2: net2} - - ret = utils.get_network_id(subnets) - - self.assertEqual(ret, id_a) - - def test_get_network_id_invalid(self): - id_a = mock.sentinel.id_a - id_b = mock.sentinel.id_b - net1 = mock.Mock() - net1.id = id_a - net2 = mock.Mock() - net2.id = id_b - net3 = mock.Mock() - net3.id = id_a - subnets = {1: net1, 2: net2, 3: net3} - - self.assertRaises(exceptions.IntegrityError, utils.get_network_id, - subnets) - - def test_get_network_id_empty(self): - self.assertRaises(exceptions.IntegrityError, utils.get_network_id, {}) - - def test_match_selector(self): - self.assertFalse( - utils.match_selector({'matchLabels': {'app': 'demo'}}, None)) - self.assertFalse( - utils.match_selector({'matchLabels': {'app': 'demo'}}, {})) - self.assertFalse( - utils.match_selector({'matchLabels': {'app': 'demo'}}, - {'app': 'foobar'})) - self.assertTrue( - utils.match_selector({'matchLabels': {'app': 'demo'}}, - {'app': 'demo'})) - self.assertTrue( - utils.match_selector({'matchLabels': {'app': 'demo'}}, - {'app': 'demo', 'foo': 'bar'})) - self.assertTrue( - utils.match_selector({'matchLabels': {'app': 'demo', - 'foo': 'bar'}}, - {'app': 'demo', 'foo': 'bar'})) - self.assertFalse( - utils.match_selector({'matchLabels': {'app': 'demo', - 'foo': 'bar'}}, - {'app': 'demo'})) - - def test_is_network_policy_enabled(self): - CONF.set_override('enabled_handlers', ['fake_handler'], - group='kubernetes') - CONF.set_override('service_security_groups_driver', 'foo', - group='kubernetes') - - self.assertFalse(utils.is_network_policy_enabled()) - - CONF.set_override('enabled_handlers', ['policy'], - group='kubernetes') - CONF.set_override('service_security_groups_driver', 'foo', - group='kubernetes') - - self.assertFalse(utils.is_network_policy_enabled()) - - CONF.set_override('enabled_handlers', ['policy'], - group='kubernetes') - self.addCleanup(CONF.clear_override, 'enabled_handlers', - group='kubernetes') - CONF.set_override('service_security_groups_driver', 'policy', - group='kubernetes') - self.addCleanup(CONF.clear_override, 'service_security_groups_driver', - group='kubernetes') - - self.assertTrue(utils.is_network_policy_enabled()) - - def test_get_resource_name_with_too_long_name(self): - name = 253 * "a" - prefix = 'ns/' - suffix = '-net' - - new_name = utils.get_resource_name(name, prefix=prefix, suffix=suffix) - - self.assertEqual(new_name, - prefix + 248 * 'a' + suffix) - self.assertEqual(len(new_name), 255) - - def test_get_resource_name_with_sane_name(self): - name = 'myns' - prefix = 'ns/' - suffix = '-foo' - - new_name = utils.get_resource_name(name, prefix=prefix, suffix=suffix) - - self.assertEqual(new_name, f'{prefix}{name}{suffix}') - - def test_get_resource_name_with_prefix(self): - name = 'fun_name' - prefix = 'something/' - - new_name = utils.get_resource_name(name, prefix=prefix) - - self.assertEqual(new_name, f'{prefix}{name}') - - def test_get_resource_name_with_sufix(self): - name = 'another' - suffix = '/something-else' - - new_name = utils.get_resource_name(name, suffix=suffix) - - self.assertEqual(new_name, f'{name}{suffix}') - - def test_get_resource_name_non_ascii(self): - name = 'Ру́сский вое́нный кора́бль, иди́ на хуй!' - prefix = 'bar:' - suffix = ':baz' - - new_name = utils.get_resource_name(name, prefix=prefix, suffix=suffix) - - self.assertEqual(new_name, f'{prefix}{name}{suffix}') - - def test_get_resource_name_uid(self): - name = 'ns name' - prefix = 'foo:' - suffix = ':bar' - uid = 'b0f21afa-6d7b-496e-b151-6d7f252b8c6c' - - new_name = utils.get_resource_name(name, uid, prefix, suffix) - - self.assertEqual(new_name, f'{prefix}{uid}/{name}{suffix}') diff --git a/kuryr_kubernetes/tests/unit/controller/drivers/test_vif_pool.py b/kuryr_kubernetes/tests/unit/controller/drivers/test_vif_pool.py deleted file mode 100644 index 2c7dc9b74..000000000 --- a/kuryr_kubernetes/tests/unit/controller/drivers/test_vif_pool.py +++ /dev/null @@ -1,1878 +0,0 @@ -# Copyright 2017 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. - -import collections -import eventlet -import functools -import threading -from unittest import mock -import uuid - -import ddt -from openstack import exceptions as os_exc -from openstack.network.v2 import network as os_network -from openstack.network.v2 import port as os_port -from oslo_config import cfg as oslo_cfg - -from os_vif.objects import network as osv_network -from os_vif.objects import vif as osv_vif - -from kuryr_kubernetes import constants -from kuryr_kubernetes.controller.drivers import nested_vlan_vif -from kuryr_kubernetes.controller.drivers import neutron_vif -from kuryr_kubernetes.controller.drivers import utils -from kuryr_kubernetes.controller.drivers import vif_pool -from kuryr_kubernetes import exceptions -from kuryr_kubernetes import os_vif_util as ovu -from kuryr_kubernetes.tests import base as test_base -from kuryr_kubernetes.tests import fake -from kuryr_kubernetes.tests.unit import kuryr_fixtures as k_fix - - -def get_pod_obj(): - return { - 'status': { - 'qosClass': 'BestEffort', - 'hostIP': '192.168.1.2', - }, - 'kind': 'Pod', - 'spec': { - 'schedulerName': 'default-scheduler', - 'containers': [{ - 'name': 'busybox', - 'image': 'busybox', - 'resources': {} - }], - 'nodeName': 'kuryr-devstack' - }, - 'metadata': { - 'name': 'busybox-sleep1', - 'namespace': 'default', - 'resourceVersion': '53808', - 'uid': '452176db-4a85-11e7-80bd-fa163e29dbbb', - 'annotations': { - 'openstack.org/kuryr-vif': {} - } - }} - - -def get_pod_name(pod): - return "%(namespace)s/%(name)s" % pod['metadata'] - - -AVAILABLE_PORTS_TYPE = functools.partial(collections.defaultdict, - collections.OrderedDict) - - -@ddt.ddt -class BaseVIFPool(test_base.TestCase): - - def test_request_vif(self): - cls = vif_pool.BaseVIFPool - m_driver = mock.MagicMock(spec=cls) - - pod = get_pod_obj() - project_id = str(uuid.uuid4()) - subnet_id = str(uuid.uuid4()) - net_id = str(uuid.uuid4()) - _net = os_network.Network( - id=net_id, - name=None, - mtu=None, - provider_network_type=None, - ) - network = ovu.neutron_to_osvif_network(_net) - subnets = {subnet_id: network} - security_groups = [mock.sentinel.security_groups] - vif = mock.sentinel.vif - - m_driver._get_port_from_pool.return_value = vif - m_driver._recovered_pools = True - oslo_cfg.CONF.set_override('ports_pool_min', - 5, - group='vif_pool') - pool_length = 5 - m_driver._get_pool_size.return_value = pool_length - - self.assertEqual(vif, cls.request_vif(m_driver, pod, project_id, - subnets, security_groups)) - - def test_request_vif_empty_pool(self): - cls = vif_pool.BaseVIFPool - m_driver = mock.MagicMock(spec=cls) - - host_addr = mock.sentinel.host_addr - pod_status = mock.MagicMock() - pod_status.__getitem__.return_value = host_addr - pod = mock.MagicMock() - pod.__getitem__.return_value = pod_status - project_id = str(uuid.uuid4()) - subnet_id = str(uuid.uuid4()) - net_id = str(uuid.uuid4()) - _net = os_network.Network( - id=net_id, - name=None, - mtu=None, - provider_network_type=None, - ) - network = ovu.neutron_to_osvif_network(_net) - subnets = {subnet_id: network} - security_groups = [mock.sentinel.security_groups] - m_driver._recovered_pools = True - m_driver._get_port_from_pool.side_effect = ( - exceptions.ResourceNotReady(pod)) - - self.assertRaises(exceptions.ResourceNotReady, cls.request_vif, - m_driver, pod, project_id, subnets, security_groups) - m_driver._populate_pool.assert_called_once() - - def test_request_vif_pod_without_host(self): - cls = vif_pool.BaseVIFPool - m_driver = mock.MagicMock(spec=cls) - - pod = get_pod_obj() - project_id = str(uuid.uuid4()) - subnets = mock.sentinel.subnets - security_groups = [mock.sentinel.security_groups] - m_driver._get_host_addr.side_effect = KeyError - m_driver._recovered_pools = True - - resp = cls.request_vif(m_driver, pod, project_id, subnets, - security_groups) - self.assertIsNone(resp) - - def test_request_vif_multi_vif_pod_without_host(self): - cls = vif_pool.MultiVIFPool - m_driver = mock.MagicMock(spec=cls) - - pod = get_pod_obj().copy() - del pod['spec']['nodeName'] - project_id = str(uuid.uuid4()) - subnets = mock.sentinel.subnets - security_groups = [mock.sentinel.security_groups] - m_driver._vif_drvs = {} - m_driver._vif_drvs['nested-vlan'] = 'NestedVIFPool' - m_driver._get_pod_vif_type.side_effect = KeyError - - resp = cls.request_vif(m_driver, pod, project_id, subnets, - security_groups) - self.assertIsNone(resp) - - @mock.patch('kuryr_kubernetes.clients.get_kubernetes_client') - @mock.patch('time.time', return_value=50) - @ddt.data((neutron_vif.NeutronPodVIFDriver), - (nested_vlan_vif.NestedVlanPodVIFDriver)) - def test__populate_pool(self, m_vif_driver, m_time, - m_get_kubernetes_client): - cls = vif_pool.BaseVIFPool - m_driver = mock.MagicMock(spec=cls) - - cls_vif_driver = m_vif_driver - vif_driver = mock.MagicMock(spec=cls_vif_driver) - m_driver._drv_vif = vif_driver - - pod = mock.sentinel.pod - project_id = str(uuid.uuid4()) - subnets = mock.sentinel.subnets - security_groups = ['test-sg'] - pool_key = (mock.sentinel.host_addr, project_id) - vif = osv_vif.VIFOpenVSwitch(id='0fa0e837-d34e-4580-a6c4-04f5f607d93e') - vifs = [vif] - - m_driver._existing_vifs = {} - m_driver._available_ports_pools = AVAILABLE_PORTS_TYPE() - m_driver._recovered_pools = True - m_driver._lock = threading.Lock() - m_driver._populate_pool_lock = { - pool_key: mock.MagicMock(spec=threading.Lock())} - m_driver._create_ports_semaphore = mock.MagicMock( - spec=eventlet.semaphore.Semaphore(20)) - - oslo_cfg.CONF.set_override('ports_pool_min', - 5, - group='vif_pool') - oslo_cfg.CONF.set_override('ports_pool_update_frequency', - 15, - group='vif_pool') - m_driver._get_pool_size.return_value = 2 - vif_driver.request_vifs.return_value = vifs - - cls._populate_pool(m_driver, pool_key, pod, subnets, - tuple(security_groups)) - m_driver._get_pool_size.assert_called_once() - m_driver._drv_vif.request_vifs.assert_called_once() - - @mock.patch('kuryr_kubernetes.clients.get_kubernetes_client') - @ddt.data((neutron_vif.NeutronPodVIFDriver), - (nested_vlan_vif.NestedVlanPodVIFDriver)) - def test__populate_pool_not_ready(self, m_vif_driver, - m_get_kubernetes_client): - cls = vif_pool.BaseVIFPool - m_driver = mock.MagicMock(spec=cls) - - cls_vif_driver = m_vif_driver - vif_driver = mock.MagicMock(spec=cls_vif_driver) - m_driver._drv_vif = vif_driver - - pod = mock.sentinel.pod - project_id = str(uuid.uuid4()) - subnets = mock.sentinel.subnets - security_groups = 'test-sg' - pool_key = (mock.sentinel.host_addr, project_id) - m_driver._recovered_pools = False - - self.assertFalse(cls._populate_pool( - m_driver, pool_key, pod, subnets, - tuple(security_groups))) - m_driver._drv_vif.request_vifs.assert_not_called() - - @mock.patch('kuryr_kubernetes.clients.get_kubernetes_client') - @ddt.data((neutron_vif.NeutronPodVIFDriver), - (nested_vlan_vif.NestedVlanPodVIFDriver)) - def test__populate_pool_not_ready_dont_raise(self, m_vif_driver, - m_get_kubernetes_client): - cls = vif_pool.BaseVIFPool - m_driver = mock.MagicMock(spec=cls) - - cls_vif_driver = m_vif_driver - vif_driver = mock.MagicMock(spec=cls_vif_driver) - m_driver._drv_vif = vif_driver - - pod = mock.sentinel.pod - project_id = str(uuid.uuid4()) - subnets = mock.sentinel.subnets - security_groups = 'test-sg' - pool_key = (mock.sentinel.host_addr, project_id) - m_driver._recovered_pools = False - - cls._populate_pool(m_driver, pool_key, pod, subnets, - tuple(security_groups)) - m_driver._drv_vif.request_vifs.assert_not_called() - - @mock.patch('kuryr_kubernetes.clients.get_kubernetes_client') - @mock.patch('time.time', return_value=0) - @ddt.data((neutron_vif.NeutronPodVIFDriver), - (nested_vlan_vif.NestedVlanPodVIFDriver)) - def test__populate_pool_no_update(self, m_vif_driver, m_time, - m_get_kubernetes_client): - cls = vif_pool.BaseVIFPool - m_driver = mock.MagicMock(spec=cls) - - pod = mock.sentinel.pod - project_id = str(uuid.uuid4()) - subnets = mock.sentinel.subnets - security_groups = 'test-sg' - pool_key = (mock.sentinel.host_addr, project_id) - m_driver._get_pool_size.return_value = 4 - - cls_vif_driver = m_vif_driver - vif_driver = mock.MagicMock(spec=cls_vif_driver) - m_driver._drv_vif = vif_driver - - oslo_cfg.CONF.set_override('ports_pool_update_frequency', - 15, - group='vif_pool') - oslo_cfg.CONF.set_override('ports_pool_min', - 3, - group='vif_pool') - m_driver._get_pool_size.return_value = 10 - m_driver._recovered_pools = True - - cls._populate_pool(m_driver, pool_key, pod, subnets, - tuple(security_groups)) - m_driver._get_pool_size.assert_called() - m_driver._drv_vif.request_vifs.assert_not_called() - - def test_release_vif(self): - cls = vif_pool.BaseVIFPool - m_driver = mock.MagicMock(spec=cls) - m_driver._recyclable_ports = {} - m_driver._existing_vifs = {} - - pod = get_pod_obj() - project_id = mock.sentinel.project_id - security_groups = [mock.sentinel.security_groups] - net_id = str(uuid.uuid4()) - _net = os_network.Network( - id=net_id, - name=None, - mtu=None, - provider_network_type=None, - ) - network = ovu.neutron_to_osvif_network(_net) - vif = osv_vif.VIFOpenVSwitch(id='0fa0e837-d34e-4580-a6c4-04f5f607d93e', - network=network) - - m_driver._return_ports_to_pool.return_value = None - m_driver._recovered_pools = True - - cls.release_vif(m_driver, pod, vif, project_id, security_groups) - - m_driver._return_ports_to_pool.assert_not_called() - - @mock.patch('kuryr_kubernetes.controller.drivers.utils.get_vifs') - def test__get_in_use_ports(self, get_vifs): - cls = vif_pool.BaseVIFPool - m_driver = mock.MagicMock(spec=cls) - - kubernetes = self.useFixture(k_fix.MockK8sClient()).client - pod = get_pod_obj() - port_id = str(uuid.uuid4()) - port_network = osv_network.Network(id=str(uuid.uuid4())) - pod_vif = osv_vif.VIFBase(id=port_id, network=port_network) - get_vifs.return_value = {'eth0': pod_vif} - items = [pod] - kubernetes.get.return_value = {'items': items} - network = {} - - resp = cls._get_in_use_ports_info(m_driver) - network[port_network.id] = port_network - self.assertEqual(resp, ([port_id], network)) - - def test__get_in_use_ports_empty(self): - cls = vif_pool.BaseVIFPool - m_driver = mock.MagicMock(spec=cls) - - kubernetes = self.useFixture(k_fix.MockK8sClient()).client - items = [] - kubernetes.get.return_value = {'items': items} - - resp = cls._get_in_use_ports_info(m_driver) - - self.assertEqual(resp, ([], {})) - - def test_cleanup_leftover_ports(self): - cls = vif_pool.BaseVIFPool - m_driver = mock.MagicMock(spec=cls) - - os_net = self.useFixture(k_fix.MockNetworkClient()).client - - port_id = str(uuid.uuid4()) - port = fake.get_port_obj(port_id=port_id) - net_id = port.network_id - tags = 'clusterTest' - port.tags = [tags] - os_net.ports.return_value = [port] - oslo_cfg.CONF.set_override('resource_tags', - tags, - group='neutron_defaults') - self.addCleanup(oslo_cfg.CONF.clear_override, 'resource_tags', - group='neutron_defaults') - - os_net.networks.return_value = [os_network.Network(id=net_id)] - - cls._cleanup_leftover_ports(m_driver) - os_net.networks.assert_called() - os_net.delete_port.assert_not_called() - - def test_cleanup_leftover_ports_different_network(self): - cls = vif_pool.BaseVIFPool - m_driver = mock.MagicMock(spec=cls) - - os_net = self.useFixture(k_fix.MockNetworkClient()).client - - port_id = str(uuid.uuid4()) - port = fake.get_port_obj(port_id=port_id) - tags = 'clusterTest' - port.tags = [tags] - os_net.ports.return_value = [port] - oslo_cfg.CONF.set_override('resource_tags', - tags, - group='neutron_defaults') - self.addCleanup(oslo_cfg.CONF.clear_override, 'resource_tags', - group='neutron_defaults') - os_net.networks.return_value = [] - - cls._cleanup_leftover_ports(m_driver) - os_net.networks.assert_called() - os_net.delete_port.assert_not_called() - - def test_cleanup_leftover_ports_no_binding(self): - cls = vif_pool.BaseVIFPool - m_driver = mock.MagicMock(spec=cls) - - os_net = self.useFixture(k_fix.MockNetworkClient()).client - - port_id = str(uuid.uuid4()) - port = fake.get_port_obj(port_id=port_id) - net_id = port.network_id - tags = 'clusterTest' - port.tags = [tags] - port.binding_host_id = None - os_net.ports.return_value = [port] - oslo_cfg.CONF.set_override('resource_tags', - tags, - group='neutron_defaults') - self.addCleanup(oslo_cfg.CONF.clear_override, 'resource_tags', - group='neutron_defaults') - - os_net.networks.return_value = [os_network.Network(id=net_id)] - - cls._cleanup_leftover_ports(m_driver) - os_net.networks.assert_called() - os_net.delete_port.assert_called_once_with(port.id) - - def test_cleanup_leftover_ports_no_tags(self): - cls = vif_pool.BaseVIFPool - m_driver = mock.MagicMock(spec=cls) - - os_net = self.useFixture(k_fix.MockNetworkClient()).client - - port_id = str(uuid.uuid4()) - port = fake.get_port_obj(port_id=port_id) - net_id = port.network_id - tags = 'clusterTest' - os_net.ports.return_value = [port] - oslo_cfg.CONF.set_override('resource_tags', - tags, - group='neutron_defaults') - self.addCleanup(oslo_cfg.CONF.clear_override, 'resource_tags', - group='neutron_defaults') - - os_net.networks.return_value = [os_network.Network(id=net_id)] - - cls._cleanup_leftover_ports(m_driver) - os_net.networks.assert_called() - os_net.delete_port.assert_called_once_with(port.id) - - def test_cleanup_leftover_ports_no_tagging(self): - cls = vif_pool.BaseVIFPool - m_driver = mock.MagicMock(spec=cls) - - os_net = self.useFixture(k_fix.MockNetworkClient()).client - - port_id = str(uuid.uuid4()) - port = fake.get_port_obj(port_id=port_id) - os_net.ports.return_value = [port] - - cls._cleanup_leftover_ports(m_driver) - os_net.networks.assert_not_called() - os_net.delete_port.assert_not_called() - - @mock.patch('kuryr_kubernetes.controller.drivers.utils.delete_ports') - def test_cleanup_leftover_ports_no_tagging_no_binding(self, m_del_ports): - cls = vif_pool.BaseVIFPool - m_driver = mock.MagicMock(spec=cls) - - os_net = self.useFixture(k_fix.MockNetworkClient()).client - - port_id = str(uuid.uuid4()) - port = fake.get_port_obj(port_id=port_id) - port.binding_host_id = None - os_net.ports.return_value = [port] - - cls._cleanup_leftover_ports(m_driver) - os_net.networks.assert_not_called() - m_del_ports.assert_called_once_with([port]) - - -@ddt.ddt -class NeutronVIFPool(test_base.TestCase): - - @mock.patch('kuryr_kubernetes.controller.drivers.utils.get_port_name') - @mock.patch('eventlet.spawn') - def test__get_port_from_pool(self, m_eventlet, m_get_port_name): - m_driver = vif_pool.NeutronVIFPool() - os_net = self.useFixture(k_fix.MockNetworkClient()).client - - pool_key = mock.sentinel.pool_key - port_id = str(uuid.uuid4()) - port = mock.sentinel.port - subnets = mock.sentinel.subnets - sgs = ('test-sg',) - - oslo_cfg.CONF.set_override('port_debug', - True, group='kubernetes') - pod = get_pod_obj() - m_get_port_name.return_value = get_pod_name(pod) - - m_driver._available_ports_pools = AVAILABLE_PORTS_TYPE() - m_driver._available_ports_pools[pool_key][sgs] = [port_id] - m_driver._existing_vifs = {port_id: port} - m_get_port_name.return_value = get_pod_name(pod) - - oslo_cfg.CONF.set_override('ports_pool_min', 5, group='vif_pool') - oslo_cfg.CONF.set_override('port_debug', True, group='kubernetes') - m_driver._get_pool_size = mock.Mock(return_value=5) - - self.assertEqual(port, m_driver._get_port_from_pool( - pool_key, pod, subnets, sgs)) - - os_net.update_port.assert_called_once_with( - port_id, name=get_pod_name(pod), device_id=pod['metadata']['uid']) - # 2 calls from the constructor, so no calls in _get_port_from_pool() - self.assertEqual(3, m_eventlet.call_count) - - @mock.patch('kuryr_kubernetes.controller.drivers.utils.get_port_name') - @mock.patch('eventlet.spawn') - def test__get_port_from_pool_pool_populate(self, m_eventlet, - m_get_port_name): - m_driver = vif_pool.NeutronVIFPool() - os_net = self.useFixture(k_fix.MockNetworkClient()).client - - pool_key = mock.sentinel.pool_key - port_id = str(uuid.uuid4()) - port = mock.sentinel.port - subnets = mock.sentinel.subnets - sgs = ('test-sg',) - - pod = get_pod_obj() - - m_driver._available_ports_pools = AVAILABLE_PORTS_TYPE() - m_driver._available_ports_pools[pool_key][sgs] = [port_id] - m_driver._existing_vifs = {port_id: port} - m_get_port_name.return_value = get_pod_name(pod) - - oslo_cfg.CONF.set_override('ports_pool_min', 5, group='vif_pool') - oslo_cfg.CONF.set_override('port_debug', True, group='kubernetes') - m_driver._get_pool_size = mock.Mock(return_value=3) - - self.assertEqual(port, m_driver._get_port_from_pool( - pool_key, pod, subnets, sgs)) - - os_net.update_port.assert_called_once_with( - port_id, name=get_pod_name(pod), device_id=pod['metadata']['uid']) - # 2 calls come from the constructor, so 1 call in _get_port_from_pool() - self.assertEqual(3, m_eventlet.call_count) - - def test__get_port_from_pool_empty_pool(self): - cls = vif_pool.NeutronVIFPool - m_driver = mock.MagicMock(spec=cls) - - os_net = self.useFixture(k_fix.MockNetworkClient()).client - - pod = get_pod_obj() - pool_key = mock.sentinel.pool_key - subnets = mock.sentinel.subnets - sgs = ('test-sg',) - - m_driver._available_ports_pools = AVAILABLE_PORTS_TYPE() - m_driver._available_ports_pools[pool_key][sgs] = [] - - self.assertRaises(exceptions.ResourceNotReady, cls._get_port_from_pool, - m_driver, pool_key, pod, subnets, sgs) - - os_net.update_port.assert_not_called() - - @mock.patch('eventlet.spawn') - def test__get_port_from_pool_empty_pool_reuse(self, m_eventlet): - cls = vif_pool.NeutronVIFPool - m_driver = mock.MagicMock(spec=cls) - - os_net = self.useFixture(k_fix.MockNetworkClient()).client - - pod = get_pod_obj() - port_id = str(uuid.uuid4()) - port_id_2 = str(uuid.uuid4()) - port = mock.sentinel.port - port_2 = mock.sentinel.port_2 - pool_key = mock.sentinel.pool_key - subnets = mock.sentinel.subnets - sgs = ('test-sg',) - sgs_2 = ('test-sg2',) - sgs_3 = ('test-sg3',) - - oslo_cfg.CONF.set_override('port_debug', False, group='kubernetes') - - m_driver._available_ports_pools = AVAILABLE_PORTS_TYPE() - m_driver._available_ports_pools[pool_key][sgs] = [] - m_driver._available_ports_pools[pool_key][sgs_2] = [port_id] - m_driver._available_ports_pools[pool_key][sgs_3] = [port_id_2] - m_driver._existing_vifs = {port_id: port, port_id_2: port_2} - - self.assertEqual(port, cls._get_port_from_pool( - m_driver, pool_key, pod, subnets, sgs)) - - os_net.update_port.assert_called_once_with( - port_id, security_groups=list(sgs)) - m_eventlet.assert_called() - - def test__get_port_from_pool_empty_pool_reuse_no_ports(self): - cls = vif_pool.NeutronVIFPool - m_driver = mock.MagicMock(spec=cls) - - os_net = self.useFixture(k_fix.MockNetworkClient()).client - - pod = get_pod_obj() - pool_key = mock.sentinel.pool_key - subnets = mock.sentinel.subnets - sgs = ('test-sg',) - sgs_2 = ('test-sg2',) - - oslo_cfg.CONF.set_override('port_debug', False, group='kubernetes') - pool_length = 5 - m_driver._get_pool_size.return_value = pool_length - - m_driver._available_ports_pools = AVAILABLE_PORTS_TYPE() - m_driver._available_ports_pools[pool_key][(sgs_2,)] = [] - m_driver._available_ports_pools[pool_key][(sgs,)] = [] - - self.assertRaises(exceptions.ResourceNotReady, cls._get_port_from_pool, - m_driver, pool_key, pod, subnets, sgs) - - os_net.update_port.assert_not_called() - - @ddt.data((0), (10)) - def test__trigger_return_to_pool(self, max_pool): - cls = vif_pool.NeutronVIFPool - m_driver = mock.MagicMock(spec=cls) - - os_net = self.useFixture(k_fix.MockNetworkClient()).client - - pool_key = ('node_ip', 'project_id') - port_id = str(uuid.uuid4()) - pool_length = 5 - - m_driver._recyclable_ports = {port_id: pool_key} - m_driver._available_ports_pools = AVAILABLE_PORTS_TYPE() - m_driver._recovered_pools = True - oslo_cfg.CONF.set_override('ports_pool_max', - max_pool, - group='vif_pool') - oslo_cfg.CONF.set_override('port_debug', - True, - group='kubernetes') - os_net.ports.return_value = [ - os_port.Port( - id=port_id, - security_group_ids=['security_group_modified'], - ), - ] - m_driver._get_pool_size.return_value = pool_length - - cls._trigger_return_to_pool(m_driver) - - os_net.update_port.assert_called_once_with( - port_id, name=constants.KURYR_PORT_NAME, device_id='') - os_net.delete_port.assert_not_called() - - @ddt.data((0), (10)) - def test__trigger_return_to_pool_no_update(self, max_pool): - cls = vif_pool.NeutronVIFPool - m_driver = mock.MagicMock(spec=cls) - - os_net = self.useFixture(k_fix.MockNetworkClient()).client - - pool_key = ('node_ip', 'project_id') - port_id = str(uuid.uuid4()) - pool_length = 5 - - m_driver._recyclable_ports = {port_id: pool_key} - m_driver._available_ports_pools = AVAILABLE_PORTS_TYPE() - oslo_cfg.CONF.set_override('ports_pool_max', - max_pool, - group='vif_pool') - oslo_cfg.CONF.set_override('port_debug', - False, - group='kubernetes') - - port = fake.get_port_obj(port_id=port_id) - port.security_group_ids = ['security_group'] - os_net.ports.return_value = (p for p in [port]) - m_driver._get_pool_size.return_value = pool_length - m_driver._recovered_pools = True - - cls._trigger_return_to_pool(m_driver) - - os_net.update_port.assert_not_called() - os_net.delete_port.assert_not_called() - - def test__trigger_return_to_pool_delete_port(self): - cls = vif_pool.NeutronVIFPool - m_driver = mock.MagicMock(spec=cls) - - os_net = self.useFixture(k_fix.MockNetworkClient()).client - - pool_key = ('node_ip', 'project_id') - port_id = str(uuid.uuid4()) - pool_length = 10 - vif = mock.sentinel.vif - - m_driver._recyclable_ports = {port_id: pool_key} - m_driver._available_ports_pools = AVAILABLE_PORTS_TYPE() - m_driver._existing_vifs = {port_id: vif} - m_driver._recovered_pools = True - oslo_cfg.CONF.set_override('ports_pool_max', - 10, - group='vif_pool') - os_net.ports.return_value = [ - os_port.Port( - id=port_id, - security_group_ids=['security_group_modified'], - ), - ] - m_driver._get_pool_size.return_value = pool_length - - cls._trigger_return_to_pool(m_driver) - - os_net.update_port.assert_not_called() - os_net.delete_port.assert_called_once_with(port_id) - - def test__trigger_return_to_pool_update_exception(self): - cls = vif_pool.NeutronVIFPool - m_driver = mock.MagicMock(spec=cls) - - os_net = self.useFixture(k_fix.MockNetworkClient()).client - - pool_key = ('node_ip', 'project_id') - port_id = str(uuid.uuid4()) - pool_length = 5 - - m_driver._recyclable_ports = {port_id: pool_key} - m_driver._available_ports_pools = AVAILABLE_PORTS_TYPE() - m_driver._recovered_pools = True - oslo_cfg.CONF.set_override('ports_pool_max', - 0, - group='vif_pool') - oslo_cfg.CONF.set_override('port_debug', - True, - group='kubernetes') - oslo_cfg.CONF.set_override('port_debug', - True, - group='kubernetes') - os_net.ports.return_value = [ - os_port.Port( - id=port_id, - security_group_ids=['security_group_modified'], - ), - ] - m_driver._get_pool_size.return_value = pool_length - os_net.update_port.side_effect = os_exc.SDKException - - cls._trigger_return_to_pool(m_driver) - - os_net.update_port.assert_called_once_with( - port_id, name=constants.KURYR_PORT_NAME, device_id='') - os_net.delete_port.assert_not_called() - - def test__trigger_return_to_pool_delete_exception(self): - cls = vif_pool.NeutronVIFPool - m_driver = mock.MagicMock(spec=cls) - - os_net = self.useFixture(k_fix.MockNetworkClient()).client - - pool_key = ('node_ip', 'project_id') - port_id = str(uuid.uuid4()) - pool_length = 10 - vif = mock.sentinel.vif - - m_driver._recyclable_ports = {port_id: pool_key} - m_driver._available_ports_pools = AVAILABLE_PORTS_TYPE() - m_driver._existing_vifs = {port_id: vif} - m_driver._recovered_pools = True - oslo_cfg.CONF.set_override('ports_pool_max', - 5, - group='vif_pool') - os_net.ports.return_value = [ - os_port.Port( - id=port_id, - security_group_ids=['security_group_modified'], - ), - ] - m_driver._get_pool_size.return_value = pool_length - - cls._trigger_return_to_pool(m_driver) - - os_net.update_port.assert_not_called() - os_net.delete_port.assert_called_once_with(port_id) - - def test__trigger_return_to_pool_delete_key_error(self): - cls = vif_pool.NeutronVIFPool - m_driver = mock.MagicMock(spec=cls) - - os_net = self.useFixture(k_fix.MockNetworkClient()).client - - pool_key = ('node_ip', 'project_id') - port_id = str(uuid.uuid4()) - pool_length = 10 - - m_driver._recyclable_ports = {port_id: pool_key} - m_driver._available_ports_pools = AVAILABLE_PORTS_TYPE() - m_driver._existing_vifs = {} - m_driver._recovered_pools = True - oslo_cfg.CONF.set_override('ports_pool_max', - 5, - group='vif_pool') - os_net.ports.return_value = [ - os_port.Port( - id=port_id, - security_group_ids=['security_group_modified'], - ), - ] - m_driver._get_pool_size.return_value = pool_length - - cls._trigger_return_to_pool(m_driver) - - os_net.update_port.assert_not_called() - os_net.delete_port.assert_not_called() - - @mock.patch('kuryr_kubernetes.os_vif_util.neutron_to_osvif_vif') - @mock.patch('kuryr_kubernetes.utils.get_subnet') - def test__recover_precreated_ports(self, m_get_subnet, m_to_osvif): - cls = vif_pool.NeutronVIFPool - m_driver = mock.MagicMock(spec=cls) - os_net = self.useFixture(k_fix.MockNetworkClient()).client - - cls_vif_driver = neutron_vif.NeutronPodVIFDriver - vif_driver = mock.MagicMock(spec=cls_vif_driver) - m_driver._drv_vif = vif_driver - - m_driver._existing_vifs = {} - m_driver._available_ports_pools = AVAILABLE_PORTS_TYPE() - - port_id = str(uuid.uuid4()) - port = fake.get_port_obj(port_id=port_id) - filtered_ports = [port] - os_net.ports.return_value = filtered_ports - vif_plugin = mock.sentinel.plugin - port.binding_vif_type = vif_plugin - - oslo_cfg.CONF.set_override('port_debug', - False, - group='kubernetes') - - subnet_id = port.fixed_ips[0]['subnet_id'] - net_id = str(uuid.uuid4()) - _net = os_network.Network( - id=net_id, - name=None, - mtu=None, - provider_network_type=None, - ) - network = ovu.neutron_to_osvif_network(_net) - subnet = {subnet_id: network} - m_get_subnet.return_value = network - vif = mock.sentinel.vif - m_to_osvif.return_value = vif - m_driver._get_in_use_ports_info.return_value = [], {} - - pool_key = (port.binding_host_id, port.project_id, net_id) - m_driver._get_pool_key.return_value = pool_key - m_driver._get_trunks_info.return_value = ({}, {}, {}) - - cls._recover_precreated_ports(m_driver) - - os_net.ports.assert_called_once() - m_get_subnet.assert_called_with(subnet_id) - m_to_osvif.assert_called_once_with(vif_plugin, port, subnet) - - self.assertEqual(m_driver._existing_vifs[port_id], vif) - self.assertEqual(m_driver._available_ports_pools[pool_key], - {tuple(port.security_group_ids): [port_id]}) - - @mock.patch('kuryr_kubernetes.os_vif_util.neutron_to_osvif_vif') - @mock.patch('kuryr_kubernetes.utils.get_subnet') - def test__recover_precreated_ports_empty(self, m_get_subnet, m_to_osvif): - cls = vif_pool.NeutronVIFPool - m_driver = mock.MagicMock(spec=cls) - os_net = self.useFixture(k_fix.MockNetworkClient()).client - - filtered_ports = [] - os_net.ports.return_value = filtered_ports - m_driver._get_trunks_info.return_value = ({}, {}, {}) - m_driver._get_in_use_ports_info.return_value = [], {} - - oslo_cfg.CONF.set_override('port_debug', - False, - group='kubernetes') - - cls._recover_precreated_ports(m_driver) - - os_net.ports.assert_called_once() - m_get_subnet.assert_not_called() - m_to_osvif.assert_not_called() - - @mock.patch('eventlet.GreenPool') - def test_delete_network_pools(self, m_green_pool): - cls = vif_pool.NeutronVIFPool - m_driver = mock.MagicMock(spec=cls) - m_pool = mock.MagicMock() - m_green_pool.return_value = m_pool - - net_id = mock.sentinel.net_id - pool_key = ('node_ip', 'project_id') - port_id = str(uuid.uuid4()) - m_driver._available_ports_pools = AVAILABLE_PORTS_TYPE() - m_driver._available_ports_pools[pool_key][('sg',)] = [port_id] - m_driver._lock = threading.Lock() - m_driver._populate_pool_lock = { - pool_key: mock.MagicMock(spec=threading.Lock())} - m_driver._existing_vifs = {port_id: mock.sentinel.vif} - m_driver._recovered_pools = True - - m_driver._get_pool_key_net.return_value = net_id - - cls.delete_network_pools(m_driver, net_id) - - m_driver._trigger_return_to_pool.assert_called_once() - m_driver._get_pool_key_net.assert_called_once() - m_pool.imap.assert_called_once_with(utils.delete_neutron_port, - [port_id]) - - def test_delete_network_pools_not_ready(self): - cls = vif_pool.NeutronVIFPool - m_driver = mock.MagicMock(spec=cls) - os_net = self.useFixture(k_fix.MockNetworkClient()).client - - net_id = mock.sentinel.net_id - m_driver._recovered_pools = False - - self.assertRaises(exceptions.ResourceNotReady, - cls.delete_network_pools, m_driver, net_id) - - m_driver._trigger_return_to_pool.assert_not_called() - m_driver._get_pool_key_net.assert_not_called() - os_net.delete_port.assert_not_called() - - @mock.patch('eventlet.GreenPool') - def test_delete_network_pools_missing_port_id(self, m_green_pool): - cls = vif_pool.NeutronVIFPool - m_driver = mock.MagicMock(spec=cls) - m_pool = mock.MagicMock() - m_green_pool.return_value = m_pool - - net_id = mock.sentinel.net_id - pool_key = ('node_ip', 'project_id') - port_id = str(uuid.uuid4()) - m_driver._available_ports_pools = AVAILABLE_PORTS_TYPE() - m_driver._available_ports_pools[pool_key][('sgs',)] = [port_id] - m_driver._lock = threading.Lock() - m_driver._populate_pool_lock = { - pool_key: mock.MagicMock(spec=threading.Lock())} - m_driver._existing_vifs = {} - m_driver._recovered_pools = True - - m_driver._get_pool_key_net.return_value = net_id - - cls.delete_network_pools(m_driver, net_id) - - m_driver._trigger_return_to_pool.assert_called_once() - m_driver._get_pool_key_net.assert_called_once() - m_pool.imap.assert_called_once_with(utils.delete_neutron_port, - [port_id]) - - -@ddt.ddt -class NestedVIFPool(test_base.TestCase): - - def _get_trunk_obj(self, port_id=None, subport_id=None, trunk_id=None): - trunk_obj = { - 'status': 'ACTIVE', - 'name': 'trunk-01aa31ea-5adf-4776-9c5d-21b50dba0ccc', - 'admin_state_up': True, - 'tenant_id': '18fbc0e645d74e83931193ef99dfe5c5', - 'sub_ports': [{'port_id': '85104e7d-8597-4bf7-94e7-a447ef0b50f1', - 'segmentation_type': 'vlan', - 'segmentation_id': 4056}], - 'updated_at': '2017-06-09T13:25:01Z', - 'id': 'd1217757-848f-45dd-9ff2-3640f9b053dc', - 'revision_number': 2359, - 'project_id': '18fbc0e645d74e83931193ef99dfe5c5', - 'port_id': '01aa31ea-5adf-4776-9c5d-21b50dba0ccc', - 'created_at': '2017-05-19T16:43:22Z', - 'description': '' - } - - if port_id: - trunk_obj['port_id'] = port_id - if subport_id: - trunk_obj['sub_ports'][0]['port_id'] = subport_id - if trunk_id: - trunk_obj['id'] = trunk_id - - return trunk_obj - - def _get_parent_ports(self, trunk_objs): - parent_ports = {} - for trunk_obj in trunk_objs: - parent_ports[trunk_obj['id']] = { - 'ip': 'kuryr-devstack', - 'subports': trunk_obj['sub_ports']} - return parent_ports - - @mock.patch('kuryr_kubernetes.controller.drivers.utils.get_port_name') - @mock.patch('eventlet.spawn') - def test__get_port_from_pool(self, m_eventlet, m_get_port_name): - m_driver = vif_pool.NestedVIFPool() - os_net = self.useFixture(k_fix.MockNetworkClient()).client - - pool_key = mock.sentinel.pool_key - port_id = str(uuid.uuid4()) - port = mock.sentinel.port - subnets = mock.sentinel.subnets - sgs = ('test-sg',) - - pod = get_pod_obj() - - m_driver._available_ports_pools = AVAILABLE_PORTS_TYPE() - m_driver._available_ports_pools[pool_key][sgs] = [port_id] - m_driver._existing_vifs = {port_id: port} - m_get_port_name.return_value = get_pod_name(pod) - - oslo_cfg.CONF.set_override('ports_pool_min', 5, group='vif_pool') - oslo_cfg.CONF.set_override('port_debug', True, group='kubernetes') - m_driver._get_pool_size = mock.Mock(return_value=5) - - self.assertEqual(port, m_driver._get_port_from_pool( - pool_key, pod, subnets, sgs)) - - os_net.update_port.assert_called_once_with( - port_id, name=get_pod_name(pod)) - self.assertEqual(3, m_eventlet.call_count) - - @mock.patch('kuryr_kubernetes.controller.drivers.utils.get_port_name') - @mock.patch('eventlet.spawn') - def test__get_port_from_pool_pool_populate(self, m_eventlet, - m_get_port_name): - m_driver = vif_pool.NestedVIFPool() - os_net = self.useFixture(k_fix.MockNetworkClient()).client - - pool_key = mock.sentinel.pool_key - port_id = str(uuid.uuid4()) - port = mock.sentinel.port - subnets = mock.sentinel.subnets - sgs = ('test-sg',) - - pod = get_pod_obj() - - m_driver._available_ports_pools = AVAILABLE_PORTS_TYPE() - m_driver._available_ports_pools[pool_key][sgs] = [port_id] - m_driver._existing_vifs = {port_id: port} - m_get_port_name.return_value = get_pod_name(pod) - - oslo_cfg.CONF.set_override('ports_pool_min', 5, group='vif_pool') - oslo_cfg.CONF.set_override('port_debug', True, group='kubernetes') - m_driver._get_pool_size = mock.Mock(return_value=3) - - self.assertEqual(port, m_driver._get_port_from_pool( - pool_key, pod, subnets, sgs)) - - os_net.update_port.assert_called_once_with( - port_id, name=get_pod_name(pod)) - # 2 calls come from the constructor, so 1 call in _get_port_from_pool() - self.assertEqual(3, m_eventlet.call_count) - - def test__get_port_from_pool_empty_pool(self): - cls = vif_pool.NestedVIFPool - m_driver = mock.MagicMock(spec=cls) - - os_net = self.useFixture(k_fix.MockNetworkClient()).client - - pod = mock.sentinel.pod - pool_key = mock.sentinel.pool_key - subnets = mock.sentinel.subnets - sgs = ('test-sg',) - - m_driver._available_ports_pools = AVAILABLE_PORTS_TYPE() - m_driver._available_ports_pools[pool_key][sgs] = [] - - self.assertRaises(exceptions.ResourceNotReady, cls._get_port_from_pool, - m_driver, pool_key, pod, subnets, sgs) - - os_net.update_port.assert_not_called() - - @mock.patch('eventlet.spawn') - def test__get_port_from_pool_empty_pool_reuse(self, m_eventlet): - cls = vif_pool.NestedVIFPool - m_driver = mock.MagicMock(spec=cls) - - os_net = self.useFixture(k_fix.MockNetworkClient()).client - - pod = mock.sentinel.pod - port_id = str(uuid.uuid4()) - port_id_2 = str(uuid.uuid4()) - port = mock.sentinel.port - port_2 = mock.sentinel.port_2 - pool_key = mock.sentinel.pool_key - subnets = mock.sentinel.subnets - sgs = ('test-sg',) - sgs_2 = ('test-sg2',) - sgs_3 = ('test-sg3',) - - oslo_cfg.CONF.set_override('port_debug', False, group='kubernetes') - pool_length = 5 - m_driver._get_pool_size.return_value = pool_length - - m_driver._available_ports_pools = AVAILABLE_PORTS_TYPE() - m_driver._available_ports_pools[pool_key][sgs] = [] - m_driver._available_ports_pools[pool_key][sgs_2] = [port_id] - m_driver._available_ports_pools[pool_key][sgs_3] = [port_id_2] - m_driver._existing_vifs = {port_id: port, port_id_2: port_2} - - self.assertEqual(port, cls._get_port_from_pool( - m_driver, pool_key, pod, subnets, sgs)) - - os_net.update_port.assert_called_once_with( - port_id, security_groups=list(sgs)) - m_eventlet.assert_called() - - def test__get_port_from_pool_empty_pool_reuse_no_ports(self): - cls = vif_pool.NestedVIFPool - m_driver = mock.MagicMock(spec=cls) - - os_net = self.useFixture(k_fix.MockNetworkClient()).client - - pod = mock.sentinel.pod - pool_key = mock.sentinel.pool_key - subnets = mock.sentinel.subnets - sgs = ('test-sg',) - sgs_2 = ('test-sg2',) - - oslo_cfg.CONF.set_override('port_debug', False, group='kubernetes') - pool_length = 5 - m_driver._get_pool_size.return_value = pool_length - - m_driver._available_ports_pools = AVAILABLE_PORTS_TYPE() - m_driver._available_ports_pools[pool_key][sgs] = [] - m_driver._available_ports_pools[pool_key][sgs_2] = [] - - self.assertRaises(exceptions.ResourceNotReady, cls._get_port_from_pool, - m_driver, pool_key, pod, subnets, sgs) - - os_net.update_port.assert_not_called() - - @ddt.data((0), (10)) - def test__trigger_return_to_pool(self, max_pool): - cls = vif_pool.NestedVIFPool - m_driver = mock.MagicMock(spec=cls) - - os_net = self.useFixture(k_fix.MockNetworkClient()).client - - pool_key = ('node_ip', 'project_id') - port_id = str(uuid.uuid4()) - pool_length = 5 - - m_driver._recyclable_ports = {port_id: pool_key} - m_driver._available_ports_pools = AVAILABLE_PORTS_TYPE() - oslo_cfg.CONF.set_override('ports_pool_max', - max_pool, - group='vif_pool') - oslo_cfg.CONF.set_override('port_debug', - True, - group='kubernetes') - os_net.ports.return_value = [ - os_port.Port( - id=port_id, - security_group_ids=['security_group_modified'], - ), - ] - m_driver._get_pool_size.return_value = pool_length - m_driver._recovered_pools = True - - cls._trigger_return_to_pool(m_driver) - - (os_net.update_port - .assert_called_once_with(port_id, - name=constants.KURYR_PORT_NAME)) - os_net.delete_port.assert_not_called() - - @ddt.data((0), (10)) - def test__trigger_return_to_pool_no_update(self, max_pool): - cls = vif_pool.NestedVIFPool - m_driver = mock.MagicMock(spec=cls) - - os_net = self.useFixture(k_fix.MockNetworkClient()).client - - pool_key = ('node_ip', 'project_id') - port_id = str(uuid.uuid4()) - pool_length = 5 - - m_driver._recyclable_ports = {port_id: pool_key} - m_driver._available_ports_pools = AVAILABLE_PORTS_TYPE() - oslo_cfg.CONF.set_override('ports_pool_max', - max_pool, - group='vif_pool') - oslo_cfg.CONF.set_override('port_debug', - False, - group='kubernetes') - port = fake.get_port_obj(port_id=port_id) - port.security_group_ids = ['security_group'] - os_net.ports.return_value = [port] - m_driver._get_pool_size.return_value = pool_length - m_driver._recovered_pools = True - - cls._trigger_return_to_pool(m_driver) - - os_net.update_port.assert_not_called() - os_net.delete_port.assert_not_called() - - def test__trigger_return_to_pool_delete_port(self): - cls = vif_pool.NestedVIFPool - m_driver = mock.MagicMock(spec=cls) - - os_net = self.useFixture(k_fix.MockNetworkClient()).client - - cls_vif_driver = nested_vlan_vif.NestedVlanPodVIFDriver - vif_driver = mock.MagicMock(spec=cls_vif_driver) - m_driver._drv_vif = vif_driver - - pool_key = ('node_ip', 'project_id') - port_id = str(uuid.uuid4()) - pool_length = 10 - vif = mock.MagicMock() - vif.vlan_id = mock.sentinel.vlan_id - trunk_id = str(uuid.uuid4()) - - m_driver._recyclable_ports = {port_id: pool_key} - m_driver._available_ports_pools = AVAILABLE_PORTS_TYPE() - m_driver._existing_vifs = {port_id: vif} - oslo_cfg.CONF.set_override('ports_pool_max', - 10, - group='vif_pool') - port = fake.get_port_obj(port_id=port_id) - port.security_group_ids = ['security_group_modified'] - os_net.ports.return_value = [port] - m_driver._get_pool_size.return_value = pool_length - m_driver._get_trunk_id.return_value = trunk_id - m_driver._known_trunk_ids = {} - m_driver._recovered_pools = True - - cls._trigger_return_to_pool(m_driver) - - os_net.update_port.assert_not_called() - os_net.delete_port.assert_called_once_with(port_id) - m_driver._get_trunk_id.assert_called_once() - m_driver._drv_vif._remove_subport.assert_called_once_with(trunk_id, - port_id) - - def test__trigger_return_to_pool_update_exception(self): - cls = vif_pool.NestedVIFPool - m_driver = mock.MagicMock(spec=cls) - - os_net = self.useFixture(k_fix.MockNetworkClient()).client - - pool_key = ('node_ip', 'project_id') - port_id = str(uuid.uuid4()) - pool_length = 5 - - m_driver._recyclable_ports = {port_id: pool_key} - m_driver._available_ports_pools = AVAILABLE_PORTS_TYPE() - oslo_cfg.CONF.set_override('ports_pool_max', - 0, - group='vif_pool') - oslo_cfg.CONF.set_override('port_debug', - True, - group='kubernetes') - port = fake.get_port_obj(port_id=port_id) - port.security_group_ids = ['security_group_modified'] - os_net.ports.return_value = [port] - m_driver._get_pool_size.return_value = pool_length - os_net.update_port.side_effect = os_exc.SDKException - m_driver._recovered_pools = True - - cls._trigger_return_to_pool(m_driver) - - os_net.update_port.assert_called_once_with( - port_id, name=constants.KURYR_PORT_NAME) - os_net.delete_port.assert_not_called() - - def test__trigger_return_to_pool_delete_exception(self): - cls = vif_pool.NestedVIFPool - m_driver = mock.MagicMock(spec=cls) - os_net = self.useFixture(k_fix.MockNetworkClient()).client - cls_vif_driver = nested_vlan_vif.NestedVlanPodVIFDriver - vif_driver = mock.MagicMock(spec=cls_vif_driver) - m_driver._drv_vif = vif_driver - - pool_key = ('node_ip', 'project_id') - port_id = str(uuid.uuid4()) - pool_length = 10 - vif = mock.MagicMock() - vif.vlan_id = mock.sentinel.vlan_id - trunk_id = str(uuid.uuid4()) - - m_driver._recyclable_ports = {port_id: pool_key} - m_driver._available_ports_pools = AVAILABLE_PORTS_TYPE() - m_driver._existing_vifs = {port_id: vif} - oslo_cfg.CONF.set_override('ports_pool_max', - 5, - group='vif_pool') - port = fake.get_port_obj(port_id=port_id) - port.security_group_ids = ['security_group_modified'] - os_net.ports.return_value = [port] - m_driver._get_pool_size.return_value = pool_length - m_driver._get_trunk_id.return_value = trunk_id - m_driver._known_trunk_ids = {} - m_driver._recovered_pools = True - - cls._trigger_return_to_pool(m_driver) - - os_net.update_port.assert_not_called() - m_driver._get_trunk_id.assert_called_once() - m_driver._drv_vif._remove_subport.assert_called_once_with(trunk_id, - port_id) - os_net.delete_port.assert_called_once_with(port_id) - - def test__trigger_return_to_pool_delete_key_error(self): - cls = vif_pool.NestedVIFPool - m_driver = mock.MagicMock(spec=cls) - os_net = self.useFixture(k_fix.MockNetworkClient()).client - cls_vif_driver = nested_vlan_vif.NestedVlanPodVIFDriver - vif_driver = mock.MagicMock(spec=cls_vif_driver) - m_driver._drv_vif = vif_driver - - pool_key = ('node_ip', 'project_id') - port_id = str(uuid.uuid4()) - pool_length = 10 - trunk_id = str(uuid.uuid4()) - - m_driver._recyclable_ports = {port_id: pool_key} - m_driver._available_ports_pools = AVAILABLE_PORTS_TYPE() - m_driver._existing_vifs = {} - oslo_cfg.CONF.set_override('ports_pool_max', - 5, - group='vif_pool') - port = fake.get_port_obj(port_id=port_id) - port.security_group_ids = ['security_group_modified'] - os_net.ports.return_value = [port] - m_driver._get_pool_size.return_value = pool_length - m_driver._known_trunk_ids = {} - m_driver._get_trunk_id.return_value = trunk_id - m_driver._recovered_pools = True - - cls._trigger_return_to_pool(m_driver) - - os_net.update_port.assert_not_called() - m_driver._get_trunk_id.assert_called_once() - m_driver._drv_vif._remove_subport.assert_called_once_with(trunk_id, - port_id) - os_net.delete_port.assert_not_called() - - @mock.patch('kuryr_kubernetes.utils.get_subnet') - def test__get_trunk_info(self, m_get_subnet): - cls = vif_pool.NestedVIFPool - m_driver = mock.MagicMock(spec=cls) - os_net = self.useFixture(k_fix.MockNetworkClient()).client - - port_id = str(uuid.uuid4()) - trunk_port = fake.get_port_obj(port_id=port_id) - trunk_id = str(uuid.uuid4()) - trunk_details = { - 'trunk_id': trunk_id, - 'sub_ports': [{ - 'port_id': '85104e7d-8597-4bf7-94e7-a447ef0b50f1', - 'segmentation_type': 'vlan', - 'segmentation_id': 4056}]} - trunk_port.trunk_details = trunk_details - - subport_id = str(uuid.uuid4()) - subport = fake.get_port_obj(port_id=subport_id, - device_owner='trunk:subport') - os_net.ports.return_value = [trunk_port, subport] - m_driver._get_in_use_ports_info.return_value = [], {} - subnet = mock.sentinel.subnet - m_get_subnet.return_value = subnet - - exp_p_ports = {trunk_id: { - 'ip': trunk_port.fixed_ips[0]['ip_address'], - 'subports': trunk_details['sub_ports']}} - exp_subnets = {subport.fixed_ips[0]['subnet_id']: - {subport.fixed_ips[0]['subnet_id']: subnet}} - - r_p_ports, r_subports, r_subnets = cls._get_trunks_info(m_driver) - - self.assertEqual(r_p_ports, exp_p_ports) - self.assertDictEqual(r_subports[subport_id].to_dict(), - subport.to_dict()) - self.assertEqual(r_subnets, exp_subnets) - - def test__get_trunk_info_empty(self): - cls = vif_pool.NestedVIFPool - m_driver = mock.MagicMock(spec=cls) - os_net = self.useFixture(k_fix.MockNetworkClient()).client - - os_net.ports.return_value = [] - m_driver._get_in_use_ports_info.return_value = [], {} - - r_p_ports, r_subports, r_subnets = cls._get_trunks_info(m_driver) - - self.assertEqual(r_p_ports, {}) - self.assertEqual(r_subports, {}) - self.assertEqual(r_subnets, {}) - - def test__get_trunk_info_no_trunk_details(self): - cls = vif_pool.NestedVIFPool - m_driver = mock.MagicMock(spec=cls) - os_net = self.useFixture(k_fix.MockNetworkClient()).client - - port_id = str(uuid.uuid4()) - port = fake.get_port_obj(port_id=port_id, device_owner='compute:nova') - os_net.ports.return_value = [port] - m_driver._get_in_use_ports_info.return_value = [], {} - - r_p_ports, r_subports, r_subnets = cls._get_trunks_info(m_driver) - - self.assertEqual(r_p_ports, {}) - self.assertEqual(r_subports, {}) - self.assertEqual(r_subnets, {}) - - @mock.patch('kuryr_kubernetes.os_vif_util.' - 'neutron_to_osvif_vif_nested_vlan') - def test__precreated_ports_recover(self, m_to_osvif): - cls = vif_pool.NestedVIFPool - m_driver = mock.MagicMock(spec=cls) - - os_net = self.useFixture(k_fix.MockNetworkClient()).client - - m_driver._available_ports_pools = AVAILABLE_PORTS_TYPE() - m_driver._existing_vifs = {} - - oslo_cfg.CONF.set_override('port_debug', - True, - group='kubernetes') - port_id = str(uuid.uuid4()) - trunk_id = str(uuid.uuid4()) - trunk_obj = self._get_trunk_obj(port_id=trunk_id, subport_id=port_id) - port = fake.get_port_obj(port_id=port_id, device_owner='trunk:subport') - - p_ports = self._get_parent_ports([trunk_obj]) - a_subports = {port_id: port} - subnet_id = port.fixed_ips[0]['subnet_id'] - net_id = str(uuid.uuid4()) - _net = os_network.Network( - id=net_id, - name=None, - mtu=None, - provider_network_type=None, - ) - network = ovu.neutron_to_osvif_network(_net) - subnets = {subnet_id: {subnet_id: network}} - m_driver._get_trunks_info.return_value = (p_ports, a_subports, - subnets) - - vif = mock.sentinel.vif - m_to_osvif.return_value = vif - - pool_key = (port.binding_host_id, port.project_id, net_id) - m_driver._get_pool_key.return_value = pool_key - - cls._precreated_ports(m_driver, 'recover') - - m_driver._get_trunks_info.assert_called_once() - self.assertEqual(m_driver._existing_vifs[port_id], vif) - self.assertEqual(m_driver._available_ports_pools[pool_key], - {tuple(port.security_group_ids): [port_id]}) - os_net.delete_port.assert_not_called() - - @mock.patch('kuryr_kubernetes.os_vif_util.' - 'neutron_to_osvif_vif_nested_vlan') - def test__precreated_ports_recover_plus_port_cleanup(self, m_to_osvif): - cls = vif_pool.NestedVIFPool - m_driver = mock.MagicMock(spec=cls) - os_net = self.useFixture(k_fix.MockNetworkClient()).client - - m_driver._available_ports_pools = AVAILABLE_PORTS_TYPE() - m_driver._existing_vifs = {} - - oslo_cfg.CONF.set_override('port_debug', - True, - group='kubernetes') - - port_id = str(uuid.uuid4()) - trunk_id = str(uuid.uuid4()) - trunk_obj = self._get_trunk_obj(port_id=trunk_id, subport_id=port_id) - port = fake.get_port_obj(port_id=port_id, device_owner='trunk:subport') - port_to_delete_id = str(uuid.uuid4()) - port_to_delete = fake.get_port_obj(port_id=port_to_delete_id, - device_owner='trunk:subport') - - p_ports = self._get_parent_ports([trunk_obj]) - a_subports = {port_id: port, port_to_delete_id: port_to_delete} - subnet_id = port.fixed_ips[0]['subnet_id'] - net_id = str(uuid.uuid4()) - _net = os_network.Network( - id=net_id, - name=None, - mtu=None, - provider_network_type=None, - ) - network = ovu.neutron_to_osvif_network(_net) - subnets = {subnet_id: {subnet_id: network}} - m_driver._get_trunks_info.return_value = (p_ports, a_subports, - subnets) - - vif = mock.sentinel.vif - m_to_osvif.return_value = vif - - pool_key = (port.binding_host_id, port.project_id, net_id) - m_driver._get_pool_key.return_value = pool_key - - cls._precreated_ports(m_driver, 'recover') - - m_driver._get_trunks_info.assert_called_once() - self.assertEqual(m_driver._existing_vifs[port_id], vif) - self.assertEqual(m_driver._available_ports_pools[pool_key], - {tuple(port.security_group_ids): [port_id]}) - os_net.delete_port.assert_called_with(port_to_delete_id) - - def test__precreated_ports_free(self): - cls = vif_pool.NestedVIFPool - m_driver = mock.MagicMock(spec=cls) - os_net = self.useFixture(k_fix.MockNetworkClient()).client - cls_vif_driver = nested_vlan_vif.NestedVlanPodVIFDriver - vif_driver = mock.MagicMock(spec=cls_vif_driver) - m_driver._drv_vif = vif_driver - - oslo_cfg.CONF.set_override('port_debug', - True, - group='kubernetes') - - port_id = str(uuid.uuid4()) - trunk_id = str(uuid.uuid4()) - trunk_obj = self._get_trunk_obj(port_id=trunk_id, subport_id=port_id) - port = fake.get_port_obj(port_id=port_id, - device_owner='trunk:subport') - - p_ports = self._get_parent_ports([trunk_obj]) - a_subports = {port_id: port} - subnet_id = port.fixed_ips[0]['subnet_id'] - net_id = str(uuid.uuid4()) - _net = os_network.Network( - id=net_id, - name=None, - mtu=None, - provider_network_type=None, - ) - network = ovu.neutron_to_osvif_network(_net) - subnets = {subnet_id: {subnet_id: network}} - m_driver._get_trunks_info.return_value = (p_ports, a_subports, - subnets) - - pool_key = (port.binding_host_id, port.project_id, net_id) - m_driver._get_pool_key.return_value = pool_key - m_driver._available_ports_pools = AVAILABLE_PORTS_TYPE() - m_driver._available_ports_pools[pool_key][ - tuple(port.security_group_ids)] = [port_id] - m_driver._existing_vifs = {port_id: mock.sentinel.vif} - - cls._precreated_ports(m_driver, 'free') - - m_driver._get_trunks_info.assert_called_once() - m_driver._drv_vif._remove_subport.assert_called_once() - os_net.delete_port.assert_called_once() - m_driver._drv_vif._release_vlan_id.assert_called_once() - - self.assertEqual(m_driver._existing_vifs, {}) - self.assertEqual(m_driver._available_ports_pools[pool_key][tuple( - port.security_group_ids)], []) - - @mock.patch('kuryr_kubernetes.os_vif_util.' - 'neutron_to_osvif_vif_nested_vlan') - def test__precreated_ports_recover_several_trunks(self, m_to_osvif): - cls = vif_pool.NestedVIFPool - m_driver = mock.MagicMock(spec=cls) - - os_net = self.useFixture(k_fix.MockNetworkClient()).client - - m_driver._available_ports_pools = AVAILABLE_PORTS_TYPE() - m_driver._existing_vifs = {} - - oslo_cfg.CONF.set_override('port_debug', - True, - group='kubernetes') - - port_id1 = str(uuid.uuid4()) - trunk_id1 = str(uuid.uuid4()) - - port_id2 = str(uuid.uuid4()) - trunk_id2 = str(uuid.uuid4()) - - trunk_obj1 = self._get_trunk_obj(port_id=trunk_id1, - subport_id=port_id1) - trunk_obj2 = self._get_trunk_obj(port_id=trunk_id2, - subport_id=port_id2, - trunk_id=str(uuid.uuid4())) - - port1 = fake.get_port_obj(port_id=port_id1, - device_owner='trunk:subport') - port2 = fake.get_port_obj(port_id=port_id2, - device_owner='trunk:subport') - - p_ports = self._get_parent_ports([trunk_obj1, trunk_obj2]) - a_subports = {port_id1: port1, port_id2: port2} - subnet_id = port1.fixed_ips[0]['subnet_id'] - net_id = str(uuid.uuid4()) - _net = os_network.Network( - id=net_id, - name=None, - mtu=None, - provider_network_type=None, - ) - network = ovu.neutron_to_osvif_network(_net) - subnets = {subnet_id: {subnet_id: network}} - - m_driver._get_trunks_info.return_value = (p_ports, a_subports, - subnets) - vif = mock.sentinel.vif - m_to_osvif.return_value = vif - - cls._precreated_ports(m_driver, 'recover') - - m_driver._get_trunks_info.assert_called_once() - self.assertEqual(m_driver._existing_vifs, {port_id1: vif, - port_id2: vif}) - os_net.delete_port.assert_not_called() - - @mock.patch('kuryr_kubernetes.os_vif_util.' - 'neutron_to_osvif_vif_nested_vlan') - def test__precreated_ports_recover_several_subports(self, m_to_osvif): - cls = vif_pool.NestedVIFPool - m_driver = mock.MagicMock(spec=cls) - - os_net = self.useFixture(k_fix.MockNetworkClient()).client - - m_driver._available_ports_pools = AVAILABLE_PORTS_TYPE() - m_driver._existing_vifs = {} - - oslo_cfg.CONF.set_override('port_debug', - True, - group='kubernetes') - - port_id1 = str(uuid.uuid4()) - port_id2 = str(uuid.uuid4()) - trunk_id = str(uuid.uuid4()) - trunk_obj = self._get_trunk_obj(port_id=trunk_id, - subport_id=port_id1) - trunk_obj['sub_ports'].append({'port_id': port_id2, - 'segmentation_type': 'vlan', - 'segmentation_id': 101}) - port1 = fake.get_port_obj(port_id=port_id1, - device_owner='trunk:subport') - port2 = fake.get_port_obj(port_id=port_id2, - device_owner='trunk:subport') - - p_ports = self._get_parent_ports([trunk_obj]) - a_subports = {port_id1: port1, port_id2: port2} - subnet_id = port1.fixed_ips[0]['subnet_id'] - net_id = str(uuid.uuid4()) - _net = os_network.Network( - id=net_id, - name=None, - mtu=None, - provider_network_type=None, - ) - network = ovu.neutron_to_osvif_network(_net) - subnets = {subnet_id: {subnet_id: network}} - - m_driver._get_trunks_info.return_value = (p_ports, a_subports, - subnets) - - vif = mock.sentinel.vif - m_to_osvif.return_value = vif - - pool_key = (port1.binding_host_id, port1.project_id, net_id) - m_driver._get_pool_key.return_value = pool_key - cls._precreated_ports(m_driver, 'recover') - - m_driver._get_trunks_info.assert_called_once() - self.assertEqual(m_driver._existing_vifs, {port_id1: vif, - port_id2: vif}) - self.assertEqual(m_driver._available_ports_pools[pool_key], - {tuple(port1.security_group_ids): [port_id1, - port_id2]}) - os_net.delete_port.assert_not_called() - - @ddt.data(('recover'), ('free')) - def test__precreated_ports_no_ports(self, m_action): - cls = vif_pool.NestedVIFPool - m_driver = mock.MagicMock(spec=cls) - - os_net = self.useFixture(k_fix.MockNetworkClient()).client - - oslo_cfg.CONF.set_override('port_debug', - True, - group='kubernetes') - m_driver._available_ports_pools = AVAILABLE_PORTS_TYPE() - m_driver._existing_vifs = {} - - port_id = mock.sentinel.port_id - trunk_id = mock.sentinel.trunk_id - trunk_obj = self._get_trunk_obj(port_id=trunk_id, subport_id=port_id) - - p_ports = self._get_parent_ports([trunk_obj]) - a_subports = {} - subnets = {} - m_driver._get_trunks_info.return_value = (p_ports, a_subports, - subnets) - - cls._precreated_ports(m_driver, m_action) - - m_driver._get_trunks_info.assert_called_once() - self.assertEqual(m_driver._existing_vifs, {}) - self.assertEqual(m_driver._available_ports_pools, {}) - os_net.delete_port.assert_not_called() - - @ddt.data(('recover'), ('free')) - def test__precreated_ports_no_trunks(self, m_action): - cls = vif_pool.NestedVIFPool - m_driver = mock.MagicMock(spec=cls) - - os_net = self.useFixture(k_fix.MockNetworkClient()).client - - m_driver._available_ports_pools = AVAILABLE_PORTS_TYPE() - m_driver._existing_vifs = {} - oslo_cfg.CONF.set_override('port_debug', - True, - group='kubernetes') - - port_id = str(uuid.uuid4()) - port = fake.get_port_obj(port_id=port_id, - device_owner='trunk:subport') - - p_ports = {} - a_subports = {} - subnet_id = port.fixed_ips[0]['subnet_id'] - subnet = mock.sentinel.subnet - subnets = {subnet_id: {subnet_id: subnet}} - m_driver._get_trunks_info.return_value = (p_ports, a_subports, - subnets) - cls._precreated_ports(m_driver, m_action) - m_driver._get_trunks_info.assert_called_once() - self.assertEqual(m_driver._existing_vifs, {}) - self.assertEqual(m_driver._available_ports_pools, {}) - os_net.delete_port.assert_not_called() - - @mock.patch('eventlet.GreenPool') - def test_delete_network_pools(self, m_green_pool): - cls = vif_pool.NestedVIFPool - m_driver = mock.MagicMock(spec=cls) - cls_vif_driver = nested_vlan_vif.NestedVlanPodVIFDriver - vif_driver = mock.MagicMock(spec=cls_vif_driver) - m_driver._drv_vif = vif_driver - m_pool = mock.MagicMock() - m_green_pool.return_value = m_pool - - net_id = mock.sentinel.net_id - pool_key = ('node_ip', 'project_id') - port_id = str(uuid.uuid4()) - trunk_id = str(uuid.uuid4()) - vif = mock.MagicMock() - vlan_id = mock.sentinel.vlan_id - vif.vlan_id = vlan_id - m_driver._available_ports_pools = AVAILABLE_PORTS_TYPE() - m_driver._available_ports_pools[pool_key][('sg',)] = [port_id] - m_driver._lock = threading.Lock() - m_driver._populate_pool_lock = { - pool_key: mock.MagicMock(spec=threading.Lock())} - m_driver._existing_vifs = {port_id: vif} - m_driver._recovered_pools = True - - m_driver._get_trunk_id.return_value = trunk_id - m_driver._get_pool_key_net.return_value = net_id - - cls.delete_network_pools(m_driver, net_id) - - m_driver._trigger_return_to_pool.assert_called_once() - m_driver._get_pool_key_net.assert_called_once() - m_driver._get_trunk_id.assert_called_once_with(pool_key) - m_driver._drv_vif._remove_subports.assert_called_once_with(trunk_id, - [port_id]) - m_driver._drv_vif._release_vlan_id.assert_called_once_with(vlan_id) - m_pool.imap.assert_called_once_with(utils.delete_neutron_port, - [port_id]) - - def test_delete_network_pools_not_ready(self): - cls = vif_pool.NestedVIFPool - m_driver = mock.MagicMock(spec=cls) - cls_vif_driver = nested_vlan_vif.NestedVlanPodVIFDriver - vif_driver = mock.MagicMock(spec=cls_vif_driver) - m_driver._drv_vif = vif_driver - os_net = self.useFixture(k_fix.MockNetworkClient()).client - - net_id = mock.sentinel.net_id - m_driver._recovered_pools = False - - self.assertRaises(exceptions.ResourceNotReady, - cls.delete_network_pools, m_driver, net_id) - - m_driver._trigger_return_to_pool.assert_not_called() - m_driver._get_pool_key_net.assert_not_called() - m_driver._get_trunk_id.assert_not_called() - m_driver._drv_vif._remove_subports.assert_not_called() - os_net.delete_port.assert_not_called() - - def test_delete_network_pools_exception(self): - cls = vif_pool.NestedVIFPool - m_driver = mock.MagicMock(spec=cls) - cls_vif_driver = nested_vlan_vif.NestedVlanPodVIFDriver - vif_driver = mock.MagicMock(spec=cls_vif_driver) - m_driver._drv_vif = vif_driver - - os_net = self.useFixture(k_fix.MockNetworkClient()).client - - net_id = mock.sentinel.net_id - pool_key = ('node_ip', 'project_id') - port_id = str(uuid.uuid4()) - trunk_id = str(uuid.uuid4()) - vif = mock.MagicMock() - vlan_id = mock.sentinel.vlan_id - vif.vlan_id = vlan_id - m_driver._available_ports_pools = AVAILABLE_PORTS_TYPE() - m_driver._available_ports_pools[pool_key][('sg',)] = [port_id] - m_driver._existing_vifs = {port_id: vif} - m_driver._recovered_pools = True - - m_driver._get_trunk_id.return_value = trunk_id - m_driver._get_pool_key_net.return_value = net_id - m_driver._drv_vif._remove_subports.side_effect = os_exc.SDKException - - self.assertRaises(exceptions.ResourceNotReady, - cls.delete_network_pools, m_driver, net_id) - - m_driver._trigger_return_to_pool.assert_called_once() - m_driver._get_pool_key_net.assert_called_once() - m_driver._get_trunk_id.assert_called_once_with(pool_key) - m_driver._drv_vif._remove_subports.assert_called_once_with(trunk_id, - [port_id]) - m_driver._drv_vif._release_vlan_id.assert_not_called() - os_net.delete_port.assert_not_called() - - @mock.patch('eventlet.GreenPool') - def test_delete_network_pools_missing_port(self, m_green_pool): - cls = vif_pool.NestedVIFPool - m_driver = mock.MagicMock(spec=cls) - cls_vif_driver = nested_vlan_vif.NestedVlanPodVIFDriver - vif_driver = mock.MagicMock(spec=cls_vif_driver) - m_driver._drv_vif = vif_driver - m_pool = mock.MagicMock() - m_green_pool.return_value = m_pool - - net_id = mock.sentinel.net_id - pool_key = ('node_ip', 'project_id') - port_id = str(uuid.uuid4()) - trunk_id = str(uuid.uuid4()) - vif = mock.MagicMock() - vlan_id = mock.sentinel.vlan_id - vif.vlan_id = vlan_id - m_driver._available_ports_pools = AVAILABLE_PORTS_TYPE() - m_driver._available_ports_pools[pool_key][('sg',)] = [port_id] - m_driver._lock = threading.Lock() - m_driver._populate_pool_lock = { - pool_key: mock.MagicMock(spec=threading.Lock())} - m_driver._existing_vifs = {} - m_driver._recovered_pools = True - - m_driver._get_trunk_id.return_value = trunk_id - m_driver._get_pool_key_net.return_value = net_id - - cls.delete_network_pools(m_driver, net_id) - - m_driver._trigger_return_to_pool.assert_called_once() - m_driver._get_pool_key_net.assert_called_once() - m_driver._get_trunk_id.assert_called_once_with(pool_key) - m_driver._drv_vif._remove_subports.assert_called_once_with(trunk_id, - [port_id]) - m_driver._drv_vif._release_vlan_id.assert_not_called() - m_pool.imap.assert_called_once_with(utils.delete_neutron_port, - [port_id]) diff --git a/kuryr_kubernetes/tests/unit/controller/handlers/__init__.py b/kuryr_kubernetes/tests/unit/controller/handlers/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/kuryr_kubernetes/tests/unit/controller/handlers/test_fake_handler.py b/kuryr_kubernetes/tests/unit/controller/handlers/test_fake_handler.py deleted file mode 100644 index 94bae4811..000000000 --- a/kuryr_kubernetes/tests/unit/controller/handlers/test_fake_handler.py +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright (c) 2018 RedHat, 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. -from kuryr_kubernetes.handlers import k8s_base - - -class TestHandler(k8s_base.ResourceEventHandler): - - OBJECT_KIND = 'DUMMY' - OBJECT_WATCH_PATH = 'DUMMY_PATH' - - def __init__(self): - super(TestHandler, self).__init__() diff --git a/kuryr_kubernetes/tests/unit/controller/handlers/test_kuryrnetwork.py b/kuryr_kubernetes/tests/unit/controller/handlers/test_kuryrnetwork.py deleted file mode 100644 index c67bf6c91..000000000 --- a/kuryr_kubernetes/tests/unit/controller/handlers/test_kuryrnetwork.py +++ /dev/null @@ -1,395 +0,0 @@ -# Copyright 2020 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 unittest import mock - -from openstack.network.v2 import network as os_network -from openstack.network.v2 import subnet as os_subnet -from oslo_config import cfg as oslo_cfg - -from kuryr_kubernetes.controller.drivers import base as drivers -from kuryr_kubernetes.controller.drivers import namespace_subnet as subnet_drv -from kuryr_kubernetes.controller.drivers import utils as driver_utils -from kuryr_kubernetes.controller.drivers import vif_pool -from kuryr_kubernetes.controller.handlers import kuryrnetwork -from kuryr_kubernetes import exceptions as k_exc -from kuryr_kubernetes.tests import base as test_base -from kuryr_kubernetes.tests.unit import kuryr_fixtures as k_fix - - -@mock.patch('kuryr_kubernetes.controller.drivers.utils.' - 'is_network_policy_enabled', mock.Mock(return_value=True)) -class TestKuryrNetworkHandler(test_base.TestCase): - - def setUp(self): - super(TestKuryrNetworkHandler, self).setUp() - - self._project_id = mock.sentinel.project_id - self._subnets = mock.sentinel.subnets - self._kuryrnet_crd = { - 'metadata': { - 'name': 'ns-test-namespace', - }, - 'spec': { - 'nsName': 'test-namespace', - 'projectId': 'test-project', - 'nsLabels': {}, - }, - 'status': { - } - } - - self._handler = mock.MagicMock( - spec=kuryrnetwork.KuryrNetworkHandler) - self._handler._drv_project = mock.Mock(spec=drivers.PodProjectDriver) - # NOTE(ltomasbo): The KuryrNetwork handler is associated to the usage - # of namespace subnet driver, - self._handler._drv_subnets = mock.Mock( - spec=subnet_drv.NamespacePodSubnetDriver) - self._handler._drv_sg = mock.Mock(spec=drivers.PodSecurityGroupsDriver) - self._handler._drv_vif_pool = mock.MagicMock( - spec=vif_pool.MultiVIFPool) - self._handler.k8s = self.useFixture(k_fix.MockK8sClient()).client - - self._get_project = self._handler._drv_project.get_project - self._set_vif_driver = self._handler._drv_vif_pool.set_vif_driver - self._create_network = self._handler._drv_subnets.create_network - self._create_subnet = self._handler._drv_subnets.create_subnet - self._delete_namespace_subnet = ( - self._handler._drv_subnets.delete_namespace_subnet) - self._add_subnet_to_router = ( - self._handler._drv_subnets.add_subnet_to_router) - self._delete_ns_sg_rules = ( - self._handler._drv_sg.delete_namespace_sg_rules) - self._update_ns_sg_rules = ( - self._handler._drv_sg.update_namespace_sg_rules) - self._delete_network_pools = ( - self._handler._drv_vif_pool.delete_network_pools) - - self._get_project.return_value = self._project_id - - @mock.patch.object(drivers.LBaaSDriver, 'get_instance') - @mock.patch.object(drivers.VIFPoolDriver, 'get_instance') - @mock.patch.object(drivers.PodSecurityGroupsDriver, 'get_instance') - @mock.patch.object(drivers.PodSubnetsDriver, 'get_instance') - @mock.patch.object(drivers.NamespaceProjectDriver, 'get_instance') - def test_init(self, m_get_project_driver, m_get_subnet_driver, - m_get_sg_driver, m_get_vif_pool_driver, m_get_lbaas_driver): - project_driver = mock.sentinel.project_driver - subnet_driver = mock.sentinel.subnet_driver - sg_driver = mock.sentinel.sg_driver - vif_pool_driver = mock.Mock(spec=vif_pool.MultiVIFPool) - lbaas_driver = mock.sentinel.lbaas_driver - - m_get_project_driver.return_value = project_driver - m_get_subnet_driver.return_value = subnet_driver - m_get_sg_driver.return_value = sg_driver - m_get_vif_pool_driver.return_value = vif_pool_driver - m_get_lbaas_driver.return_value = lbaas_driver - - handler = kuryrnetwork.KuryrNetworkHandler() - - self.assertEqual(project_driver, handler._drv_project) - self.assertEqual(subnet_driver, handler._drv_subnets) - self.assertEqual(sg_driver, handler._drv_sg) - self.assertEqual(vif_pool_driver, handler._drv_vif_pool) - - @mock.patch.object(driver_utils, 'get_services') - @mock.patch.object(driver_utils, 'get_namespace') - def test_on_present(self, m_get_ns, m_get_svc): - net_id = mock.sentinel.net_id - subnet_id = mock.sentinel.subnet_id - subnet_cidr = mock.sentinel.subnet_cidr - router_id = mock.sentinel.router_id - ns = {'metadata': {'uid': 'e28127f4-bc41-450a-97cf-56d9bc76d53e', - 'name': 'test'}} - - self._create_network.return_value = net_id - self._create_subnet.return_value = (subnet_id, subnet_cidr) - self._add_subnet_to_router.return_value = router_id - m_get_ns.return_value = ns - m_get_svc.return_value = [] - - kuryrnetwork.KuryrNetworkHandler.on_present(self._handler, - self._kuryrnet_crd) - - self._handler._patch_kuryrnetwork_crd.assert_called() - self._create_network.assert_called_once_with( - ns, - self._kuryrnet_crd['spec']['projectId']) - self._create_subnet.assert_called_once_with( - ns, - self._kuryrnet_crd['spec']['projectId'], - net_id) - self._add_subnet_to_router.assert_called_once_with(subnet_id) - m_get_ns.assert_called_once_with(self._kuryrnet_crd['spec']['nsName']) - self._update_ns_sg_rules.assert_called_once_with(ns) - m_get_svc.assert_called_once() - self._handler._update_services.assert_called_once() - self.assertEqual(self._handler.k8s.add_event.call_count, 4) - - @mock.patch.object(driver_utils, 'get_services') - @mock.patch.object(driver_utils, 'get_namespace') - def test_on_present_no_sg_enforce(self, m_get_ns, m_get_svc): - net_id = mock.sentinel.net_id - subnet_id = mock.sentinel.subnet_id - subnet_cidr = mock.sentinel.subnet_cidr - router_id = mock.sentinel.router_id - ns = {'metadata': {'uid': '843645f7-d255-4fd8-86fb-09140d57c392', - 'name': 'test'}} - - self._create_network.return_value = net_id - self._create_subnet.return_value = (subnet_id, subnet_cidr) - self._add_subnet_to_router.return_value = router_id - m_get_ns.return_value = ns - - oslo_cfg.CONF.set_override('enforce_sg_rules', - False, - group='octavia_defaults') - self.addCleanup(oslo_cfg.CONF.clear_override, 'enforce_sg_rules', - group='octavia_defaults') - - kuryrnetwork.KuryrNetworkHandler.on_present(self._handler, - self._kuryrnet_crd) - - self._handler._patch_kuryrnetwork_crd.assert_called() - self._create_network.assert_called_once_with( - ns, - self._kuryrnet_crd['spec']['projectId']) - self._create_subnet.assert_called_once_with( - ns, - self._kuryrnet_crd['spec']['projectId'], - net_id) - self._add_subnet_to_router.assert_called_once_with(subnet_id) - m_get_ns.assert_called_once_with(self._kuryrnet_crd['spec']['nsName']) - self._update_ns_sg_rules.assert_called_once_with(ns) - m_get_svc.assert_not_called() - self._handler._update_services.assert_not_called() - self.assertEqual(self._handler.k8s.add_event.call_count, 4) - - @mock.patch.object(driver_utils, 'get_namespace') - def test_on_present_existing(self, m_get_ns): - net_id = mock.sentinel.net_id - subnet_id = mock.sentinel.subnet_id - subnet_cidr = mock.sentinel.subnet_cidr - router_id = mock.sentinel.router_id - - kns_crd = self._kuryrnet_crd.copy() - kns_crd['status'] = { - 'netId': net_id, - 'subnetId': subnet_id, - 'subnetCIDR': subnet_cidr, - 'routerId': router_id} - - kuryrnetwork.KuryrNetworkHandler.on_present(self._handler, kns_crd) - - self._handler._patch_kuryrnetwork_crd.assert_not_called() - self._create_network.assert_not_called() - self._create_subnet.assert_not_called() - self._add_subnet_to_router.assert_not_called() - self._handler.k8s.add_event.assert_not_called() - - @mock.patch.object(driver_utils, 'get_services') - def test_on_finalize(self, m_get_svc): - net_id = mock.sentinel.net_id - kns_crd = self._kuryrnet_crd.copy() - kns_crd['status'] = {'netId': net_id} - crd_selector = mock.sentinel.crd_selector - - self._delete_ns_sg_rules.return_value = [crd_selector] - m_get_svc.return_value = [] - - kuryrnetwork.KuryrNetworkHandler.on_finalize(self._handler, kns_crd) - - self._delete_network_pools.assert_called_once_with(net_id) - self._delete_namespace_subnet.assert_called_once_with(kns_crd) - self._delete_ns_sg_rules.assert_called_once() - m_get_svc.assert_called_once() - self._handler._update_services.assert_called_once() - self._handler.k8s.remove_finalizer.assert_called_once() - self._handler.k8s.add_event.assert_not_called() - - @mock.patch('kuryr_kubernetes.clients.get_network_client') - @mock.patch.object(driver_utils, 'get_services') - def test_on_finalize_no_network(self, m_get_svc, m_get_net_client): - crd_selector = mock.sentinel.crd_selector - self._delete_ns_sg_rules.return_value = [crd_selector] - m_get_svc.return_value = [] - net_mock = mock.MagicMock() - net_mock.return_value = [] - m_get_net_client.return_value = net_mock - self._handler.k8s.get.return_value = ( - {'metadata': {'name': 'test-namespace', - 'uid': 'e237acaf-ea5f-4d96-b604-292268a938a1'}}) - - kuryrnetwork.KuryrNetworkHandler.on_finalize(self._handler, - self._kuryrnet_crd) - - self._delete_network_pools.assert_not_called() - self._delete_namespace_subnet.assert_not_called() - self._delete_ns_sg_rules.assert_called_once() - m_get_svc.assert_called_once() - self._handler._update_services.assert_called_once() - self._handler.k8s.remove_finalizer.assert_called_once() - self._handler.k8s.add_event.assert_not_called() - - @mock.patch('kuryr_kubernetes.clients.get_network_client') - @mock.patch.object(driver_utils, 'get_services') - def test_on_finalize_no_network_in_kn_no_desc(self, m_get_svc, - m_get_net_client): - crd_selector = mock.sentinel.crd_selector - self._delete_ns_sg_rules.return_value = [crd_selector] - m_get_svc.return_value = [] - net_mock = mock.MagicMock() - net_id = '1612ffb1-ff7d-4590-bd9c-95aeb043705a' - net_mock.networks.return_value = [ - os_network.Network(id=net_id, description=''), - ] - m_get_net_client.return_value = net_mock - self._handler.k8s.get.return_value = ( - {'metadata': {'name': 'test-namespace', 'uid': net_id}}) - - kuryrnetwork.KuryrNetworkHandler.on_finalize(self._handler, - self._kuryrnet_crd) - - self._delete_network_pools.assert_not_called() - self._delete_ns_sg_rules.assert_called_once() - m_get_svc.assert_called_once() - self._handler._update_services.assert_called_once() - self._handler.k8s.remove_finalizer.assert_called_once() - self._handler.k8s.add_event.assert_not_called() - - @mock.patch('kuryr_kubernetes.clients.get_network_client') - @mock.patch.object(driver_utils, 'get_services') - def test_on_finalize_no_network_in_kn_with_subnet(self, m_get_svc, - m_get_net_client): - crd_selector = mock.sentinel.crd_selector - self._delete_ns_sg_rules.return_value = [crd_selector] - m_get_svc.return_value = [] - net_id = 'db0b2c83-dae3-47ff-b4b0-9a19b7c15589' - ns_id = '0b6d6f0b-4e44-4a1b-a711-71ab6c79bee8' - subnet_id = 'a595fc4b-6885-48ff-b90c-d3f7aefd6d1a' - net_mock = mock.MagicMock() - net_mock.networks.return_value = [ - os_network.Network(id=net_id, description=ns_id), - ] - net_mock.subnets.return_value = [os_subnet.Subnet(id=subnet_id)] - m_get_net_client.return_value = net_mock - self._handler.k8s.get.return_value = ( - {'metadata': {'name': 'test-namespace', 'uid': ns_id}}) - - kuryrnetwork.KuryrNetworkHandler.on_finalize(self._handler, - self._kuryrnet_crd) - - self._delete_network_pools.assert_called_once() - self._delete_ns_sg_rules.assert_called_once() - m_get_svc.assert_called_once() - self._handler._update_services.assert_called_once() - self._handler.k8s.remove_finalizer.assert_called_once() - self._handler.k8s.add_event.assert_not_called() - - @mock.patch('kuryr_kubernetes.clients.get_network_client') - @mock.patch.object(driver_utils, 'get_services') - def test_on_finalize_no_network_in_kn_with_no_ns_match(self, m_get_svc, - m_get_net_client): - crd_selector = mock.sentinel.crd_selector - self._delete_ns_sg_rules.return_value = [crd_selector] - m_get_svc.return_value = [] - net_id = 'db0b2c83-dae3-47ff-b4b0-9a19b7c15589' - ns_id = '0b6d6f0b-4e44-4a1b-a711-71ab6c79bee8' - subnet_id = 'a595fc4b-6885-48ff-b90c-d3f7aefd6d1a' - net_mock = mock.MagicMock() - net_mock.networks.return_value = [ - os_network.Network(id=net_id, description=ns_id), - ] - net_mock.subnets.return_value = [os_subnet.Subnet(id=subnet_id)] - m_get_net_client.return_value = net_mock - self._handler.k8s.get.return_value = ( - {'metadata': {'name': 'test-namespace', 'uid': net_id}}) - - kuryrnetwork.KuryrNetworkHandler.on_finalize(self._handler, - self._kuryrnet_crd) - - self._delete_network_pools.assert_not_called() - m_get_svc.assert_called_once() - self._handler._update_services.assert_called_once() - self._handler.k8s.remove_finalizer.assert_called_once() - self._handler.k8s.add_event.assert_not_called() - - @mock.patch.object(driver_utils, 'get_services') - def test_on_finalize_no_sg_enforce(self, m_get_svc): - net_id = mock.sentinel.net_id - kns_crd = self._kuryrnet_crd.copy() - kns_crd['status'] = {'netId': net_id} - crd_selector = mock.sentinel.crd_selector - - self._delete_ns_sg_rules.return_value = [crd_selector] - m_get_svc.return_value = [] - oslo_cfg.CONF.set_override('enforce_sg_rules', - False, - group='octavia_defaults') - self.addCleanup(oslo_cfg.CONF.clear_override, 'enforce_sg_rules', - group='octavia_defaults') - - kuryrnetwork.KuryrNetworkHandler.on_finalize( - self._handler, kns_crd) - - self._delete_network_pools.assert_called_once_with(net_id) - self._delete_namespace_subnet.assert_called_once_with(kns_crd) - self._delete_ns_sg_rules.assert_called_once() - m_get_svc.assert_not_called() - self._handler._update_services.assert_not_called() - self._handler.k8s.remove_finalizer.assert_called_once() - self._handler.k8s.add_event.assert_not_called() - - def test_on_finalize_finalizer_delete_ns_subnet_exception(self): - net_id = mock.sentinel.net_id - kns_crd = self._kuryrnet_crd.copy() - kns_crd['status'] = {'netId': net_id} - crd_selector = mock.sentinel.crd_selector - - self._delete_ns_sg_rules.return_value = [crd_selector] - self._delete_namespace_subnet.side_effect = (k_exc. - ResourceNotReady(kns_crd)) - - self.assertRaises(k_exc.ResourceNotReady, - kuryrnetwork.KuryrNetworkHandler.on_finalize, - self._handler, kns_crd) - - self._delete_network_pools.assert_called_once_with(net_id) - self._delete_namespace_subnet.assert_called_once_with(kns_crd) - - @mock.patch.object(driver_utils, 'get_services') - def test_on_finalize_finalizer_exception(self, m_get_svc): - net_id = mock.sentinel.net_id - kns_crd = self._kuryrnet_crd.copy() - kns_crd['status'] = {'netId': net_id} - crd_selector = mock.sentinel.crd_selector - - self._delete_ns_sg_rules.return_value = [crd_selector] - m_get_svc.return_value = [] - k8s = self._handler.k8s - k8s.remove_finalizer.side_effect = k_exc.K8sClientException - - self.assertRaises( - k_exc.K8sClientException, - kuryrnetwork.KuryrNetworkHandler.on_finalize, - self._handler, kns_crd) - - self._delete_network_pools.assert_called_once_with(net_id) - self._delete_namespace_subnet.assert_called_once_with(kns_crd) - self._delete_ns_sg_rules.assert_called_once() - m_get_svc.assert_called_once() - self._handler._update_services.assert_called_once() - k8s.remove_finalizer.assert_called_once() diff --git a/kuryr_kubernetes/tests/unit/controller/handlers/test_kuryrnetwork_population.py b/kuryr_kubernetes/tests/unit/controller/handlers/test_kuryrnetwork_population.py deleted file mode 100644 index 84492c0be..000000000 --- a/kuryr_kubernetes/tests/unit/controller/handlers/test_kuryrnetwork_population.py +++ /dev/null @@ -1,112 +0,0 @@ -# Copyright 2020, 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 unittest import mock - -from kuryr_kubernetes.controller.drivers import base as drivers -from kuryr_kubernetes.controller.drivers import namespace_subnet as subnet_drv -from kuryr_kubernetes.controller.drivers import node_subnets -from kuryr_kubernetes.controller.drivers import utils as driver_utils -from kuryr_kubernetes.controller.drivers import vif_pool -from kuryr_kubernetes.controller.handlers import kuryrnetwork_population -from kuryr_kubernetes.tests import base as test_base -from kuryr_kubernetes import utils - - -class TestKuryrNetworkPopulationHandler(test_base.TestCase): - - def setUp(self): - super(TestKuryrNetworkPopulationHandler, self).setUp() - - self._project_id = mock.sentinel.project_id - self._subnets = mock.sentinel.subnets - self._kuryrnet_crd = { - 'metadata': { - 'name': 'test-namespace', - }, - 'spec': { - 'nsName': 'test-namespace', - 'projectId': 'test-project', - 'nsLabels': {}, - }, - 'status': { - 'subnetId': 'test-subnet' - } - } - - self._handler = mock.MagicMock( - spec=kuryrnetwork_population.KuryrNetworkPopulationHandler) - # NOTE(ltomasbo): The KuryrNetwork handler is associated to the usage - # of namespace subnet driver, - self._handler._drv_subnets = mock.Mock( - spec=subnet_drv.NamespacePodSubnetDriver) - self._handler._drv_vif_pool = mock.MagicMock( - spec=vif_pool.MultiVIFPool) - self._handler._drv_nodes_subnets = mock.MagicMock( - spec=node_subnets.ConfigNodesSubnets) - - self._get_namespace_subnet = ( - self._handler._drv_subnets.get_namespace_subnet) - self._set_vif_driver = self._handler._drv_vif_pool.set_vif_driver - self._populate_pool = self._handler._drv_vif_pool.populate_pool - self._patch_kuryrnetwork_crd = self._handler._patch_kuryrnetwork_crd - - self._get_namespace_subnet.return_value = self._subnets - - @mock.patch.object(drivers.VIFPoolDriver, 'get_instance') - @mock.patch.object(drivers.PodSubnetsDriver, 'get_instance') - def test_init(self, m_get_subnet_driver, m_get_vif_pool_driver): - subnet_driver = mock.sentinel.subnet_driver - vif_pool_driver = mock.Mock(spec=vif_pool.MultiVIFPool) - - m_get_subnet_driver.return_value = subnet_driver - m_get_vif_pool_driver.return_value = vif_pool_driver - - handler = kuryrnetwork_population.KuryrNetworkPopulationHandler() - - self.assertEqual(subnet_driver, handler._drv_subnets) - self.assertEqual(vif_pool_driver, handler._drv_vif_pool) - - @mock.patch.object(driver_utils, 'get_annotations') - @mock.patch.object(driver_utils, 'get_namespace') - @mock.patch.object(utils, 'get_nodes_ips') - def test_on_present(self, m_get_nodes_ips, m_get_ns, m_get_ann): - m_get_nodes_ips.return_value = ['node-ip'] - m_get_ns.return_value = mock.sentinel.ns - m_get_ann.return_value = self._kuryrnet_crd['metadata']['name'] - - kuryrnetwork_population.KuryrNetworkPopulationHandler.on_present( - self._handler, self._kuryrnet_crd) - - self._get_namespace_subnet.assert_called_once_with( - self._kuryrnet_crd['spec']['nsName'], - self._kuryrnet_crd['status']['subnetId']) - self._populate_pool.assert_called_once_with( - 'node-ip', self._kuryrnet_crd['spec']['projectId'], self._subnets, - []) - self._patch_kuryrnetwork_crd.assert_called_once() - - def test_on_added_no_subnet(self): - kns = self._kuryrnet_crd.copy() - del kns['status'] - kuryrnetwork_population.KuryrNetworkPopulationHandler.on_added( - self._handler, kns) - self._get_namespace_subnet.assert_not_called() - - def test_on_added_populated(self): - kns = self._kuryrnet_crd.copy() - kns['status'] = {'populated': True} - kuryrnetwork_population.KuryrNetworkPopulationHandler.on_added( - self._handler, kns) - self._get_namespace_subnet.assert_not_called() diff --git a/kuryr_kubernetes/tests/unit/controller/handlers/test_kuryrnetworkpolicy.py b/kuryr_kubernetes/tests/unit/controller/handlers/test_kuryrnetworkpolicy.py deleted file mode 100644 index 5f3f84c29..000000000 --- a/kuryr_kubernetes/tests/unit/controller/handlers/test_kuryrnetworkpolicy.py +++ /dev/null @@ -1,97 +0,0 @@ -# Copyright 2020 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 unittest import mock - -from kuryr_kubernetes.controller.drivers import base as drivers -from kuryr_kubernetes.controller.handlers import kuryrnetworkpolicy -from kuryr_kubernetes.tests import base as test_base - - -class TestPolicyHandler(test_base.TestCase): - - @mock.patch.object(drivers.LBaaSDriver, 'get_instance') - @mock.patch.object(drivers.NetworkPolicyDriver, 'get_instance') - @mock.patch('kuryr_kubernetes.clients.get_kubernetes_client') - @mock.patch('kuryr_kubernetes.clients.get_network_client') - @mock.patch('kuryr_kubernetes.clients.get_loadbalancer_client') - def setUp(self, m_get_os_lb, m_get_os_net, m_get_k8s, m_get_np, - m_get_lbaas): - super(TestPolicyHandler, self).setUp() - - self._project_id = mock.sentinel.project_id - self._policy_name = 'np-test' - self._policy_uid = mock.sentinel.policy_uid - self._policy_link = mock.sentinel.policy_link - - self._policy = { - 'apiVersion': 'networking.k8s.io/v1', - 'kind': 'NetworkPolicy', - 'metadata': { - 'name': self._policy_name, - 'resourceVersion': '2259309', - 'generation': 1, - 'creationTimestamp': '2018-09-18T14:09:51Z', - 'namespace': 'default', - 'annotations': {}, - 'uid': self._policy_uid - }, - 'spec': { - 'egress': [{'ports': [{'port': 5978, 'protocol': 'TCP'}]}], - 'ingress': [{'ports': [{'port': 6379, 'protocol': 'TCP'}]}], - 'policyTypes': ['Ingress', 'Egress'] - } - } - - self.k8s = mock.Mock() - m_get_k8s.return_value = self.k8s - self.m_get_k8s = m_get_k8s - - self.os_net = mock.Mock() - m_get_os_net.return_value = self.os_net - self.m_get_os_net = m_get_os_net - - self.np_driver = mock.Mock() - m_get_np.return_value = self.np_driver - self.m_get_np = m_get_np - - self.lbaas_driver = mock.Mock() - m_get_lbaas.return_value = self.lbaas_driver - self.m_get_lbaas = m_get_lbaas - - self.k8s.get.return_value = {} - self.handler = kuryrnetworkpolicy.KuryrNetworkPolicyHandler() - - def _get_knp_obj(self): - knp_obj = { - 'apiVersion': 'openstack.org/v1', - 'kind': 'KuryrNetworkPolicy', - 'metadata': { - 'name': 'np-test-network-policy', - 'namespace': 'test-1', - }, - 'spec': { - 'securityGroupId': 'c1ac16f5-e198-4628-9d84-253c6001be8e', - 'securityGroupName': 'sg-test-network-policy' - }} - return knp_obj - - def test_init(self): - self.m_get_k8s.assert_called_once() - self.m_get_np.assert_called_once() - - self.assertEqual(self.np_driver, self.handler._drv_policy) - self.assertEqual(self.k8s, self.handler.k8s) - self.assertEqual(self.os_net, self.handler.os_net) - self.assertEqual(self.lbaas_driver, self.handler._drv_lbaas) diff --git a/kuryr_kubernetes/tests/unit/controller/handlers/test_kuryrport.py b/kuryr_kubernetes/tests/unit/controller/handlers/test_kuryrport.py deleted file mode 100644 index 703f9e3e8..000000000 --- a/kuryr_kubernetes/tests/unit/controller/handlers/test_kuryrport.py +++ /dev/null @@ -1,681 +0,0 @@ -# Copyright (c) 2020 Red Hat, 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. - -from unittest import mock - -from openstack import exceptions as os_exc -from os_vif import objects as os_obj -from oslo_config import cfg - -from kuryr_kubernetes import constants -from kuryr_kubernetes.controller.drivers import multi_vif -from kuryr_kubernetes.controller.handlers import kuryrport -from kuryr_kubernetes import exceptions as k_exc -from kuryr_kubernetes.tests import base as test_base -from kuryr_kubernetes.tests.unit import kuryr_fixtures as k_fix -from kuryr_kubernetes import utils - - -CONF = cfg.CONF - - -class TestKuryrPortHandler(test_base.TestCase): - - def setUp(self): - super().setUp() - self._project_id = mock.sentinel.project_id - self._subnets = mock.sentinel.subnets - self._security_groups = mock.sentinel.security_groups - self._host = mock.sentinel.hostname - self._pod_version = mock.sentinel.pod_version - self._pod_link = mock.sentinel.pod_link - self._kp_version = mock.sentinel.kp_version - self._kp_namespace = mock.sentinel.namespace - self._kp_uid = mock.sentinel.kp_uid - self._kp_name = 'pod1' - self._pod_uid = 'deadbeef' - - self._pod = {'apiVersion': 'v1', - 'kind': 'Pod', - 'metadata': { - 'resourceVersion': self._pod_version, - 'name': self._kp_name, - 'deletionTimestamp': mock.sentinel.date, - 'namespace': self._kp_namespace, - 'uid': self._pod_uid, - }, - 'spec': {'nodeName': self._host}} - - self._kp = { - 'apiVersion': 'openstack.org/v1', - 'kind': 'KuryrPort', - 'metadata': { - 'resourceVersion': self._kp_version, - 'name': self._kp_name, - 'namespace': self._kp_namespace, - 'labels': { - constants.KURYRPORT_LABEL: self._host - }, - 'finalizers': [], - }, - 'spec': { - 'podUid': self._pod_uid, - 'podNodeName': self._host - }, - 'status': {'vifs': {}} - } - - self._vif1 = os_obj.vif.VIFBase() - self._vif2 = os_obj.vif.VIFBase() - self._vif1.active = False - self._vif2.active = False - self._vif1.plugin = 'object' - self._vif2.plugin = 'object' - self._vif1_primitive = self._vif1.obj_to_primitive() - self._vif2_primitive = self._vif2.obj_to_primitive() - self._vifs_primitive = {'eth0': {'default': True, - 'vif': self._vif1_primitive}, - 'eth1': {'default': False, - 'vif': self._vif2_primitive}} - self._vifs = {'eth0': {'default': True, - 'vif': self._vif1}, - 'eth1': {'default': False, - 'vif': self._vif2}} - self._pod_uri = (f"{constants.K8S_API_NAMESPACES}" - f"/{self._kp['metadata']['namespace']}/pods/" - f"{self._kp['metadata']['name']}") - self._kp_uri = utils.get_res_link(self._kp) - self._node_uri = f"{constants.K8S_API_BASE}/nodes/{self._host}" - self.useFixture(k_fix.MockNetworkClient()) - self._driver = multi_vif.NoopMultiVIFDriver() - - @mock.patch('kuryr_kubernetes.controller.handlers.kuryrport.' - 'KuryrPortHandler.get_vifs') - @mock.patch('kuryr_kubernetes.clients.get_kubernetes_client') - @mock.patch('kuryr_kubernetes.controller.drivers.base.MultiVIFDriver.' - 'get_enabled_drivers') - def test_on_present_no_vifs_create(self, ged, get_k8s_client, get_vifs): - ged.return_value = [self._driver] - kp = kuryrport.KuryrPortHandler() - get_vifs.return_value = True - - kp.on_present(self._kp) - - get_vifs.assert_called_once_with(self._kp) - - @mock.patch('kuryr_kubernetes.controller.handlers.kuryrport.' - 'KuryrPortHandler.get_vifs') - @mock.patch('kuryr_kubernetes.clients.get_kubernetes_client') - @mock.patch('kuryr_kubernetes.controller.drivers.base.MultiVIFDriver.' - 'get_enabled_drivers') - def test_on_present_getting_vifs_failed(self, ged, get_k8s_client, - get_vifs): - ged.return_value = [self._driver] - kp = kuryrport.KuryrPortHandler() - get_vifs.return_value = False - - self.assertFalse(kp.on_present(self._kp)) - - get_vifs.assert_called_once_with(self._kp) - - @mock.patch('kuryr_kubernetes.controller.drivers.default_project.' - 'DefaultPodProjectDriver.get_project') - @mock.patch('kuryr_kubernetes.controller.handlers.kuryrport.' - 'KuryrPortHandler._update_kuryrport_crd') - @mock.patch('kuryr_kubernetes.controller.drivers.vif_pool.MultiVIFPool.' - 'activate_vif') - @mock.patch('kuryr_kubernetes.clients.get_kubernetes_client') - @mock.patch('kuryr_kubernetes.controller.drivers.base.MultiVIFDriver.' - 'get_enabled_drivers') - def test_on_present(self, ged, get_k8s_client, activate_vif, - update_crd, get_project): - ged.return_value = [mock.MagicMock] - kp = kuryrport.KuryrPortHandler() - self._kp['status']['vifs'] = self._vifs_primitive - get_project.return_value = self._project_id - - with mock.patch.object(kp, 'k8s') as k8s: - k8s.get.return_value = self._pod - - kp.on_present(self._kp) - - k8s.get.assert_called_once_with(self._pod_uri) - - activate_vif.assert_has_calls([mock.call(self._vif1, pod=self._pod, - retry_info=mock.ANY), - mock.call(self._vif2, pod=self._pod, - retry_info=mock.ANY)]) - update_crd.assert_called_once_with(self._kp, self._vifs) - - @mock.patch('kuryr_kubernetes.clients.get_kubernetes_client') - @mock.patch('kuryr_kubernetes.controller.drivers.base.MultiVIFDriver.' - 'get_enabled_drivers') - def test_on_present_active(self, ged, get_k8s_client): - ged.return_value = [self._driver] - kp = kuryrport.KuryrPortHandler() - self._vif1.active = True - self._vif2.active = True - self._kp['status']['vifs'] = { - 'eth0': {'default': True, - 'vif': self._vif1.obj_to_primitive()}, - 'eth1': {'default': False, - 'vif': self._vif2.obj_to_primitive()}} - - kp.on_present(self._kp) - - @mock.patch('kuryr_kubernetes.controller.handlers.kuryrport.' - 'KuryrPortHandler._update_kuryrport_crd') - @mock.patch('kuryr_kubernetes.controller.drivers.vif_pool.MultiVIFPool.' - 'activate_vif') - @mock.patch('kuryr_kubernetes.clients.get_kubernetes_client') - @mock.patch('kuryr_kubernetes.controller.drivers.base.MultiVIFDriver.' - 'get_enabled_drivers') - def test_on_present_port_not_found(self, ged, get_k8s_client, activate_vif, - update_crd): - ged.return_value = [self._driver] - kp = kuryrport.KuryrPortHandler() - self._kp['status']['vifs'] = self._vifs_primitive - activate_vif.side_effect = os_exc.ResourceNotFound() - - kp.on_present(self._kp) - - activate_vif.assert_has_calls([mock.call(self._vif1, pod=mock.ANY, - retry_info=mock.ANY), - mock.call(self._vif2, pod=mock.ANY, - retry_info=mock.ANY)]) - - @mock.patch('kuryr_kubernetes.controller.drivers.vif_pool.MultiVIFPool.' - 'activate_vif') - @mock.patch('kuryr_kubernetes.clients.get_kubernetes_client') - @mock.patch('kuryr_kubernetes.controller.drivers.base.MultiVIFDriver.' - 'get_enabled_drivers') - def test_on_present_pod_not_found(self, ged, get_k8s_client, activate_vif): - ged.return_value = [self._driver] - kp = kuryrport.KuryrPortHandler() - self._kp['status']['vifs'] = self._vifs_primitive - - with mock.patch.object(kp, 'k8s') as k8s: - k8s.get.side_effect = k_exc.K8sResourceNotFound(self._pod) - - self.assertRaises(k_exc.K8sResourceNotFound, kp.on_present, - self._kp) - - k8s.get.assert_called_once_with(self._pod_uri) - - @mock.patch('kuryr_kubernetes.controller.drivers.vif_pool.MultiVIFPool.' - 'release_vif') - @mock.patch('kuryr_kubernetes.controller.drivers.default_security_groups.' - 'DefaultPodSecurityGroupsDriver.get_security_groups') - @mock.patch('kuryr_kubernetes.controller.drivers.default_project.' - 'DefaultPodProjectDriver.get_project') - @mock.patch('kuryr_kubernetes.controller.handlers.kuryrport.' - 'KuryrPortHandler._update_kuryrport_crd') - @mock.patch('kuryr_kubernetes.controller.drivers.vif_pool.MultiVIFPool.' - 'activate_vif') - @mock.patch('kuryr_kubernetes.clients.get_kubernetes_client') - @mock.patch('kuryr_kubernetes.controller.drivers.base.MultiVIFDriver.' - 'get_enabled_drivers') - def test_on_present_fail_update_crd(self, ged, get_k8s_client, - activate_vif, update_crd, get_project, - get_sg, release_vif): - ged.return_value = [self._driver] - kp = kuryrport.KuryrPortHandler() - self._kp['status']['vifs'] = self._vifs_primitive - update_crd.side_effect = k_exc.K8sResourceNotFound(self._kp) - get_project.return_value = self._project_id - get_sg.return_value = self._security_groups - - with mock.patch.object(kp, 'k8s') as k8s: - k8s.get.return_value = self._pod - - kp.on_present(self._kp) - - k8s.get.assert_called_once_with(self._pod_uri) - - @mock.patch('kuryr_kubernetes.controller.drivers.vif_pool.MultiVIFPool.' - 'release_vif') - @mock.patch('kuryr_kubernetes.controller.drivers.default_security_groups.' - 'DefaultPodSecurityGroupsDriver.get_security_groups') - @mock.patch('kuryr_kubernetes.controller.drivers.default_project.' - 'DefaultPodProjectDriver.get_project') - @mock.patch('kuryr_kubernetes.controller.handlers.kuryrport.' - 'KuryrPortHandler._update_kuryrport_crd') - @mock.patch('kuryr_kubernetes.controller.drivers.vif_pool.MultiVIFPool.' - 'activate_vif') - @mock.patch('kuryr_kubernetes.clients.get_kubernetes_client') - @mock.patch('kuryr_kubernetes.controller.drivers.base.MultiVIFDriver.' - 'get_enabled_drivers') - def test_on_present_exception_during_update_crd(self, ged, get_k8s_client, - activate_vif, - update_crd, get_project, - get_sg, release_vif): - ged.return_value = [self._driver] - kp = kuryrport.KuryrPortHandler() - self._kp['status']['vifs'] = self._vifs_primitive - update_crd.side_effect = k_exc.K8sClientException() - get_project.return_value = self._project_id - get_sg.return_value = self._security_groups - - with mock.patch.object(kp, 'k8s') as k8s: - k8s.get.return_value = self._pod - - self.assertRaises(k_exc.ResourceNotReady, kp.on_present, self._kp) - - k8s.get.assert_called_once_with(self._pod_uri) - - update_crd.assert_called_once_with(self._kp, self._vifs) - - @mock.patch('kuryr_kubernetes.controller.drivers.default_project.' - 'DefaultPodProjectDriver.get_project') - @mock.patch('kuryr_kubernetes.controller.drivers.utils.get_services') - @mock.patch('kuryr_kubernetes.controller.handlers.kuryrport.' - 'KuryrPortHandler._update_services') - @mock.patch('kuryr_kubernetes.controller.drivers.default_security_groups.' - 'DefaultPodSecurityGroupsDriver.create_sg_rules') - @mock.patch('kuryr_kubernetes.controller.drivers.base.' - 'ServiceSecurityGroupsDriver.get_instance') - @mock.patch('kuryr_kubernetes.controller.drivers.base.LBaaSDriver.' - 'get_instance') - @mock.patch('kuryr_kubernetes.controller.handlers.kuryrport.' - 'KuryrPortHandler._update_kuryrport_crd') - @mock.patch('kuryr_kubernetes.controller.drivers.vif_pool.MultiVIFPool.' - 'activate_vif') - @mock.patch('kuryr_kubernetes.clients.get_kubernetes_client') - @mock.patch('kuryr_kubernetes.controller.drivers.utils.' - 'is_network_policy_enabled') - @mock.patch('kuryr_kubernetes.controller.drivers.base.MultiVIFDriver.' - 'get_enabled_drivers') - def test_on_present_np(self, ged, is_np_enabled, get_k8s_client, - activate_vif, update_crd, get_lb_instance, - get_sg_instance, create_sgr, update_services, - get_services, get_project): - ged.return_value = [self._driver] - kp = kuryrport.KuryrPortHandler() - self._kp['status']['vifs'] = self._vifs_primitive - - with mock.patch.object(kp, 'k8s') as k8s: - k8s.get.return_value = self._pod - - kp.on_present(self._kp) - - k8s.get.assert_called_once_with(self._pod_uri) - - activate_vif.assert_has_calls([mock.call(self._vif1, pod=self._pod, - retry_info=mock.ANY), - mock.call(self._vif2, pod=self._pod, - retry_info=mock.ANY)]) - update_crd.assert_called_once_with(self._kp, self._vifs) - create_sgr.assert_called_once_with(self._pod) - - @mock.patch('kuryr_kubernetes.controller.drivers.default_project.' - 'DefaultPodProjectDriver.get_project') - @mock.patch('kuryr_kubernetes.utils.get_parent_port_id') - @mock.patch('kuryr_kubernetes.utils.get_parent_port_ip') - @mock.patch('kuryr_kubernetes.clients.get_kubernetes_client') - @mock.patch('kuryr_kubernetes.controller.drivers.base.MultiVIFDriver.' - 'get_enabled_drivers') - def test_on_finalize_exception_on_pod(self, ged, k8s, gppip, gppid, - project_driver): - ged.return_value = [self._driver] - kp = kuryrport.KuryrPortHandler() - self._kp['metadata']['deletionTimestamp'] = 'foobar' - self._kp['status']['vifs'] = self._vifs_primitive - - with mock.patch.object(kp, 'k8s') as k8s: - k8s.get.side_effect = k_exc.K8sResourceNotFound(self._pod) - - self.assertIsNone(kp.on_finalize(self._kp)) - - k8s.get.assert_has_calls([mock.call(self._pod_uri), - mock.call(self._node_uri)]) - k8s.remove_finalizer.assert_has_calls( - (mock.call(mock.ANY, constants.POD_FINALIZER), - mock.call(self._kp, constants.KURYRPORT_FINALIZER))) - - @mock.patch('kuryr_kubernetes.controller.handlers.kuryrport.' - 'KuryrPortHandler._update_services') - @mock.patch('kuryr_kubernetes.controller.drivers.utils.get_services') - @mock.patch('kuryr_kubernetes.controller.drivers.base.' - 'ServiceSecurityGroupsDriver.get_instance') - @mock.patch('kuryr_kubernetes.controller.drivers.base.LBaaSDriver.' - 'get_instance') - @mock.patch('kuryr_kubernetes.controller.drivers.utils.' - 'is_network_policy_enabled') - @mock.patch('kuryr_kubernetes.controller.drivers.vif_pool.MultiVIFPool.' - 'release_vif') - @mock.patch('kuryr_kubernetes.controller.drivers.default_security_groups.' - 'DefaultPodSecurityGroupsDriver.delete_sg_rules') - @mock.patch('kuryr_kubernetes.controller.drivers.default_project.' - 'DefaultPodProjectDriver.get_project') - @mock.patch('kuryr_kubernetes.clients.get_kubernetes_client') - @mock.patch('kuryr_kubernetes.controller.drivers.base.MultiVIFDriver.' - 'get_enabled_drivers') - def test_on_finalize_np(self, ged, k8s, get_project, delete_sg_rules, - release_vif, is_np_enabled, get_lb_instance, - get_sg_instance, get_services, update_services): - ged.return_value = [self._driver] - CONF.set_override('enforce_sg_rules', True, group='octavia_defaults') - self.addCleanup(CONF.clear_override, 'enforce_sg_rules', - group='octavia_defaults') - kp = kuryrport.KuryrPortHandler() - self._kp['status']['vifs'] = self._vifs_primitive - get_project.return_value = self._project_id - selector = mock.sentinel.selector - delete_sg_rules.return_value = selector - get_services.return_value = mock.sentinel.services - - with mock.patch.object(kp, 'k8s') as k8s: - k8s.get.return_value = self._pod - - kp.on_finalize(self._kp) - - k8s.get.assert_called_once_with(self._pod_uri) - k8s.remove_finalizer.assert_has_calls( - [mock.call(self._pod, constants.POD_FINALIZER), - mock.call(self._kp, constants.KURYRPORT_FINALIZER)]) - - delete_sg_rules.assert_called_once_with(self._pod) - release_vif.assert_has_calls([mock.call(self._pod, self._vif1, - self._project_id), - mock.call(self._pod, self._vif2, - self._project_id)]) - - get_services.assert_called_once() - update_services.assert_called_once_with(mock.sentinel.services, - selector, self._project_id) - - @mock.patch('kuryr_kubernetes.clients.get_kubernetes_client') - @mock.patch('kuryr_kubernetes.controller.drivers.base.MultiVIFDriver.' - 'get_enabled_drivers') - def test_on_finalize_pod_running(self, ged, k8s): - ged.return_value = [self._driver] - # copy, so it will not be affected by other tests run in parallel. - pod = dict(self._pod) - del(pod['metadata']['deletionTimestamp']) - - kp = kuryrport.KuryrPortHandler() - - with mock.patch.object(kp, 'k8s') as k8s: - k8s.get.return_value = pod - self.assertIsNone(kp.on_finalize(self._kp)) - k8s.get.assert_called_once_with(self._pod_uri) - - @mock.patch('kuryr_kubernetes.controller.handlers.kuryrport.' - 'KuryrPortHandler._update_kuryrport_crd') - @mock.patch('kuryr_kubernetes.controller.drivers.vif_pool.MultiVIFPool.' - 'request_vif') - @mock.patch('kuryr_kubernetes.controller.drivers.default_subnet.' - 'DefaultPodSubnetDriver.get_subnets') - @mock.patch('kuryr_kubernetes.controller.drivers.default_security_groups.' - 'DefaultPodSecurityGroupsDriver.get_security_groups') - @mock.patch('kuryr_kubernetes.controller.drivers.default_project.' - 'DefaultPodProjectDriver.get_project') - @mock.patch('kuryr_kubernetes.clients.get_kubernetes_client') - @mock.patch('kuryr_kubernetes.controller.drivers.base.MultiVIFDriver.' - 'get_enabled_drivers') - def test_get_vifs(self, ged, k8s, get_project, get_sg, get_subnets, - request_vif, update_crd): - ged.return_value = [self._driver] - kp = kuryrport.KuryrPortHandler() - kp.k8s.get.return_value = self._pod - get_sg.return_value = self._security_groups - get_project.return_value = self._project_id - get_subnets.return_value = mock.sentinel.subnets - request_vif.return_value = self._vif1 - - self.assertTrue(kp.get_vifs(self._kp)) - - kp.k8s.get.assert_called_once_with(self._pod_uri) - get_project.assert_called_once_with(self._pod) - get_sg.assert_called_once_with(self._pod, self._project_id) - get_subnets.assert_called_once_with(self._pod, self._project_id) - request_vif.assert_called_once_with(self._pod, self._project_id, - mock.sentinel.subnets, - self._security_groups) - update_crd.assert_called_once_with(self._kp, - {constants.DEFAULT_IFNAME: - {'default': True, - 'vif': self._vif1}}) - - @mock.patch('kuryr_kubernetes.clients.get_kubernetes_client') - @mock.patch('kuryr_kubernetes.controller.drivers.base.MultiVIFDriver.' - 'get_enabled_drivers') - def test_get_vifs_pod_not_found(self, ged, k8s): - ged.return_value = [self._driver] - kp = kuryrport.KuryrPortHandler() - kp.k8s.get.side_effect = k_exc.K8sResourceNotFound(self._pod) - - self.assertFalse(kp.get_vifs(self._kp)) - - kp.k8s.get.assert_called_once_with(self._pod_uri) - kp.k8s.delete.assert_called_once_with(self._kp_uri) - - @mock.patch('kuryr_kubernetes.controller.drivers.default_subnet.' - 'DefaultPodSubnetDriver.get_subnets') - @mock.patch('kuryr_kubernetes.controller.drivers.default_security_groups.' - 'DefaultPodSecurityGroupsDriver.get_security_groups') - @mock.patch('kuryr_kubernetes.controller.drivers.default_project.' - 'DefaultPodProjectDriver.get_project') - @mock.patch('kuryr_kubernetes.clients.get_kubernetes_client') - @mock.patch('kuryr_kubernetes.controller.drivers.base.MultiVIFDriver.' - 'get_enabled_drivers') - def test_get_vifs_subnet_error(self, ged, k8s, get_project, get_sg, - get_subnets): - ged.return_value = [self._driver] - kp = kuryrport.KuryrPortHandler() - kp.k8s.get.return_value = self._pod - get_sg.return_value = self._security_groups - get_project.return_value = self._project_id - get_subnets.side_effect = os_exc.ResourceNotFound() - - self.assertFalse(kp.get_vifs(self._kp)) - - kp.k8s.get.assert_called_once_with(self._pod_uri) - get_project.assert_called_once_with(self._pod) - get_sg.assert_called_once_with(self._pod, self._project_id) - get_subnets.assert_called_once_with(self._pod, self._project_id) - - @mock.patch('kuryr_kubernetes.controller.drivers.vif_pool.MultiVIFPool.' - 'request_vif') - @mock.patch('kuryr_kubernetes.controller.drivers.default_subnet.' - 'DefaultPodSubnetDriver.get_subnets') - @mock.patch('kuryr_kubernetes.controller.drivers.default_security_groups.' - 'DefaultPodSecurityGroupsDriver.get_security_groups') - @mock.patch('kuryr_kubernetes.controller.drivers.default_project.' - 'DefaultPodProjectDriver.get_project') - @mock.patch('kuryr_kubernetes.clients.get_kubernetes_client') - @mock.patch('kuryr_kubernetes.controller.drivers.base.MultiVIFDriver.' - 'get_enabled_drivers') - def test_get_vifs_no_vif(self, ged, k8s, get_project, get_sg, get_subnets, - request_vif): - ged.return_value = [self._driver] - kp = kuryrport.KuryrPortHandler() - kp.k8s.get.return_value = self._pod - get_sg.return_value = self._security_groups - get_project.return_value = self._project_id - get_subnets.return_value = mock.sentinel.subnets - request_vif.return_value = None - - self.assertFalse(kp.get_vifs(self._kp)) - - kp.k8s.get.assert_called_once_with(self._pod_uri) - get_project.assert_called_once_with(self._pod) - get_sg.assert_called_once_with(self._pod, self._project_id) - get_subnets.assert_called_once_with(self._pod, self._project_id) - request_vif.assert_called_once_with(self._pod, self._project_id, - mock.sentinel.subnets, - self._security_groups) - - @mock.patch('kuryr_kubernetes.controller.drivers.vif_pool.MultiVIFPool.' - 'request_vif') - @mock.patch('kuryr_kubernetes.controller.drivers.default_subnet.' - 'DefaultPodSubnetDriver.get_subnets') - @mock.patch('kuryr_kubernetes.controller.drivers.default_security_groups.' - 'DefaultPodSecurityGroupsDriver.get_security_groups') - @mock.patch('kuryr_kubernetes.controller.drivers.default_project.' - 'DefaultPodProjectDriver.get_project') - @mock.patch('kuryr_kubernetes.clients.get_kubernetes_client') - @mock.patch('kuryr_kubernetes.controller.drivers.base.MultiVIFDriver.' - 'get_enabled_drivers') - def test_get_vifs_resource_not_found(self, ged, k8s, get_project, get_sg, - get_subnets, request_vif): - ged.return_value = [self._driver] - kp = kuryrport.KuryrPortHandler() - kp.k8s.get.return_value = self._pod - get_sg.return_value = self._security_groups - get_project.return_value = self._project_id - get_subnets.return_value = mock.sentinel.subnets - request_vif.side_effect = os_exc.ResourceNotFound() - - self.assertRaises(k_exc.ResourceNotReady, kp.get_vifs, self._kp) - - kp.k8s.get.assert_called_once_with(self._pod_uri) - get_project.assert_called_once_with(self._pod) - get_sg.assert_called_once_with(self._pod, self._project_id) - get_subnets.assert_called_once_with(self._pod, self._project_id) - request_vif.assert_called_once_with(self._pod, self._project_id, - mock.sentinel.subnets, - self._security_groups) - - @mock.patch('kuryr_kubernetes.controller.handlers.kuryrport.' - 'KuryrPortHandler._update_kuryrport_crd') - @mock.patch('kuryr_kubernetes.controller.drivers.vif_pool.MultiVIFPool.' - 'request_vif') - @mock.patch('kuryr_kubernetes.controller.drivers.default_subnet.' - 'DefaultPodSubnetDriver.get_subnets') - @mock.patch('kuryr_kubernetes.controller.drivers.default_security_groups.' - 'DefaultPodSecurityGroupsDriver.get_security_groups') - @mock.patch('kuryr_kubernetes.controller.drivers.default_project.' - 'DefaultPodProjectDriver.get_project') - @mock.patch('kuryr_kubernetes.clients.get_kubernetes_client') - @mock.patch('kuryr_kubernetes.controller.drivers.base.MultiVIFDriver.' - 'get_enabled_drivers') - def test_get_vifs_with_additional_vif(self, ged, k8s, get_project, get_sg, - get_subnets, request_vif, - update_crd): - ged.return_value = [self._driver] - kp = kuryrport.KuryrPortHandler() - kp.k8s.get.return_value = self._pod - fake_driver = mock.MagicMock() - fake_driver.request_additional_vifs.return_value = [self._vif2] - kp._drv_multi_vif.append(fake_driver) - get_sg.return_value = self._security_groups - get_project.return_value = self._project_id - get_subnets.return_value = mock.sentinel.subnets - request_vif.return_value = self._vif1 - - self.assertTrue(kp.get_vifs(self._kp)) - - kp.k8s.get.assert_called_once_with(self._pod_uri) - get_project.assert_called_once_with(self._pod) - get_sg.assert_called_once_with(self._pod, self._project_id) - get_subnets.assert_called_once_with(self._pod, self._project_id) - request_vif.assert_called_once_with(self._pod, self._project_id, - mock.sentinel.subnets, - self._security_groups) - update_crd.assert_called_once_with(self._kp, - {'eth0': {'default': True, - 'vif': self._vif1}, - 'eth1': {'default': False, - 'vif': self._vif2}}) - - @mock.patch('kuryr_kubernetes.controller.drivers.vif_pool.MultiVIFPool.' - 'release_vif') - @mock.patch('kuryr_kubernetes.controller.handlers.kuryrport.' - 'KuryrPortHandler._update_kuryrport_crd') - @mock.patch('kuryr_kubernetes.controller.drivers.vif_pool.MultiVIFPool.' - 'request_vif') - @mock.patch('kuryr_kubernetes.controller.drivers.default_subnet.' - 'DefaultPodSubnetDriver.get_subnets') - @mock.patch('kuryr_kubernetes.controller.drivers.default_security_groups.' - 'DefaultPodSecurityGroupsDriver.get_security_groups') - @mock.patch('kuryr_kubernetes.controller.drivers.default_project.' - 'DefaultPodProjectDriver.get_project') - @mock.patch('kuryr_kubernetes.clients.get_kubernetes_client') - @mock.patch('kuryr_kubernetes.controller.drivers.base.MultiVIFDriver.' - 'get_enabled_drivers') - def test_get_exception_on_update_crd(self, ged, k8s, get_project, get_sg, - get_subnets, request_vif, update_crd, - release_vif): - ged.return_value = [self._driver] - kp = kuryrport.KuryrPortHandler() - kp.k8s.get.return_value = self._pod - get_sg.return_value = self._security_groups - get_project.return_value = self._project_id - get_subnets.return_value = mock.sentinel.subnets - request_vif.return_value = self._vif1 - update_crd.side_effect = k_exc.K8sClientException() - - self.assertTrue(kp.get_vifs(self._kp)) - - kp.k8s.get.assert_called_once_with(self._pod_uri) - get_project.assert_called_once_with(self._pod) - get_sg.assert_called_once_with(self._pod, self._project_id) - get_subnets.assert_called_once_with(self._pod, self._project_id) - request_vif.assert_called_once_with(self._pod, self._project_id, - mock.sentinel.subnets, - self._security_groups) - update_crd.assert_called_once_with(self._kp, - {constants.DEFAULT_IFNAME: - {'default': True, - 'vif': self._vif1}}) - release_vif.assert_called_once_with(self._pod, self._vif1, - self._project_id) - - @mock.patch('kuryr_kubernetes.clients.get_kubernetes_client') - @mock.patch('kuryr_kubernetes.controller.drivers.base.MultiVIFDriver.' - 'get_enabled_drivers') - def test_update_kuryrport_crd(self, ged, k8s): - ged.return_value = [self._driver] - kp = kuryrport.KuryrPortHandler() - - kp._update_kuryrport_crd(self._kp, self._vifs) - self._vif1.obj_reset_changes() - self._vif2.obj_reset_changes() - vif1 = self._vif1.obj_to_primitive() - vif2 = self._vif2.obj_to_primitive() - - arg = {'vifs': {'eth0': {'default': True, 'vif': vif1}, - 'eth1': {'default': False, 'vif': vif2}}} - kp.k8s.patch_crd.assert_called_once_with('status', - utils.get_res_link(self._kp), - arg) - - @mock.patch('kuryr_kubernetes.controller.drivers.utils.' - 'service_matches_affected_pods') - @mock.patch('kuryr_kubernetes.clients.get_kubernetes_client') - @mock.patch('kuryr_kubernetes.controller.drivers.base.MultiVIFDriver.' - 'get_enabled_drivers') - def test_update_services(self, ged, k8s, smap): - ged.return_value = [self._driver] - kp = kuryrport.KuryrPortHandler() - kp._drv_lbaas = mock.MagicMock() - kp._drv_svc_sg = mock.MagicMock() - kp._drv_svc_sg.get_security_groups.return_value = self._security_groups - - smap.side_effect = [True, False] - services = {'items': ['service1', 'service2']} - - kp._update_services(services, mock.sentinel.crd_pod_selectors, - self._project_id) - - smap.assert_has_calls([mock.call('service1', - mock.sentinel.crd_pod_selectors), - mock.call('service2', - mock.sentinel.crd_pod_selectors)]) - kp._drv_svc_sg.get_security_groups.assert_called_once_with( - 'service1', self._project_id) - kp._drv_lbaas.update_lbaas_sg.assert_called_once_with( - 'service1', self._security_groups) diff --git a/kuryr_kubernetes/tests/unit/controller/handlers/test_lbaas.py b/kuryr_kubernetes/tests/unit/controller/handlers/test_lbaas.py deleted file mode 100644 index e1e6c39bc..000000000 --- a/kuryr_kubernetes/tests/unit/controller/handlers/test_lbaas.py +++ /dev/null @@ -1,448 +0,0 @@ -# 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. - -from oslo_log import log as logging -from unittest import mock - -import os_vif.objects.network as osv_network -import os_vif.objects.subnet as osv_subnet - -from kuryr_kubernetes.controller.handlers import lbaas as h_lbaas -from kuryr_kubernetes import exceptions as k_exc -from kuryr_kubernetes.tests import base as test_base - -_SUPPORTED_LISTENER_PROT = ('HTTP', 'HTTPS', 'TCP') - - -@mock.patch('kuryr_kubernetes.controller.drivers.base.LBaaSDriver.' - 'get_instance', mock.Mock()) -@mock.patch('kuryr_kubernetes.clients.get_kubernetes_client', mock.Mock()) -class TestServiceHandler(test_base.TestCase): - @mock.patch('kuryr_kubernetes.clients.get_kubernetes_client') - def test_on_present(self, get_k8s_client): - svc_event = { - 'apiVersion': 'v1', - 'kind': 'Service', - "metadata": { - "creationTimestamp": "2020-07-25T18:15:12Z", - "finalizers": [ - "openstack.org/service" - ], - "labels": { - "run": "test" - }, - "name": "test", - "namespace": "test", - "resourceVersion": "413753", - "uid": "a026ae48-6141-4029-b743-bac48dae7f06" - }, - "spec": { - "clusterIP": "2.2.2.2", - "ports": [ - { - "port": 1, - "protocol": "TCP", - "targetPort": 1 - } - ], - "selector": { - "run": "test" - }, - "sessionAffinity": "None", - "type": "ClusterIP" - }, - "status": { - "loadBalancer": {} - } - } - - old_spec = { - 'apiVersion': 'openstack.org/v1', - 'kind': 'KuryrLoadBalancer', - 'metadata': { - 'name': 'test', - 'finalizers': [''], - }, - 'spec': { - 'ip': '1.1.1.1' - } - } - new_spec = { - 'apiVersion': 'openstack.org/v1', - 'kind': 'KuryrLoadBalancer', - 'metadata': { - 'name': 'test', - 'finalizers': [''], - }, - 'spec': { - 'ip': '2.2.2.2' - } - } - - project_id = mock.sentinel.project_id - m_drv_project = mock.Mock() - m_drv_project.get_project.return_value = project_id - - m_handler = mock.Mock(spec=h_lbaas.ServiceHandler) - m_handler._has_lbaas_spec_changes.return_value = True - m_handler.create_crd_spec.return_value = new_spec - m_handler._should_ignore.return_value = False - m_handler._drv_project = m_drv_project - m_handler.k8s = mock.Mock() - - h_lbaas.ServiceHandler.on_present(m_handler, svc_event) - m_handler.create_crd_spec(svc_event) - m_handler._has_lbaas_spec_changes.return_value = True - m_handler._update_crd_spec(old_spec, svc_event) - - @mock.patch('kuryr_kubernetes.clients.get_kubernetes_client') - def test_on_present_no_changes(self, get_k8s_client): - svc_event = { - 'apiVersion': 'v1', - 'kind': 'Service', - "metadata": { - "creationTimestamp": "2020-07-25T18:15:12Z", - "finalizers": [ - "openstack.org/service" - ], - "labels": { - "run": "test" - }, - "name": "test", - "namespace": "test", - "resourceVersion": "413753", - "uid": "a026ae48-6141-4029-b743-bac48dae7f06" - }, - "spec": { - "clusterIP": "2.2.2.2", - "ports": [ - { - "port": 1, - "protocol": "TCP", - "targetPort": 1 - } - ], - "selector": { - "run": "test" - }, - "sessionAffinity": "None", - "type": "ClusterIP" - }, - "status": { - "loadBalancer": {} - } - } - - old_spec = { - 'apiVersion': 'openstack.org/v1', - 'kind': 'KuryrLoadBalancer', - 'metadata': { - 'name': 'test', - 'finalizers': [''], - }, - 'spec': { - 'ip': '1.1.1.1' - } - } - - project_id = mock.sentinel.project_id - m_drv_project = mock.Mock() - m_drv_project.get_project.return_value = project_id - - m_handler = mock.Mock(spec=h_lbaas.ServiceHandler) - m_handler._has_lbaas_spec_changes.return_value = True - m_handler.create_crd_spec.return_value = old_spec - m_handler._should_ignore.return_value = False - m_handler._drv_project = m_drv_project - m_handler.k8s = mock.Mock() - - h_lbaas.ServiceHandler.on_present(m_handler, svc_event) - m_handler.create_crd_spec(svc_event) - m_handler._has_lbaas_spec_changes.return_value = False - - def test_get_service_ip(self): - svc_body = {'spec': {'type': 'ClusterIP', - 'clusterIP': '192.168.0.11'}} - handler = h_lbaas.ServiceHandler() - ret = handler._get_service_ip(svc_body) - self.assertEqual('192.168.0.11', ret) - - svc_body = {'spec': {'type': 'LoadBalancer', - 'clusterIP': '192.168.0.11'}} - ret = handler._get_service_ip(svc_body) - self.assertEqual('192.168.0.11', ret) - - def test_get_service_ip_funny(self): - svc_body = {'spec': {'type': 'ClusterIP', - 'clusterIP': '172.30.0.011'}} - handler = h_lbaas.ServiceHandler() - - ret = handler._get_service_ip(svc_body) - self.assertEqual('172.30.0.11', ret) - - def test_is_supported_type_clusterip(self): - m_handler = mock.Mock(spec=h_lbaas.ServiceHandler) - svc_body = {'spec': {'type': 'ClusterIP', - 'clusterIP': mock.sentinel.cluster_ip}} - - ret = h_lbaas.ServiceHandler._is_supported_type(m_handler, svc_body) - self.assertEqual(ret, True) - - def test_is_supported_type_loadbalancer(self): - m_handler = mock.Mock(spec=h_lbaas.ServiceHandler) - svc_body = {'spec': {'type': 'LoadBalancer', - 'clusterIP': mock.sentinel.cluster_ip}} - - ret = h_lbaas.ServiceHandler._is_supported_type(m_handler, svc_body) - self.assertEqual(ret, True) - - def _make_test_net_obj(self, cidr_list): - subnets = [osv_subnet.Subnet(cidr=cidr) for cidr in cidr_list] - subnets_list = osv_subnet.SubnetList(objects=subnets) - return osv_network.Network(subnets=subnets_list) - - @mock.patch('kuryr_kubernetes.utils.has_port_changes') - def test_has_lbaas_spec_changes(self, m_port_changes): - m_handler = mock.Mock(spec=h_lbaas.ServiceHandler) - service = mock.sentinel.service - lbaas_spec = mock.sentinel.lbaas_spec - - for has_ip_changes in (True, False): - for has_port_changes in (True, False): - for timeout in (True, False): - for provider in (True, False): - m_handler._has_ip_changes.return_value = has_ip_changes - m_port_changes.return_value = has_port_changes - m_handler._has_timeout_changes.return_value = timeout - m_handler._has_provider_changes.return_value = provider - ret = h_lbaas.ServiceHandler._has_lbaas_spec_changes( - m_handler, service, lbaas_spec) - self.assertEqual( - has_ip_changes or has_port_changes or timeout - or provider, ret) - - def test_has_ip_changes(self): - m_handler = mock.Mock(spec=h_lbaas.ServiceHandler) - m_service = {'apiVersion': 'v1', - 'kind': 'Service', - "metadata": {"name": "test", - "namespace": "test"}} - m_handler._get_service_ip.return_value = '1.1.1.1' - m_lbaas_spec = mock.MagicMock() - m_lbaas_spec.ip.__str__.return_value = '2.2.2.2' - - ret = h_lbaas.ServiceHandler._has_ip_changes( - m_handler, m_service, m_lbaas_spec) - self.assertTrue(ret) - - def test_has_ip_changes__no_changes(self): - service = { - 'apiVersion': 'v1', - 'kind': 'Service', - "metadata": { - "creationTimestamp": "2020-07-25T18:15:12Z", - "finalizers": [ - "openstack.org/service" - ], - "labels": { - "run": "test" - }, - "name": "test", - "namespace": "test", - "resourceVersion": "413753", - "uid": "a026ae48-6141-4029-b743-bac48dae7f06" - }, - "spec": { - "clusterIP": "1.1.1.1" - } - } - m_handler = mock.Mock(spec=h_lbaas.ServiceHandler) - m_handler._get_service_ip.return_value = '1.1.1.1' - lb_crd = { - 'apiVersion': 'openstack.org/v1', - 'kind': 'KuryrLoadBalancer', - 'metadata': { - 'name': 'test', - 'finalizers': [''], - }, - 'spec': { - 'ip': '1.1.1.1' - } - } - - ret = h_lbaas.ServiceHandler._has_ip_changes( - m_handler, service, lb_crd) - self.assertFalse(ret) - - def test_has_ip_changes__no_spec(self): - m_handler = mock.Mock(spec=h_lbaas.ServiceHandler) - m_handler._get_service_ip.return_value = '1.1.1.1' - service = { - 'apiVersion': 'v1', - 'kind': 'Service', - "metadata": { - "creationTimestamp": "2020-07-25T18:15:12Z", - "finalizers": [ - "openstack.org/service" - ], - "labels": { - "run": "test" - }, - "name": "test", - "namespace": "test", - "resourceVersion": "413753", - "uid": "a026ae48-6141-4029-b743-bac48dae7f06" - }, - "spec": { - "clusterIP": "1.1.1.1" - } - } - lb_crd = { - "spec": { - "ip": None - } - } - - ret = h_lbaas.ServiceHandler._has_ip_changes( - m_handler, service, lb_crd) - self.assertTrue(ret) - - def test_has_ip_changes__no_nothing(self): - m_handler = mock.Mock(spec=h_lbaas.ServiceHandler) - service = { - 'apiVersion': 'v1', - 'kind': 'Service', - "metadata": { - "creationTimestamp": "2020-07-25T18:15:12Z", - "finalizers": [ - "openstack.org/service" - ], - "labels": { - "run": "test" - }, - "name": "test", - "namespace": "test", - "resourceVersion": "413753", - "uid": "a026ae48-6141-4029-b743-bac48dae7f06" - }, - "spec": { - "clusterIP": "1.1.1.1" - } - } - lb_crd = { - "spec": { - "ip": None - } - } - m_handler._get_service_ip.return_value = None - - ret = h_lbaas.ServiceHandler._has_ip_changes( - m_handler, service, lb_crd) - self.assertFalse(ret) - - def test_set_lbaas_spec(self): - self.skipTest("skipping until generalised annotation handling is " - "implemented") - - def test_get_lbaas_spec(self): - self.skipTest("skipping until generalised annotation handling is " - "implemented") - - -class TestEndpointsHandler(test_base.TestCase): - - def setUp(self): - super().setUp() - self._ep_name = 'my-service' - self._ep_namespace = mock.sentinel.namespace - self._ep_ip = '1.2.3.4' - - self._ep = { - "kind": "Endpoints", - "apiVersion": "v1", - "metadata": { - "name": self._ep_name, - "namespace": self._ep_namespace, - }, - "subsets": [ - { - "addresses": [ - { - "ip": self._ep_ip - }, - ], - "ports": [ - { - "port": 8080, - "protocol": "TCP" - } - ] - } - ] - } - - self._klb_name = 'my-service' - self._klb_ip = '1.1.1.1' - - self._klb = { - 'apiVersion': 'openstack.org/v1', - 'kind': 'KuryrLoadBalancer', - 'metadata': { - 'name': self._klb_name, - 'finalizers': [''], - }, - 'spec': { - 'ip': self._klb_ip - } - } - - def test_on_deleted(self): - m_handler = mock.Mock(spec=h_lbaas.EndpointsHandler) - h_lbaas.EndpointsHandler.on_deleted(m_handler, self._ep) - m_handler._remove_endpoints.assert_called_once_with(self._ep) - - @mock.patch('kuryr_kubernetes.utils.get_klb_crd_path') - def test__remove_endpoints(self, get_klb_crd_path): - m_handler = mock.Mock() - h_lbaas.EndpointsHandler._remove_endpoints(m_handler, self._ep) - m_handler.k8s.patch_crd.assert_called_once_with( - 'spec', get_klb_crd_path(self._ep), 'endpointSlices', - action='remove') - - @mock.patch.object(logging.getLogger( - 'kuryr_kubernetes.controller.handlers.lbaas'), - 'debug') - def test__remove_endpoints_not_found(self, log): - m_handler = mock.Mock() - m_handler.k8s.patch_crd.side_effect = k_exc.K8sResourceNotFound('foo') - h_lbaas.EndpointsHandler._remove_endpoints(m_handler, self._ep) - log.assert_called_once() - - def test__remove_endpoints_client_exception(self): - m_handler = mock.Mock() - m_handler.k8s.patch_crd.side_effect = k_exc.K8sClientException() - self.assertRaises(k_exc.K8sClientException, - h_lbaas.EndpointsHandler._remove_endpoints, - m_handler, self._ep) - - @mock.patch.object(logging.getLogger( - 'kuryr_kubernetes.controller.handlers.lbaas'), - 'warning') - def test__remove_endpoints_unprocessable_entity(self, log): - m_handler = mock.Mock() - m_handler.k8s.patch_crd.side_effect = k_exc.K8sUnprocessableEntity( - 'bar') - h_lbaas.EndpointsHandler._remove_endpoints(m_handler, self._ep) - log.assert_not_called() diff --git a/kuryr_kubernetes/tests/unit/controller/handlers/test_loadbalancer.py b/kuryr_kubernetes/tests/unit/controller/handlers/test_loadbalancer.py deleted file mode 100755 index 58987b1e6..000000000 --- a/kuryr_kubernetes/tests/unit/controller/handlers/test_loadbalancer.py +++ /dev/null @@ -1,696 +0,0 @@ -# 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. - -from unittest import mock -import uuid - -import os_vif.objects.network as osv_network -import os_vif.objects.subnet as osv_subnet - -from kuryr_kubernetes import constants as k_const -from kuryr_kubernetes.controller.drivers import base as drv_base -from kuryr_kubernetes.controller.handlers import loadbalancer as h_lb -from kuryr_kubernetes.tests import base as test_base -from kuryr_kubernetes.tests.unit import kuryr_fixtures as k_fix - -_SUPPORTED_LISTENER_PROT = ('HTTP', 'HTTPS', 'TCP') - - -def get_lb_crd(): - return { - 'apiVersion': 'openstack.org/v1', - 'kind': 'KuryrLoadBalancer', - "metadata": { - "creationTimestamp": "2020-07-28T13:13:30Z", - "finalizers": [ - "" - ], - "generation": 6, - "name": "test", - "namespace": "default", - "resourceVersion": "111871", - "uid": "584fe3ea-04dd-43f7-be2f-713e861694ec" - }, - "spec": { - "ip": "1.2.3.4", - "ports": [ - { - "port": 1, - "protocol": "TCP", - "targetPort": "1" - } - ], - "project_id": "1023456789120", - "security_groups_ids": [ - "1d134e68-5653-4192-bda2-4214319af799", - "31d7b8c2-75f1-4125-9565-8c15c5cf046c" - ], - "subnet_id": "123456789120", - "endpointSlices": [ - { - "endpoints": [ - { - "addresses": ["1.1.1.1"], - "targetRef": { - "kind": "Pod", - "name": "test-f87976f9c-thjbk", - "namespace": "default", - "resourceVersion": "111701", - "uid": "10234567800" - } - } - ], - "ports": [ - { - "port": 2, - "protocol": "TCP" - } - ] - } - ], - "type": "LoadBalancer", - "provider": "ovn" - }, - "status": { - "listeners": [ - { - "id": "012345678912", - "loadbalancer_id": "01234567890", - "name": "default/test:TCP:80", - "port": 1, - "project_id": "12345678912", - "protocol": "TCP" - } - ], - "loadbalancer": { - "id": "01234567890", - "ip": "1.2.3.4", - "name": "default/test", - "port_id": "1023456789120", - "project_id": "12345678912", - "provider": "amphora", - "security_groups": [ - "1d134e68-5653-4192-bda2-4214319af799", - "31d7b8c2-75f1-4125-9565-8c15c5cf046c" - ], - "subnet_id": "123456789120" - }, - "members": [ - { - "id": "0123456789", - "ip": "1.1.1.1", - "name": "default/test-f87976f9c-thjbk:8080", - "pool_id": "1234567890", - "port": 2, - "project_id": "12345678912", - "subnet_id": "123456789120" - } - ], - "pools": [ - { - "id": "1234567890", - "listener_id": "012345678912", - "loadbalancer_id": "01234567890", - "name": "default/test:TCP:80", - "project_id": "12345678912", - "protocol": "TCP" - } - ], - 'service_pub_ip_info': { - 'ip_id': '1.2.3.5', - 'ip_addr': 'ec29d641-fec4-4f67-928a-124a76b3a888', - 'alloc_method': 'kk' - } - } - } - - -def get_lb_crds(): - return [ - { - 'apiVersion': 'openstack.org/v1', - 'kind': 'KuryrLoadBalancer', - "metadata": { - "creationTimestamp": "2020-07-28T13:13:30Z", - "finalizers": [ - "" - ], - "generation": 6, - "name": "test", - "namespace": "default", - "resourceVersion": "111871", - "uid": "584fe3ea-04dd-43f7-be2f-713e861694ec" - }, - "status": { - "listeners": [ - { - "id": "012345678912", - "loadbalancer_id": "01234567890", - "name": "default/test:TCP:80", - "port": 1, - "project_id": "12345678912", - "protocol": "TCP" - } - ], - "loadbalancer": { - "id": "01234567890", - "ip": "1.2.3.4", - "name": "default/test", - "port_id": "1023456789120", - "project_id": "12345678912", - "provider": "amphora", - "security_groups": [ - "1d134e68-5653-4192-bda2-4214319af799", - "31d7b8c2-75f1-4125-9565-8c15c5cf046c" - ], - "subnet_id": "123456789120" - }, - "pools": [ - { - "id": "1234567890", - "listener_id": "012345678912", - "loadbalancer_id": "01234567890", - "name": "default/test:TCP:80", - "project_id": "12345678912", - "protocol": "TCP" - } - ], - "members": [ - { - "id": "0123456789a", - "ip": "1.1.1.1", - "name": "default/test-f87976f9c-thjbk:8080", - "pool_id": "1234567890", - "port": 2, - "project_id": "12345678912", - "subnet_id": "123456789120" - } - ], - } - }, - { - 'apiVersion': 'openstack.org/v1', - 'kind': 'KuryrLoadBalancer', - "metadata": { - "creationTimestamp": "2020-07-28T13:13:30Z", - "finalizers": [ - "" - ], - "generation": 6, - "name": "demo", - "namespace": "default", - "resourceVersion": "111871", - "uid": "584fe3ea-04dd-43f7-be2f-713e861694ec" - }, - "status": { - "listeners": [ - { - "id": "012345678913", - "loadbalancer_id": "01234567891", - "name": "default/demo:TCP:80", - "port": 1, - "project_id": "12345678912", - "protocol": "TCP" - } - ], - "loadbalancer": { - "id": "01234567891", - "ip": "1.2.3.4", - "name": "default/demo", - "port_id": "1023456789120", - "project_id": "12345678912", - "provider": "amphora", - "security_groups": [ - "1d134e68-5653-4192-bda2-4214319af799", - "31d7b8c2-75f1-4125-9565-8c15c5cf046c" - ], - "subnet_id": "123456789120" - }, - "pools": [ - { - "id": "1234567891", - "listener_id": "012345678913", - "loadbalancer_id": "01234567891", - "name": "default/test:TCP:80", - "project_id": "12345678912", - "protocol": "TCP" - } - ], - "members": [ - { - "id": "0123456789b", - "ip": "1.1.1.1", - "name": "default/test_1-f87976f9c-thjbk:8080", - "pool_id": "1234567891", - "port": 2, - "project_id": "12345678913", - "subnet_id": "123456789121" - } - ], - } - } - ] - - -class FakeLBaaSDriver(drv_base.LBaaSDriver): - - def ensure_loadbalancer(self, name, project_id, subnet_id, ip, - security_groups_ids, service_type, provider=None): - - return { - 'name': name, - 'project_id': project_id, - 'subnet_id': subnet_id, - 'ip': ip, - 'id': str(uuid.uuid4()), - 'provider': provider - } - - def ensure_listener(self, loadbalancer, protocol, port, - service_type='ClusterIP'): - if protocol not in _SUPPORTED_LISTENER_PROT: - return None - - name = "%s:%s:%s" % (loadbalancer['name'], protocol, port) - return { - 'name': name, - 'project_id': loadbalancer['project_id'], - 'loadbalancer_id': loadbalancer['id'], - 'protocol': protocol, - 'port': port, - 'id': str(uuid.uuid4()) - } - - def ensure_pool(self, loadbalancer, listener): - return { - 'name': listener['name'], - 'project_id': loadbalancer['project_id'], - 'loadbalancer_id': loadbalancer['id'], - 'listener_id': listener['id'], - 'protocol': listener['protocol'], - 'id': str(uuid.uuid4()) - } - - def ensure_member(self, loadbalancer, pool, subnet_id, ip, port, - target_ref_namespace, target_ref_name, listener_port=None - ): - name = "%s:%s:%s" % (loadbalancer['name'], ip, port) - return { - 'name': name, - 'project_id': pool['project_id'], - 'pool_id': pool['id'], - 'subnet_id': subnet_id, - 'ip': ip, - 'port': port, - 'id': str(uuid.uuid4()) - } - - -@mock.patch('kuryr_kubernetes.utils.get_subnets_id_cidrs', - mock.Mock(return_value=[('id', 'cidr')])) -class TestKuryrLoadBalancerHandler(test_base.TestCase): - def test_on_present(self): - m_drv_service_pub_ip = mock.Mock() - m_drv_service_pub_ip.acquire_service_pub_ip_info.return_value = None - m_drv_service_pub_ip.associate_pub_ip.return_value = True - - m_handler = mock.Mock(spec=h_lb.KuryrLoadBalancerHandler) - - m_handler._should_ignore.return_value = False - m_handler._sync_lbaas_members.return_value = True - m_handler._drv_service_pub_ip = m_drv_service_pub_ip - - h_lb.KuryrLoadBalancerHandler.on_present(m_handler, get_lb_crd()) - - m_handler._should_ignore.assert_called_once_with(get_lb_crd()) - m_handler._sync_lbaas_members.assert_called_once_with( - get_lb_crd()) - - def _fake_sync_lbaas_members(self, crd): - loadbalancer = { - "id": "01234567890", - "ip": "1.2.3.4", - "name": "default/test", - "port_id": "1023456789120", - "project_id": "12345678912", - "provider": "amphora", - "security_groups": [ - "1d134e68-5653-4192-bda2-4214319af799", - "31d7b8c2-75f1-4125-9565-8c15c5cf046c" - ], - "subnet_id": "123456789120" - } - loadbalancer['port_id'] = 12345678 - crd['status']['loadbalancer'] = loadbalancer - crd['status']['service_pub_ip_info'] = None - return True - - def test_on_present_loadbalancer_service(self): - floating_ip = {'floating_ip_address': '1.2.3.5', - 'id': 'ec29d641-fec4-4f67-928a-124a76b3a888'} - - service_pub_ip_info = { - 'ip_id': floating_ip['id'], - 'ip_addr': floating_ip['floating_ip_address'], - 'alloc_method': 'kk' - } - crd = get_lb_crd() - m_drv_service_pub_ip = mock.Mock() - m_drv_service_pub_ip.acquire_service_pub_ip_info.return_value = ( - service_pub_ip_info) - m_drv_service_pub_ip.associate_pub_ip.return_value = True - - h = mock.Mock(spec=h_lb.KuryrLoadBalancerHandler) - h._should_ignore.return_value = False - h._sync_lbaas_members.return_value = self._fake_sync_lbaas_members(crd) - h._drv_service_pub_ip = m_drv_service_pub_ip - kubernetes = self.useFixture(k_fix.MockK8sClient()).client - kubernetes.get_kubernetes_client = mock.Mock() - kubernetes.get_kubernetes_client() - h_lb.KuryrLoadBalancerHandler.on_present(h, crd) - h._should_ignore.assert_called_once_with(crd) - h._update_lb_status.assert_called() - - @mock.patch('kuryr_kubernetes.utils.get_lbaas_spec') - @mock.patch('kuryr_kubernetes.utils.set_lbaas_state') - @mock.patch('kuryr_kubernetes.utils.get_lbaas_state') - def test_on_present_rollback(self, m_get_lbaas_state, - m_set_lbaas_state, m_get_lbaas_spec): - m_drv_service_pub_ip = mock.Mock() - m_drv_service_pub_ip.acquire_service_pub_ip_info.return_value = None - m_drv_service_pub_ip.associate_pub_ip.return_value = True - - m_handler = mock.Mock(spec=h_lb.KuryrLoadBalancerHandler) - m_handler._should_ignore.return_value = False - m_handler._sync_lbaas_members.return_value = True - m_handler._drv_service_pub_ip = m_drv_service_pub_ip - h_lb.KuryrLoadBalancerHandler.on_present(m_handler, get_lb_crd()) - - m_handler._should_ignore.assert_called_once_with(get_lb_crd()) - m_handler._sync_lbaas_members.assert_called_once_with( - get_lb_crd()) - - def test_on_cascade_deleted_lb_service(self): - m_handler = mock.Mock(spec=h_lb.KuryrLoadBalancerHandler) - m_handler._drv_lbaas = mock.Mock() - m_handler._drv_service_pub_ip = mock.Mock() - crd = get_lb_crd() - m_handler._drv_lbaas.release_loadbalancer( - loadbalancer=crd['status']['loadbalancer']) - m_handler._drv_service_pub_ip.release_pub_ip( - crd['status']['service_pub_ip_info']) - - def test_should_ignore(self): - m_handler = mock.Mock(spec=h_lb.KuryrLoadBalancerHandler) - loadbalancer_crd = get_lb_crd() - loadbalancer_crd['status'] = {} - m_handler._has_endpoints.return_value = True - - ret = h_lb.KuryrLoadBalancerHandler._should_ignore( - m_handler, loadbalancer_crd) - self.assertEqual(False, ret) - - m_handler._has_endpoints.assert_called_once_with(loadbalancer_crd) - - def test_should_ignore_member_scale_to_0(self): - m_handler = mock.Mock(spec=h_lb.KuryrLoadBalancerHandler) - m_handler._has_endpoints.return_value = False - loadbalancer_crd = get_lb_crd() - - ret = h_lb.KuryrLoadBalancerHandler._should_ignore( - m_handler, loadbalancer_crd) - self.assertEqual(False, ret) - - m_handler._has_endpoints.assert_called_once_with(loadbalancer_crd) - - def test_has_endpoints(self): - crd = get_lb_crd() - m_handler = mock.Mock(spec=h_lb.KuryrLoadBalancerHandler) - - ret = h_lb.KuryrLoadBalancerHandler._has_endpoints(m_handler, crd) - - self.assertEqual(True, ret) - - def test_get_pod_subnet(self): - subnet_id = mock.sentinel.subnet_id - project_id = mock.sentinel.project_id - target_ref = {'kind': k_const.K8S_OBJ_POD, - 'name': 'pod-name', - 'namespace': 'default', - 'spec': {}} - ip = '1.2.3.4' - m_handler = mock.Mock(spec=h_lb.KuryrLoadBalancerHandler) - m_drv_pod_project = mock.Mock() - m_drv_pod_project.get_project.return_value = project_id - m_handler._drv_pod_project = m_drv_pod_project - m_drv_pod_subnets = mock.Mock() - m_drv_pod_subnets.get_subnets.return_value = { - subnet_id: osv_network.Network(subnets=osv_subnet.SubnetList( - objects=[osv_subnet.Subnet(cidr='1.2.3.0/24')]))} - m_handler._drv_pod_subnets = m_drv_pod_subnets - - observed_subnet_id = h_lb.KuryrLoadBalancerHandler._get_pod_subnet( - m_handler, target_ref, ip) - - self.assertEqual(subnet_id, observed_subnet_id) - - def _sync_lbaas_members_impl(self, m_get_drv_lbaas, m_get_drv_project, - m_get_drv_subnets, subnet_id, project_id, - crd): - m_drv_lbaas = mock.Mock(wraps=FakeLBaaSDriver()) - m_drv_project = mock.Mock() - m_drv_project.get_project.return_value = project_id - m_drv_subnets = mock.Mock() - m_drv_subnets.get_subnets.return_value = { - subnet_id: mock.sentinel.subnet} - m_get_drv_lbaas.return_value = m_drv_lbaas - m_get_drv_project.return_value = m_drv_project - m_get_drv_subnets.return_value = m_drv_subnets - - handler = h_lb.KuryrLoadBalancerHandler() - - with mock.patch.object(handler, '_get_pod_subnet') as m_get_pod_subnet: - m_get_pod_subnet.return_value = subnet_id - handler._sync_lbaas_members(crd) - - lsnrs = {lsnr['id']: lsnr for lsnr in crd['status']['listeners']} - pools = {pool['id']: pool for pool in crd['status']['pools']} - observed_targets = sorted( - (str(member['ip']), ( - lsnrs[pools[member['pool_id']]['listener_id']]['port'], - member['port'])) - for member in crd['status']['members']) - return observed_targets - - @mock.patch('kuryr_kubernetes.utils.get_subnet_cidr') - @mock.patch('kuryr_kubernetes.controller.drivers.base.' - 'ServiceSecurityGroupsDriver.get_instance') - @mock.patch('kuryr_kubernetes.controller.drivers.base.' - 'ServiceProjectDriver.get_instance') - @mock.patch('kuryr_kubernetes.clients.get_kubernetes_client') - @mock.patch('kuryr_kubernetes.controller.drivers.base' - '.PodSubnetsDriver.get_instance') - @mock.patch('kuryr_kubernetes.controller.drivers.base' - '.PodProjectDriver.get_instance') - @mock.patch('kuryr_kubernetes.controller.drivers.base' - '.LBaaSDriver.get_instance') - @mock.patch('kuryr_kubernetes.controller.drivers.base' - '.NodesSubnetsDriver.get_instance', mock.Mock()) - def test_sync_lbaas_members(self, m_get_drv_lbaas, m_get_drv_project, - m_get_drv_subnets, m_k8s, m_svc_project_drv, - m_svc_sg_drv, m_get_cidr): - # REVISIT(ivc): test methods separately and verify ensure/release - m_get_cidr.return_value = '10.0.0.128/26' - project_id = str(uuid.uuid4()) - subnet_id = str(uuid.uuid4()) - expected_ip = '1.2.3.4' - expected_targets = { - '1.1.1.1': (1, 2), - '1.1.1.1': (1, 2), - '1.1.1.1': (1, 2)} - crd = get_lb_crd() - - observed_targets = self._sync_lbaas_members_impl( - m_get_drv_lbaas, m_get_drv_project, m_get_drv_subnets, - subnet_id, project_id, crd) - - self.assertEqual(sorted(expected_targets.items()), observed_targets) - self.assertEqual(expected_ip, str(crd['status']['loadbalancer']['ip'])) - - @mock.patch('kuryr_kubernetes.utils.get_subnet_cidr') - @mock.patch('kuryr_kubernetes.controller.drivers.base.' - 'ServiceSecurityGroupsDriver.get_instance') - @mock.patch('kuryr_kubernetes.controller.drivers.base.' - 'ServiceProjectDriver.get_instance') - @mock.patch('kuryr_kubernetes.clients.get_kubernetes_client') - @mock.patch('kuryr_kubernetes.controller.drivers.base' - '.PodSubnetsDriver.get_instance') - @mock.patch('kuryr_kubernetes.controller.drivers.base' - '.PodProjectDriver.get_instance') - @mock.patch('kuryr_kubernetes.controller.drivers.base' - '.LBaaSDriver.get_instance') - @mock.patch('kuryr_kubernetes.controller.drivers.base' - '.NodesSubnetsDriver.get_instance', mock.Mock()) - def test_sync_lbaas_members_udp(self, m_get_drv_lbaas, - m_get_drv_project, m_get_drv_subnets, - m_k8s, m_svc_project_drv, m_svc_sg_drv, - m_get_cidr): - # REVISIT(ivc): test methods separately and verify ensure/release - m_get_cidr.return_value = '10.0.0.128/26' - project_id = str(uuid.uuid4()) - subnet_id = str(uuid.uuid4()) - expected_ip = "1.2.3.4" - expected_targets = { - '1.1.1.1': (1, 2), - '1.1.1.1': (1, 2), - '1.1.1.1': (1, 2)} - - crd = get_lb_crd() - - observed_targets = self._sync_lbaas_members_impl( - m_get_drv_lbaas, m_get_drv_project, m_get_drv_subnets, - subnet_id, project_id, crd) - - self.assertEqual(sorted(expected_targets.items()), observed_targets) - self.assertEqual(expected_ip, str(crd['status']['loadbalancer']['ip'])) - - @mock.patch('kuryr_kubernetes.utils.get_subnet_cidr') - @mock.patch('kuryr_kubernetes.controller.drivers.base.' - 'ServiceSecurityGroupsDriver.get_instance') - @mock.patch('kuryr_kubernetes.controller.drivers.base.' - 'ServiceProjectDriver.get_instance') - @mock.patch('kuryr_kubernetes.clients.get_kubernetes_client') - @mock.patch('kuryr_kubernetes.controller.drivers.base' - '.PodSubnetsDriver.get_instance') - @mock.patch('kuryr_kubernetes.controller.drivers.base' - '.PodProjectDriver.get_instance') - @mock.patch('kuryr_kubernetes.controller.drivers.base' - '.LBaaSDriver.get_instance') - @mock.patch('kuryr_kubernetes.controller.drivers.base' - '.NodesSubnetsDriver.get_instance', mock.Mock()) - def test_sync_lbaas_members_svc_listener_port_edit( - self, m_get_drv_lbaas, m_get_drv_project, m_get_drv_subnets, - m_k8s, m_svc_project_drv, m_svc_sg_drv, m_get_cidr): - # REVISIT(ivc): test methods separately and verify ensure/release - m_get_cidr.return_value = '10.0.0.128/26' - project_id = str(uuid.uuid4()) - subnet_id = str(uuid.uuid4()) - expected_ip = '1.2.3.4' - crd = get_lb_crd() - - m_drv_lbaas = mock.Mock(wraps=FakeLBaaSDriver()) - m_drv_project = mock.Mock() - m_drv_project.get_project.return_value = project_id - m_drv_subnets = mock.Mock() - m_drv_subnets.get_subnets.return_value = { - subnet_id: mock.sentinel.subnet} - m_get_drv_lbaas.return_value = m_drv_lbaas - m_get_drv_project.return_value = m_drv_project - m_get_drv_subnets.return_value = m_drv_subnets - - handler = h_lb.KuryrLoadBalancerHandler() - - with mock.patch.object(handler, '_get_pod_subnet') as m_get_pod_subnet: - m_get_pod_subnet.return_value = subnet_id - handler._sync_lbaas_members(crd) - - self.assertEqual(expected_ip, str(crd['status']['loadbalancer']['ip'])) - - @mock.patch('kuryr_kubernetes.utils.get_subnet_cidr') - @mock.patch('kuryr_kubernetes.controller.drivers.base.' - 'ServiceSecurityGroupsDriver.get_instance') - @mock.patch('kuryr_kubernetes.controller.drivers.base.' - 'ServiceProjectDriver.get_instance') - @mock.patch('kuryr_kubernetes.clients.get_kubernetes_client') - @mock.patch('kuryr_kubernetes.controller.drivers.base' - '.PodSubnetsDriver.get_instance') - @mock.patch('kuryr_kubernetes.controller.drivers.base' - '.PodProjectDriver.get_instance') - @mock.patch('kuryr_kubernetes.controller.drivers.base' - '.LBaaSDriver.get_instance') - @mock.patch('kuryr_kubernetes.controller.drivers.base' - '.NodesSubnetsDriver.get_instance', mock.Mock()) - def test_add_new_members_udp(self, m_get_drv_lbaas, - m_get_drv_project, m_get_drv_subnets, - m_k8s, m_svc_project_drv, - m_svc_sg_drv, m_get_cidr): - m_get_cidr.return_value = '10.0.0.128/26' - project_id = str(uuid.uuid4()) - subnet_id = str(uuid.uuid4()) - crd = get_lb_crd() - - m_drv_lbaas = mock.Mock(wraps=FakeLBaaSDriver()) - m_drv_project = mock.Mock() - m_drv_project.get_project.return_value = project_id - m_drv_subnets = mock.Mock() - m_drv_subnets.get_subnets.return_value = { - subnet_id: mock.sentinel.subnet} - m_get_drv_lbaas.return_value = m_drv_lbaas - m_get_drv_project.return_value = m_drv_project - m_get_drv_subnets.return_value = m_drv_subnets - - handler = h_lb.KuryrLoadBalancerHandler() - member_added = handler._add_new_members(crd) - - self.assertEqual(member_added, False) - m_drv_lbaas.ensure_member.assert_not_called() - - @mock.patch('kuryr_kubernetes.utils.get_res_link') - @mock.patch('kuryr_kubernetes.clients.get_kubernetes_client') - @mock.patch('kuryr_kubernetes.controller.drivers.base' - '.LBaaSDriver.get_instance') - def test_reconcile_loadbalancers(self, m_get_drv_lbaas, m_k8s, - m_get_res_link): - loadbalancer_crds = get_lb_crds() - m_handler = mock.MagicMock(spec=h_lb.KuryrLoadBalancerHandler) - m_handler._drv_lbaas = m_get_drv_lbaas - lbaas = self.useFixture(k_fix.MockLBaaSClient()).client - lbaas.load_balancers.return_value = [] - lbaas.listeners.return_value = [] - lbaas.pools.return_value = [] - lbaas.members.return_value = [] - selflink = ('/apis/openstack.org/v1/namespaces/default/' - 'kuryrloadbalancers/test') - m_get_res_link.return_value = selflink - h_lb.KuryrLoadBalancerHandler._trigger_reconciliation( - m_handler, loadbalancer_crds) - filters = {} - lbaas.load_balancers.assert_called_once_with(**filters) - m_handler._reconcile_lb.assert_called_with({'id': mock.ANY, - 'selflink': selflink, - 'klb': mock.ANY}) - - @mock.patch('kuryr_kubernetes.clients.get_kubernetes_client') - @mock.patch('kuryr_kubernetes.controller.drivers.base' - '.LBaaSDriver.get_instance') - def test_reconcile_loadbalancers_in_sync(self, m_get_drv_lbaas, m_k8s): - loadbalancer_crds = get_lb_crds() - - m_handler = mock.MagicMock(spec=h_lb.KuryrLoadBalancerHandler) - m_handler._drv_lbaas = m_get_drv_lbaas - lbaas = self.useFixture(k_fix.MockLBaaSClient()).client - - loadbalancers_id = [{'id': '01234567890'}, {'id': '01234567891'}] - listeners_id = [{'id': '012345678912'}, {'id': '012345678913'}] - pools_id = [{'id': '1234567890'}, {'id': '1234567891'}] - members_id = [{"id": "0123456789a"}, {"id": "0123456789b"}] - lbaas.load_balancers.return_value = loadbalancers_id - lbaas.listeners.return_value = listeners_id - lbaas.pools.return_value = pools_id - lbaas.members.return_value = members_id - - h_lb.KuryrLoadBalancerHandler._trigger_reconciliation( - m_handler, loadbalancer_crds) - m_handler._reconcile_lb.assert_not_called() diff --git a/kuryr_kubernetes/tests/unit/controller/handlers/test_machine.py b/kuryr_kubernetes/tests/unit/controller/handlers/test_machine.py deleted file mode 100644 index bb3a1aea5..000000000 --- a/kuryr_kubernetes/tests/unit/controller/handlers/test_machine.py +++ /dev/null @@ -1,84 +0,0 @@ -# Copyright 2020 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 unittest import mock - -from kuryr_kubernetes import constants -from kuryr_kubernetes.controller.handlers import machine -from kuryr_kubernetes import exceptions -from kuryr_kubernetes.tests import base as test_base - - -class TestKuryrMachineHandler(test_base.TestCase): - @mock.patch( - 'kuryr_kubernetes.controller.drivers.base.NodesSubnetsDriver.' - 'get_instance') - def setUp(self, m_get_instance): - super(TestKuryrMachineHandler, self).setUp() - self.driver = mock.Mock() - m_get_instance.return_value = self.driver - self.handler = machine.MachineHandler() - - def test_on_present(self): - self.handler._bump_nps = mock.Mock() - self.driver.add_node.return_value = False - self.handler.on_present(mock.sentinel.machine) - self.driver.add_node.assert_called_once_with(mock.sentinel.machine) - self.handler._bump_nps.assert_not_called() - - def test_on_present_new(self): - self.handler._bump_nps = mock.Mock() - self.driver.add_node.return_value = True - self.handler.on_present(mock.sentinel.machine) - self.driver.add_node.assert_called_once_with(mock.sentinel.machine) - self.handler._bump_nps.assert_called_once() - - def test_on_deleted(self): - self.handler._bump_nps = mock.Mock() - self.driver.delete_node.return_value = False - self.handler.on_deleted(mock.sentinel.machine) - self.driver.delete_node.assert_called_once_with(mock.sentinel.machine) - self.handler._bump_nps.assert_not_called() - - def test_on_deleted_gone(self): - self.handler._bump_nps = mock.Mock() - self.driver.delete_node.return_value = True - self.handler.on_deleted(mock.sentinel.machine) - self.driver.delete_node.assert_called_once_with(mock.sentinel.machine) - self.handler._bump_nps.assert_called_once() - - @mock.patch('kuryr_kubernetes.clients.get_kubernetes_client') - def test_bump_nps(self, get_client): - m_k8s = mock.Mock() - get_client.return_value = m_k8s - m_k8s.get.return_value = { - 'items': [ - {'metadata': {'annotations': { - 'networkPolicyLink': mock.sentinel.link1}}}, - {'metadata': {'annotations': { - 'networkPolicyLink': mock.sentinel.link2}}}, - {'metadata': {'annotations': { - 'networkPolicyLink': mock.sentinel.link3}}}, - ] - } - m_k8s.annotate.side_effect = ( - None, exceptions.K8sResourceNotFound('NP'), None) - self.handler._bump_nps() - m_k8s.get.assert_called_once_with( - constants.K8S_API_CRD_KURYRNETWORKPOLICIES) - m_k8s.annotate.assert_has_calls([ - mock.call(mock.sentinel.link1, mock.ANY), - mock.call(mock.sentinel.link2, mock.ANY), - mock.call(mock.sentinel.link3, mock.ANY), - ]) diff --git a/kuryr_kubernetes/tests/unit/controller/handlers/test_namespace.py b/kuryr_kubernetes/tests/unit/controller/handlers/test_namespace.py deleted file mode 100644 index c06b3fdfb..000000000 --- a/kuryr_kubernetes/tests/unit/controller/handlers/test_namespace.py +++ /dev/null @@ -1,139 +0,0 @@ -# Copyright (c) 2018 Red Hat, 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. - -from unittest import mock - -from kuryr_kubernetes.controller.drivers import base as drivers -from kuryr_kubernetes.controller.handlers import namespace -from kuryr_kubernetes import exceptions as k_exc -from kuryr_kubernetes.tests import base as test_base - - -class TestNamespaceHandler(test_base.TestCase): - - def setUp(self): - super(TestNamespaceHandler, self).setUp() - - self._project_id = mock.sentinel.project_id - self._subnets = mock.sentinel.subnets - - self._namespace_version = mock.sentinel.namespace_version - self._namespace_link = mock.sentinel.namespace_link - - self._namespace_name = 'ns-test' - self._namespace = { - 'metadata': {'name': self._namespace_name, - 'resourceVersion': self._namespace_version}, - 'status': {'phase': 'Active'} - } - self._crd_id = 'ns-' + self._namespace_name - - self._handler = mock.MagicMock(spec=namespace.NamespaceHandler) - - self._handler._drv_project = mock.Mock( - spec=drivers.NamespaceProjectDriver) - - self._get_project = self._handler._drv_project.get_project - self._update_labels = self._handler._update_labels - self._get_kns_crd = self._handler._get_kns_crd - self._add_kuryrnetwork_crd = self._handler._add_kuryrnetwork_crd - self._handle_namespace = self._handler._handle_namespace - - self._get_project.return_value = self._project_id - - def _get_crd(self): - crd = { - 'kind': 'KuryrNetwork', - 'metadata': { - 'name': self._namespace_name, - 'namespace': self._namespace_name, - }, - 'spec': {} - } - return crd - - @mock.patch.object(drivers.NamespaceProjectDriver, 'get_instance') - def test_init(self, m_get_project_driver): - project_driver = mock.sentinel.project_driver - m_get_project_driver.return_value = project_driver - - handler = namespace.NamespaceHandler() - self.assertEqual(project_driver, handler._drv_project) - - def test_on_present(self): - self._get_kns_crd.return_value = None - self._handle_namespace.return_value = True - - namespace.NamespaceHandler.on_present(self._handler, self._namespace) - - self._handle_namespace.assert_called_once() - self._get_kns_crd.assert_called_once_with( - self._namespace['metadata']['name']) - self._add_kuryrnetwork_crd.assert_called_once_with( - self._namespace, {}) - - def test_on_present_existing(self): - net_crd = self._get_crd() - self._get_kns_crd.return_value = net_crd - - namespace.NamespaceHandler.on_present(self._handler, self._namespace) - - self._handle_namespace.assert_not_called() - self._get_kns_crd.assert_called_once_with( - self._namespace['metadata']['name']) - self._update_labels.assert_called_once_with(net_crd, {}) - self._add_kuryrnetwork_crd.assert_not_called() - - def test_on_present_add_kuryrnetwork_crd_exception(self): - self._get_kns_crd.return_value = None - self._add_kuryrnetwork_crd.side_effect = k_exc.K8sClientException - self._handle_namespace.return_value = True - - self.assertRaises(k_exc.ResourceNotReady, - namespace.NamespaceHandler.on_present, - self._handler, self._namespace) - - self._handle_namespace.assert_called_once() - self._get_kns_crd.assert_called_once_with( - self._namespace['metadata']['name']) - self._add_kuryrnetwork_crd.assert_called_once_with( - self._namespace, {}) - - @mock.patch('kuryr_kubernetes.clients.get_kubernetes_client') - def test_handle_namespace_no_pods(self, m_get_k8s_client): - k8s = mock.MagicMock() - m_get_k8s_client.return_value = k8s - k8s.get.return_value = {"items": []} - self.assertFalse(namespace.NamespaceHandler._handle_namespace( - self._handler, "test")) - k8s.get.assert_called_once() - - @mock.patch('kuryr_kubernetes.clients.get_kubernetes_client') - def test_handle_namespace_host_network_pods(self, m_get_k8s_client): - k8s = mock.MagicMock() - m_get_k8s_client.return_value = k8s - k8s.get.return_value = {"items": [{"spec": {"hostNetwork": True}}]} - self.assertFalse(namespace.NamespaceHandler._handle_namespace( - self._handler, "test")) - k8s.get.assert_called_once() - - @mock.patch('kuryr_kubernetes.clients.get_kubernetes_client') - def test_handle_namespace(self, m_get_k8s_client): - k8s = mock.MagicMock() - m_get_k8s_client.return_value = k8s - k8s.get.return_value = {"items": [{"spec": {}}]} - self.assertTrue(namespace.NamespaceHandler._handle_namespace( - self._handler, "test")) - k8s.get.assert_called_once() diff --git a/kuryr_kubernetes/tests/unit/controller/handlers/test_pipeline.py b/kuryr_kubernetes/tests/unit/controller/handlers/test_pipeline.py deleted file mode 100644 index b41d78691..000000000 --- a/kuryr_kubernetes/tests/unit/controller/handlers/test_pipeline.py +++ /dev/null @@ -1,61 +0,0 @@ -# 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. - -from unittest import mock - -from kuryr_kubernetes.controller.handlers import pipeline as h_pipeline -from kuryr_kubernetes.handlers import dispatch as h_dis -from kuryr_kubernetes.handlers import k8s_base as h_k8s -from kuryr_kubernetes.tests import base as test_base - - -class TestControllerPipeline(test_base.TestCase): - @mock.patch('kuryr_kubernetes.handlers.logging.LogExceptions') - @mock.patch('kuryr_kubernetes.handlers.retry.Retry') - def test_wrap_consumer(self, m_retry_type, m_logging_type): - consumer = mock.sentinel.consumer - retry_handler = mock.sentinel.retry_handler - logging_handler = mock.sentinel.logging_handler - m_retry_type.return_value = retry_handler - m_logging_type.return_value = logging_handler - thread_group = mock.sentinel.thread_group - - with mock.patch.object(h_dis.EventPipeline, '__init__'): - pipeline = h_pipeline.ControllerPipeline(thread_group) - ret = pipeline._wrap_consumer(consumer) - - self.assertEqual(logging_handler, ret) - m_logging_type.assert_called_with(retry_handler, - ignore_exceptions=mock.ANY) - m_retry_type.assert_called_with(consumer, exceptions=mock.ANY) - - @mock.patch('kuryr_kubernetes.handlers.logging.LogExceptions') - @mock.patch('kuryr_kubernetes.handlers.asynchronous.Async') - def test_wrap_dispatcher(self, m_async_type, m_logging_type): - dispatcher = mock.sentinel.dispatcher - async_handler = mock.sentinel.async_handler - logging_handler = mock.sentinel.logging_handler - m_async_type.return_value = async_handler - m_logging_type.return_value = logging_handler - thread_group = mock.sentinel.thread_group - - with mock.patch.object(h_dis.EventPipeline, '__init__'): - pipeline = h_pipeline.ControllerPipeline(thread_group) - ret = pipeline._wrap_dispatcher(dispatcher) - - self.assertEqual(logging_handler, ret) - m_logging_type.assert_called_with(async_handler) - m_async_type.assert_called_with(dispatcher, thread_group, - h_k8s.object_uid, h_k8s.object_info) diff --git a/kuryr_kubernetes/tests/unit/controller/handlers/test_pod_label.py b/kuryr_kubernetes/tests/unit/controller/handlers/test_pod_label.py deleted file mode 100644 index cfd716e41..000000000 --- a/kuryr_kubernetes/tests/unit/controller/handlers/test_pod_label.py +++ /dev/null @@ -1,126 +0,0 @@ -# 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 unittest import mock - -from kuryr_kubernetes import constants as k_const -from kuryr_kubernetes.controller.drivers import base as drivers -from kuryr_kubernetes.controller.handlers import pod_label as p_label -from kuryr_kubernetes.tests import base as test_base - - -class TestPodLabelHandler(test_base.TestCase): - - def setUp(self): - super(TestPodLabelHandler, self).setUp() - - self._project_id = mock.sentinel.project_id - self._sg_id = mock.sentinel.sg_id - - self._pod_version = mock.sentinel.pod_version - self._pod_link = mock.sentinel.pod_link - self._pod = { - 'metadata': {'resourceVersion': self._pod_version, - 'namespace': 'default'}, - 'status': {'phase': k_const.K8S_POD_STATUS_PENDING}, - 'spec': {'hostNetwork': False, - 'nodeName': 'hostname'} - } - self._handler = mock.MagicMock(spec=p_label.PodLabelHandler) - self._handler._drv_project = mock.Mock(spec=drivers.PodProjectDriver) - self._handler._drv_sg = mock.Mock(spec=drivers.PodSecurityGroupsDriver) - self._handler._drv_vif_pool = mock.MagicMock( - spec=drivers.VIFPoolDriver) - - self._get_project = self._handler._drv_project.get_project - self._get_security_groups = self._handler._drv_sg.get_security_groups - self._set_vif_driver = self._handler._drv_vif_pool.set_vif_driver - self._get_pod_info = self._handler._get_pod_info - self._set_pod_info = self._handler._set_pod_info - self._has_vifs = self._handler._has_vifs - self._update_vif_sgs = self._handler._drv_vif_pool.update_vif_sgs - - self._get_project.return_value = self._project_id - self._get_security_groups.return_value = [self._sg_id] - - @mock.patch.object(drivers.VIFPoolDriver, 'get_instance') - @mock.patch.object(drivers.PodSecurityGroupsDriver, 'get_instance') - @mock.patch.object(drivers.PodProjectDriver, 'get_instance') - @mock.patch.object(drivers.LBaaSDriver, 'get_instance') - def test_init(self, m_get_lbaas_driver, m_get_project_driver, - m_get_sg_driver, m_get_vif_pool_driver): - project_driver = mock.sentinel.project_driver - sg_driver = mock.sentinel.sg_driver - lbaas_driver = mock.sentinel.lbaas_driver - vif_pool_driver = mock.Mock(spec=drivers.VIFPoolDriver) - m_get_lbaas_driver.return_value = lbaas_driver - m_get_project_driver.return_value = project_driver - m_get_sg_driver.return_value = sg_driver - m_get_vif_pool_driver.return_value = vif_pool_driver - - handler = p_label.PodLabelHandler() - - self.assertEqual(lbaas_driver, handler._drv_lbaas) - self.assertEqual(project_driver, handler._drv_project) - self.assertEqual(sg_driver, handler._drv_sg) - self.assertEqual(vif_pool_driver, handler._drv_vif_pool) - - @mock.patch('kuryr_kubernetes.controller.drivers.utils.get_services') - def test_on_present(self, m_get_services): - m_get_services.return_value = {"items": []} - self._has_vifs.return_value = True - self._get_pod_info.return_value = ({'test1': 'test'}, '192.168.0.1') - - p_label.PodLabelHandler.on_present(self._handler, self._pod) - - self._has_vifs.assert_called_once_with(self._pod) - self._get_pod_info.assert_called_once_with(self._pod) - self._get_project.assert_called_once() - self._get_security_groups.assert_called_once() - self._update_vif_sgs.assert_called_once_with(self._pod, [self._sg_id]) - self._set_pod_info.assert_called_once_with(self._pod, (None, None)) - - def test_on_present_no_state(self): - self._has_vifs.return_value = False - - resp = p_label.PodLabelHandler.on_present(self._handler, self._pod) - - self.assertIsNone(resp) - self._has_vifs.assert_called_once_with(self._pod) - self._get_pod_info.assert_not_called() - self._set_pod_info.assert_not_called() - - @mock.patch('kuryr_kubernetes.controller.drivers.utils.get_services') - def test_on_present_no_labels(self, m_get_services): - self._has_vifs.return_value = True - self._get_pod_info.return_value = None, None - - p_label.PodLabelHandler.on_present(self._handler, self._pod) - - self._has_vifs.assert_called_once_with(self._pod) - self._get_pod_info.assert_called_once_with(self._pod) - self._set_pod_info.assert_not_called() - - def test_on_present_no_changes(self): - self._has_vifs.return_value = True - pod_with_label = self._pod.copy() - pod_with_label['metadata']['labels'] = {'test1': 'test'} - pod_with_label['status']['podIP'] = '192.168.0.1' - self._get_pod_info.return_value = ({'test1': 'test'}, '192.168.0.1') - - p_label.PodLabelHandler.on_present(self._handler, pod_with_label) - - self._has_vifs.assert_called_once_with(pod_with_label) - self._get_pod_info.assert_called_once_with(pod_with_label) - self._set_pod_info.assert_not_called() diff --git a/kuryr_kubernetes/tests/unit/controller/handlers/test_policy.py b/kuryr_kubernetes/tests/unit/controller/handlers/test_policy.py deleted file mode 100644 index 129d1665b..000000000 --- a/kuryr_kubernetes/tests/unit/controller/handlers/test_policy.py +++ /dev/null @@ -1,80 +0,0 @@ -# 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 unittest import mock - -from kuryr_kubernetes.controller.drivers import base as drivers -from kuryr_kubernetes.controller.handlers import policy -from kuryr_kubernetes.tests import base as test_base - - -class TestPolicyHandler(test_base.TestCase): - - @mock.patch.object(drivers.NetworkPolicyDriver, 'get_instance') - @mock.patch('kuryr_kubernetes.clients.get_kubernetes_client') - def setUp(self, m_get_k8s, m_get_np): - super(TestPolicyHandler, self).setUp() - - self._project_id = mock.sentinel.project_id - self._policy_name = 'np-test' - self._policy_uid = mock.sentinel.policy_uid - self._policy_link = mock.sentinel.policy_link - - self._policy = { - 'apiVersion': 'networking.k8s.io/v1', - 'kind': 'NetworkPolicy', - 'metadata': { - 'name': self._policy_name, - 'resourceVersion': '2259309', - 'generation': 1, - 'creationTimestamp': '2018-09-18T14:09:51Z', - 'namespace': 'default', - 'annotations': {}, - 'uid': self._policy_uid - }, - 'spec': { - 'egress': [{'ports': [{'port': 5978, 'protocol': 'TCP'}]}], - 'ingress': [{'ports': [{'port': 6379, 'protocol': 'TCP'}]}], - 'policyTypes': ['Ingress', 'Egress'] - } - } - - self.k8s = mock.Mock() - m_get_k8s.return_value = self.k8s - self.m_get_k8s = m_get_k8s - - self.np_driver = mock.Mock() - m_get_np.return_value = self.np_driver - self._m_get_np = m_get_np - - self.handler = policy.NetworkPolicyHandler() - - def test_init(self): - self.m_get_k8s.assert_called_once() - self._m_get_np.assert_called_once() - - self.assertEqual(self.np_driver, self.handler._drv_policy) - self.assertEqual(self.k8s, self.handler.k8s) - - def test_on_finalize(self): - self.handler.on_finalize(self._policy) - self.np_driver.release_network_policy.assert_called_once_with( - self._policy) - - def test_on_present(self): - self.handler.on_present(self._policy) - self.k8s.add_finalizer.assert_called_once_with( - self._policy, 'kuryr.openstack.org/networkpolicy-finalizer') - self.np_driver.ensure_network_policy.assert_called_once_with( - self._policy) diff --git a/kuryr_kubernetes/tests/unit/controller/handlers/test_vif.py b/kuryr_kubernetes/tests/unit/controller/handlers/test_vif.py deleted file mode 100644 index 58ddfd57b..000000000 --- a/kuryr_kubernetes/tests/unit/controller/handlers/test_vif.py +++ /dev/null @@ -1,282 +0,0 @@ -# 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. - -from unittest import mock - -from os_vif import objects as os_obj - -from kuryr_kubernetes import constants as k_const -from kuryr_kubernetes.controller.drivers import base as drivers -from kuryr_kubernetes.controller.handlers import vif as h_vif -from kuryr_kubernetes import exceptions as k_exc -from kuryr_kubernetes.objects import vif -from kuryr_kubernetes.tests import base as test_base -from kuryr_kubernetes.tests import fake - - -class TestVIFHandler(test_base.TestCase): - - def setUp(self): - super(TestVIFHandler, self).setUp() - - self._project_id = mock.sentinel.project_id - self._subnets = mock.sentinel.subnets - self._security_groups = mock.sentinel.security_groups - self._vif = os_obj.vif.VIFBase() - self._vif.active = True - self._vif_serialized = mock.sentinel.vif_serialized - self._multi_vif_drv = mock.MagicMock(spec=drivers.MultiVIFDriver) - self._additioan_vifs = [] - self._state = vif.PodState(default_vif=self._vif) - - self._pod_version = mock.sentinel.pod_version - self._pod_link = mock.sentinel.pod_link - self._pod_namespace = 'namespace1' - self._pod_uid = mock.sentinel.pod_uid - self._pod_name = 'pod1' - self._pod = fake.get_k8s_pod() - self._pod['status'] = {'phase': k_const.K8S_POD_STATUS_PENDING} - self._pod['spec'] = {'hostNetwork': False, 'nodeName': 'hostname'} - - self._kp_version = mock.sentinel.kp_version - self._kp_link = mock.sentinel.kp_link - self._kp = {'apiVersion': 'openstack.org/v1', - 'kind': 'KuryrPort', - 'metadata': {'resourceVersion': self._kp_version, - 'selfLink': mock.sentinel.kp_link, - 'namespace': self._pod_namespace, - 'labels': mock.ANY}, - 'spec': {'podUid': self._pod_uid, - 'podNodeName': 'hostname'}, - 'status': {'vifs': {}}} - - self._handler = mock.MagicMock(spec=h_vif.VIFHandler) - self._handler._drv_project = mock.Mock(spec=drivers.PodProjectDriver) - self._handler._drv_subnets = mock.Mock(spec=drivers.PodSubnetsDriver) - self._handler._drv_sg = mock.Mock(spec=drivers.PodSecurityGroupsDriver) - self._handler._drv_vif = mock.Mock(spec=drivers.PodVIFDriver) - self._handler._drv_vif_pool = mock.MagicMock( - spec=drivers.VIFPoolDriver) - self._handler._drv_multi_vif = [self._multi_vif_drv] - self._handler.k8s = mock.Mock() - - self._get_project = self._handler._drv_project.get_project - self._get_subnets = self._handler._drv_subnets.get_subnets - self._get_security_groups = self._handler._drv_sg.get_security_groups - self._set_vifs_driver = self._handler._drv_vif_pool.set_vif_driver - self._request_vif = self._handler._drv_vif_pool.request_vif - self._release_vif = self._handler._drv_vif_pool.release_vif - self._activate_vif = self._handler._drv_vif_pool.activate_vif - self._is_pod_scheduled = self._handler._is_pod_scheduled - self._request_additional_vifs = \ - self._multi_vif_drv.request_additional_vifs - - self._request_vif.return_value = self._vif - self._request_additional_vifs.return_value = self._additioan_vifs - self._is_pod_scheduled.return_value = True - self._get_project.return_value = self._project_id - self._get_subnets.return_value = self._subnets - self._get_security_groups.return_value = self._security_groups - self._set_vifs_driver.return_value = mock.Mock( - spec=drivers.PodVIFDriver) - - def test_is_pod_scheduled(self): - self.assertTrue(h_vif.VIFHandler._is_pod_scheduled(self._pod)) - - def test_is_not_pending(self): - self._pod['status']['phase'] = 'Unknown' - self.assertFalse(h_vif.VIFHandler._is_pod_scheduled(self._pod)) - - def test_is_pending_no_node(self): - self._pod['spec']['nodeName'] = None - self.assertFalse(h_vif.VIFHandler._is_pod_scheduled(self._pod)) - - def test_unset_pending(self): - self.assertFalse(h_vif.VIFHandler._is_pod_scheduled({'spec': {}, - 'status': {}})) - - @mock.patch('kuryr_kubernetes.clients.get_kubernetes_client') - @mock.patch('kuryr_kubernetes.utils.is_host_network') - @mock.patch('kuryr_kubernetes.controller.drivers.utils.get_kuryrport') - def test_on_present_host_network(self, m_get_kuryrport, m_host_network, - m_get_k8s_client): - m_get_kuryrport.return_value = self._kp - m_host_network.return_value = True - k8s = mock.MagicMock() - m_get_k8s_client.return_value = k8s - - h_vif.VIFHandler.on_present(self._handler, self._pod) - - k8s.add_finalizer.assert_not_called() - m_get_kuryrport.assert_not_called() - self._request_vif.assert_not_called() - self._request_additional_vifs.assert_not_called() - self._activate_vif.assert_not_called() - - @mock.patch('kuryr_kubernetes.controller.drivers.utils.get_k8s_resource') - @mock.patch('kuryr_kubernetes.utils.is_pod_completed') - @mock.patch('kuryr_kubernetes.utils.is_host_network') - @mock.patch('kuryr_kubernetes.controller.drivers.utils.get_kuryrport') - def test_on_present_not_scheduled(self, m_get_kuryrport, m_host_network, - m_is_pod_completed, m_get_k8s_res): - m_get_kuryrport.return_value = self._kp - m_host_network.return_value = False - m_is_pod_completed.return_value = False - m_get_k8s_res.return_value = {} - - h_vif.VIFHandler.on_present(self._handler, self._pod) - - self._handler.k8s.add_finalizer.assert_called() - m_get_kuryrport.assert_called() - self._request_vif.assert_not_called() - self._request_additional_vifs.assert_not_called() - self._activate_vif.assert_not_called() - - @mock.patch('kuryr_kubernetes.utils.is_pod_completed') - @mock.patch('kuryr_kubernetes.clients.get_kubernetes_client') - @mock.patch('kuryr_kubernetes.controller.drivers.utils.get_kuryrport') - def test_on_present_on_completed_without_kuryrport(self, m_get_kuryrport, - m_get_k8s_client, - m_is_pod_completed): - m_is_pod_completed.return_value = True - m_get_kuryrport.return_value = None - k8s = mock.MagicMock() - m_get_k8s_client.return_value = k8s - - h_vif.VIFHandler.on_present(self._handler, self._pod) - - self._handler.on_finalize.assert_called() - self._request_vif.assert_not_called() - self._request_additional_vifs.assert_not_called() - self._activate_vif.assert_not_called() - - @mock.patch('kuryr_kubernetes.utils.is_pod_completed') - @mock.patch('kuryr_kubernetes.clients.get_kubernetes_client') - @mock.patch('kuryr_kubernetes.controller.drivers.utils.get_kuryrport') - def test_on_present_on_completed_with_kuryrport(self, m_get_kuryrport, - m_get_k8s_client, - m_is_pod_completed): - m_is_pod_completed.return_value = True - m_get_kuryrport.return_value = mock.MagicMock() - k8s = mock.MagicMock() - m_get_k8s_client.return_value = k8s - - h_vif.VIFHandler.on_present(self._handler, self._pod) - - self._handler.on_finalize.assert_called() - self._request_vif.assert_not_called() - self._request_additional_vifs.assert_not_called() - self._activate_vif.assert_not_called() - - @mock.patch('kuryr_kubernetes.controller.drivers.utils.get_k8s_resource') - @mock.patch('kuryr_kubernetes.utils.is_host_network') - @mock.patch('kuryr_kubernetes.controller.drivers.utils.get_kuryrport') - def test_on_present_create(self, m_get_kuryrport, m_host_network, - m_get_k8s_res): - m_get_kuryrport.return_value = None - m_host_network.return_value = False - m_get_k8s_res.return_value = {} - - h_vif.VIFHandler.on_present(self._handler, self._pod) - - add_finalizer = self._handler.k8s.add_finalizer - add_finalizer.assert_called_once_with(self._pod, k_const.POD_FINALIZER) - m_get_kuryrport.assert_called_once_with(self._pod) - self._handler._add_kuryrport_crd.assert_called_once_with(self._pod) - - @mock.patch('kuryr_kubernetes.controller.drivers.utils.get_k8s_resource') - @mock.patch('kuryr_kubernetes.utils.is_host_network') - @mock.patch('kuryr_kubernetes.controller.drivers.utils.get_kuryrport') - def test_on_present_update(self, m_get_kuryrport, m_host_network, - m_get_k8s_res): - m_get_kuryrport.return_value = self._kp - m_host_network.return_value = False - m_get_k8s_res.return_value = {} - - h_vif.VIFHandler.on_present(self._handler, self._pod) - - add_finalizer = self._handler.k8s.add_finalizer - add_finalizer.assert_called_once_with(self._pod, k_const.POD_FINALIZER) - m_get_kuryrport.assert_called_once_with(self._pod) - self._handler._add_kuryrport_crd.assert_not_called() - - @mock.patch('kuryr_kubernetes.controller.drivers.utils.get_k8s_resource') - @mock.patch('kuryr_kubernetes.utils.is_host_network') - @mock.patch('kuryr_kubernetes.controller.drivers.utils.get_kuryrport') - def test_on_present_upgrade(self, m_get_kuryrport, m_host_network, - m_get_k8s_res): - m_get_kuryrport.return_value = self._kp - m_host_network.return_value = False - m_get_k8s_res.return_value = {} - - h_vif.VIFHandler.on_present(self._handler, self._pod) - - add_finalizer = self._handler.k8s.add_finalizer - add_finalizer.assert_called_once_with(self._pod, k_const.POD_FINALIZER) - m_get_kuryrport.assert_called() - self._request_vif.assert_not_called() - self._request_additional_vifs.assert_not_called() - self._activate_vif.assert_not_called() - - @mock.patch('kuryr_kubernetes.controller.drivers.utils.get_k8s_resource') - @mock.patch('kuryr_kubernetes.utils.is_host_network') - def test_on_present_pod_finalizer_exception(self, m_host_network, - m_get_k8s_res): - m_host_network.return_value = False - m_get_k8s_res.return_value = {} - self._handler.k8s.add_finalizer.side_effect = k_exc.K8sClientException - - self.assertRaises(k_exc.K8sClientException, - h_vif.VIFHandler.on_present, self._handler, - self._pod) - - add_finalizer = self._handler.k8s.add_finalizer - add_finalizer.assert_called_once_with(self._pod, k_const.POD_FINALIZER) - - def test_on_finalize_crd(self): - self._handler.k8s.get.return_value = self._kp - - h_vif.VIFHandler.on_finalize(self._handler, self._pod) - - self._handler.k8s.delete.assert_called_once_with( - h_vif.KURYRPORT_URI.format( - ns=self._pod["metadata"]["namespace"], - crd=self._pod["metadata"]["name"])) - - def test_on_finalize_crd_exception(self): - self._handler.k8s.get.return_value = self._kp - self._handler.k8s.delete.side_effect = k_exc.K8sClientException - - self.assertRaises(k_exc.ResourceNotReady, h_vif.VIFHandler - .on_finalize, self._handler, self._pod) - - self._handler.k8s.delete.assert_called_once_with( - h_vif.KURYRPORT_URI.format( - ns=self._pod["metadata"]["namespace"], - crd=self._pod["metadata"]["name"])) - - def test_on_finalize_crd_not_found(self): - self._handler.k8s.get.return_value = self._kp - (self._handler.k8s.delete - .side_effect) = k_exc.K8sResourceNotFound(self._pod) - - h_vif.VIFHandler.on_finalize(self._handler, self._pod) - - self._handler.k8s.delete.assert_called_once_with( - h_vif.KURYRPORT_URI.format( - ns=self._pod["metadata"]["namespace"], - crd=self._pod["metadata"]["name"])) - (self._handler.k8s.remove_finalizer - .assert_called_once_with(self._pod, k_const.POD_FINALIZER)) diff --git a/kuryr_kubernetes/tests/unit/controller/managers/__init__.py b/kuryr_kubernetes/tests/unit/controller/managers/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/kuryr_kubernetes/tests/unit/controller/managers/test_health.py b/kuryr_kubernetes/tests/unit/controller/managers/test_health.py deleted file mode 100644 index ff4a0c457..000000000 --- a/kuryr_kubernetes/tests/unit/controller/managers/test_health.py +++ /dev/null @@ -1,225 +0,0 @@ -# Copyright 2018 Maysa de Macedo Souza. -# -# 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 kuryr_kubernetes.controller.managers import health -from kuryr_kubernetes.handlers import health as h_health -from kuryr_kubernetes.tests import base -from kuryr_kubernetes.tests.unit import kuryr_fixtures as k_fix -from unittest import mock - -from oslo_config import cfg as oslo_cfg - - -def get_quota_obj(): - return { - 'quota': { - 'subnet': { - 'used': 50, - 'limit': 100, - 'reserved': 0 - }, - 'network': { - 'used': 50, - 'limit': 100, - 'reserved': 0 - }, - 'floatingip': { - 'used': 25, - 'limit': 50, - 'reserved': 0 - }, - 'subnetpool': { - 'used': 0, - 'limit': -1, - 'reserved': 0 - }, - 'security_group_rule': { - 'used': 50, - 'limit': 100, - 'reserved': 0 - }, - 'security_group': { - 'used': 5, - 'limit': 10, - 'reserved': 0 - }, - 'router': { - 'used': 5, - 'limit': 10, - 'reserved': 0 - }, - 'rbac_policy': { - 'used': 5, - 'limit': 10, - 'reserved': 0 - }, - 'port': { - 'used': 250, - 'limit': 500, - 'reserved': 0 - } - } - } - - -class _TestHandler(h_health.HealthHandler): - def is_alive(self): - pass - - def is_ready(self): - pass - - -class TestHealthServer(base.TestCase): - - def setUp(self): - super(TestHealthServer, self).setUp() - self.srv = health.HealthServer() - self.srv.application.testing = True - self.test_client = self.srv.application.test_client() - - @mock.patch('kuryr_kubernetes.controller.managers.health.HealthServer.' - '_components_ready') - @mock.patch('os.path.exists') - @mock.patch('kuryr_kubernetes.controller.managers.health.HealthServer.' - 'verify_keystone_connection') - @mock.patch('kuryr_kubernetes.controller.managers.health.HealthServer.' - 'verify_k8s_connection') - def test_readiness(self, m_verify_k8s_conn, m_verify_keystone_conn, - m_exist, m_components_ready): - m_verify_k8s_conn.return_value = True, 200 - m_exist.return_value = True - m_components_ready.return_value = True - - resp = self.test_client.get('/ready') - - m_verify_k8s_conn.assert_called_once() - m_verify_keystone_conn.assert_called_once() - m_components_ready.assert_called_once() - - self.assertEqual(200, resp.status_code) - self.assertEqual('ok', resp.data.decode()) - - @mock.patch('os.path.exists') - def test_readiness_not_found(self, m_exist): - m_exist.return_value = False - oslo_cfg.CONF.set_override('vif_pool_driver', 'neutron', - group='kubernetes') - resp = self.test_client.get('/ready') - self.assertEqual(404, resp.status_code) - - @mock.patch('kuryr_kubernetes.controller.managers.health.HealthServer.' - 'verify_k8s_connection') - @mock.patch('os.path.exists') - def test_readiness_k8s_error(self, m_exist, m_verify_k8s_conn): - m_exist.return_value = True - m_verify_k8s_conn.return_value = False - resp = self.test_client.get('/ready') - - m_verify_k8s_conn.assert_called_once() - self.assertEqual(500, resp.status_code) - - @mock.patch('kuryr_kubernetes.controller.managers.health.HealthServer.' - 'verify_keystone_connection') - @mock.patch('kuryr_kubernetes.controller.managers.health.HealthServer.' - 'verify_k8s_connection') - @mock.patch('os.path.exists') - def test_readiness_unauthorized(self, m_exist, m_verify_k8s_conn, - m_verify_keystone_conn): - m_exist.return_value = True - m_verify_k8s_conn.return_value = True, 200 - m_verify_keystone_conn.side_effect = Exception - resp = self.test_client.get('/ready') - - m_verify_keystone_conn.assert_called_once() - self.assertEqual(500, resp.status_code) - - @mock.patch('kuryr_kubernetes.controller.managers.health.HealthServer.' - '_components_ready') - @mock.patch('os.path.exists') - @mock.patch('kuryr_kubernetes.controller.managers.health.HealthServer.' - 'verify_keystone_connection') - @mock.patch('kuryr_kubernetes.controller.managers.health.HealthServer.' - 'verify_k8s_connection') - def test_readiness_neutron_error(self, m_verify_k8s_conn, - m_verify_keystone_conn, - m_exist, m_components_ready): - m_components_ready.side_effect = Exception - - resp = self.test_client.get('/ready') - - m_components_ready.assert_called_once() - self.assertEqual(500, resp.status_code) - - @mock.patch('kuryr_kubernetes.controller.managers.health.HealthServer.' - '_components_ready') - @mock.patch('os.path.exists') - @mock.patch('kuryr_kubernetes.controller.managers.health.HealthServer.' - 'verify_keystone_connection') - @mock.patch('kuryr_kubernetes.controller.managers.health.HealthServer.' - 'verify_k8s_connection') - def test_readiness_components_ready_error(self, m_verify_k8s_conn, - m_verify_keystone_conn, - m_exist, m_components_ready): - m_components_ready.return_value = False - - resp = self.test_client.get('/ready') - - m_components_ready.assert_called_once() - self.assertEqual(500, resp.status_code) - - @mock.patch.object(_TestHandler, 'is_ready') - def test__components_ready(self, m_status): - os_net = self.useFixture(k_fix.MockNetworkClient()).client - os_net.get_quota.return_value = get_quota_obj() - self.srv._registry = [_TestHandler()] - m_status.return_value = True - - resp = self.srv._components_ready() - - m_status.assert_called_once() - self.assertIs(resp, True) - os_net.get_quota.assert_called_once() - - @mock.patch.object(_TestHandler, 'is_ready') - def test__components_ready_error(self, m_status): - os_net = self.useFixture(k_fix.MockNetworkClient()).client - os_net.get_quota.return_value = get_quota_obj() - self.srv._registry = [_TestHandler()] - m_status.return_value = False - - resp = self.srv._components_ready() - - m_status.assert_called_once() - self.assertIs(resp, False) - os_net.get_quota.assert_called_once() - - @mock.patch.object(_TestHandler, 'is_alive') - def test_liveness(self, m_status): - m_status.return_value = True - self.srv._registry = [_TestHandler()] - - resp = self.test_client.get('/alive') - - m_status.assert_called_once() - self.assertEqual(200, resp.status_code) - - @mock.patch.object(_TestHandler, 'is_alive') - def test_liveness_error(self, m_status): - m_status.return_value = False - self.srv._registry = [_TestHandler()] - resp = self.test_client.get('/alive') - - m_status.assert_called_once() - self.assertEqual(500, resp.status_code) diff --git a/kuryr_kubernetes/tests/unit/controller/managers/test_pool.py b/kuryr_kubernetes/tests/unit/controller/managers/test_pool.py deleted file mode 100644 index d00395a8d..000000000 --- a/kuryr_kubernetes/tests/unit/controller/managers/test_pool.py +++ /dev/null @@ -1,355 +0,0 @@ -# Copyright 2017 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 unittest import mock - -from http.server import BaseHTTPRequestHandler - -from oslo_serialization import jsonutils - -from kuryr_kubernetes.controller.managers import pool as m_pool -from kuryr_kubernetes.tests import base as test_base - - -class TestRequestHandler(test_base.TestCase): - - def setUp(self): - super(TestRequestHandler, self).setUp() - client_address = 'localhost' - server = '/tmp/server.lock' - req = mock.MagicMock() - with mock.patch.object(BaseHTTPRequestHandler, '__init__') as m_http: - m_http.return_value = None - self._req_handler = m_pool.RequestHandler(req, client_address, - server) - self._req_handler.rfile = mock.Mock() - self._req_handler.wfile = mock.Mock() - - def _do_POST_helper(self, method, path, headers, body, expected_resp, - trigger_exception, trunk_ips, num_ports=None): - self._req_handler.headers = headers - self._req_handler.path = path - - with mock.patch.object(self._req_handler.rfile, 'read') as m_read,\ - mock.patch.object(self._req_handler, - '_create_subports') as m_create,\ - mock.patch.object(self._req_handler, - '_delete_subports') as m_delete: - m_read.return_value = body - if trigger_exception: - m_create.side_effect = Exception - m_delete.side_effect = Exception - - with mock.patch.object(self._req_handler, - 'send_header') as m_send_header,\ - mock.patch.object(self._req_handler, - 'end_headers') as m_end_headers,\ - mock.patch.object(self._req_handler.wfile, - 'write') as m_write: - self._req_handler.do_POST() - - if method == 'create': - if trunk_ips: - m_create.assert_called_once_with(num_ports, trunk_ips) - else: - m_create.assert_not_called() - if method == 'delete': - m_delete.assert_called_once_with(trunk_ips) - - m_send_header.assert_called_once_with('Content-Length', - len(expected_resp)) - m_end_headers.assert_called_once() - m_write.assert_called_once_with(expected_resp) - - def _do_GET_helper(self, method, method_resp, path, headers, body, - expected_resp, trigger_exception, pool_key=None): - self._req_handler.headers = headers - self._req_handler.path = path - - with mock.patch.object(self._req_handler.rfile, 'read') as m_read,\ - mock.patch.object(self._req_handler, - '_list_pools') as m_list,\ - mock.patch.object(self._req_handler, - '_show_pool') as m_show: - m_read.return_value = body - if trigger_exception: - m_list.side_effect = Exception - m_show.side_effect = Exception - else: - m_list.return_value = method_resp - m_show.return_value = method_resp - - with mock.patch.object(self._req_handler, - 'send_header') as m_send_header,\ - mock.patch.object(self._req_handler, - 'end_headers') as m_end_headers,\ - mock.patch.object(self._req_handler.wfile, - 'write') as m_write: - self._req_handler.do_GET() - - if method == 'list': - m_list.assert_called_once() - if method == 'show': - if pool_key and len(pool_key) == 3: - m_show.assert_called_once_with( - (pool_key[0], pool_key[1], - tuple(sorted(pool_key[2])))) - else: - m_show.assert_not_called() - - m_send_header.assert_called_once_with('Content-Length', - len(expected_resp)) - m_end_headers.assert_called_once() - m_write.assert_called_once_with(expected_resp) - - def test_do_POST_populate(self): - method = 'create' - path = "http://localhost/populatePool" - trunk_ips = ["10.0.0.6"] - num_ports = 3 - body = jsonutils.dumps({"trunks": trunk_ips, - "num_ports": num_ports}) - headers = {'Content-Type': 'application/json', 'Connection': 'close'} - headers['Content-Length'] = len(body) - trigger_exception = False - - expected_resp = ('Ports pool at {} was populated with 3 ports.' - .format(trunk_ips)).encode() - - self._do_POST_helper(method, path, headers, body, expected_resp, - trigger_exception, trunk_ips, num_ports) - - def test_do_POST_populate_exception(self): - method = 'create' - path = "http://localhost/populatePool" - trunk_ips = ["10.0.0.6"] - num_ports = 3 - body = jsonutils.dumps({"trunks": trunk_ips, - "num_ports": num_ports}) - headers = {'Content-Type': 'application/json', 'Connection': 'close'} - headers['Content-Length'] = len(body) - trigger_exception = True - - expected_resp = ('Error while populating pool {0} with {1} ports.' - .format(trunk_ips, num_ports)).encode() - - self._do_POST_helper(method, path, headers, body, expected_resp, - trigger_exception, trunk_ips, num_ports) - - def test_do_POST_populate_no_trunks(self): - method = 'create' - path = "http://localhost/populatePool" - trunk_ips = [] - num_ports = 3 - body = jsonutils.dumps({"trunks": trunk_ips, - "num_ports": num_ports}) - headers = {'Content-Type': 'application/json', 'Connection': 'close'} - headers['Content-Length'] = len(body) - trigger_exception = False - - expected_resp = ('Trunk port IP(s) missing.').encode() - - self._do_POST_helper(method, path, headers, body, expected_resp, - trigger_exception, trunk_ips, num_ports) - - def test_do_POST_free(self): - method = 'delete' - path = "http://localhost/freePool" - trunk_ips = ["10.0.0.6"] - body = jsonutils.dumps({"trunks": trunk_ips}) - headers = {'Content-Type': 'application/json', 'Connection': 'close'} - headers['Content-Length'] = len(body) - trigger_exception = False - - expected_resp = ('Ports pool belonging to {0} was freed.' - .format(trunk_ips)).encode() - - self._do_POST_helper(method, path, headers, body, expected_resp, - trigger_exception, trunk_ips) - - def test_do_POST_free_exception(self): - method = 'delete' - path = "http://localhost/freePool" - trunk_ips = ["10.0.0.6"] - body = jsonutils.dumps({"trunks": trunk_ips}) - headers = {'Content-Type': 'application/json', 'Connection': 'close'} - headers['Content-Length'] = len(body) - trigger_exception = True - - expected_resp = ('Error freeing ports pool: {0}.' - .format(trunk_ips)).encode() - - self._do_POST_helper(method, path, headers, body, expected_resp, - trigger_exception, trunk_ips) - - def test_do_POST_free_no_trunks(self): - method = 'delete' - path = "http://localhost/freePool" - trunk_ips = [] - body = jsonutils.dumps({"trunks": trunk_ips}) - headers = {'Content-Type': 'application/json', 'Connection': 'close'} - headers['Content-Length'] = len(body) - trigger_exception = False - - expected_resp = ('Ports pool belonging to all was freed.').encode() - - self._do_POST_helper(method, path, headers, body, expected_resp, - trigger_exception, trunk_ips) - - def test_do_POST_wrong_action(self): - method = 'fake' - path = "http://localhost/fakeMethod" - trunk_ips = ["10.0.0.6"] - body = jsonutils.dumps({"trunks": trunk_ips}) - headers = {'Content-Type': 'application/json', 'Connection': 'close'} - headers['Content-Length'] = len(body) - trigger_exception = False - - expected_resp = ('Method not allowed.').encode() - - self._do_POST_helper(method, path, headers, body, expected_resp, - trigger_exception, trunk_ips) - - def test_do_GET_list(self): - method = 'list' - method_resp = ('["10.0.0.6", "9d2b45c4efaa478481c30340b49fd4d2", ' - '["00efc78c-f11c-414a-bfcd-a82e16dc07d1", ' - '"fd6b13dc-7230-4cbe-9237-36b4614bc6b5"]] ' - 'has 5 ports') - - path = "http://localhost/listPools" - body = jsonutils.dumps({}) - headers = {'Content-Type': 'application/json', 'Connection': 'close'} - headers['Content-Length'] = len(body) - trigger_exception = False - - expected_resp = ('Pools:\n{0}'.format(method_resp)).encode() - - self._do_GET_helper(method, method_resp, path, headers, body, - expected_resp, trigger_exception) - - def test_do_GET_list_exception(self): - method = 'list' - method_resp = ('["10.0.0.6", "9d2b45c4efaa478481c30340b49fd4d2", ' - '["00efc78c-f11c-414a-bfcd-a82e16dc07d1", ' - '"fd6b13dc-7230-4cbe-9237-36b4614bc6b5"]] ' - 'has 5 ports') - - path = "http://localhost/listPools" - body = jsonutils.dumps({}) - headers = {'Content-Type': 'application/json', 'Connection': 'close'} - headers['Content-Length'] = len(body) - trigger_exception = True - - expected_resp = ('Error listing the pools.').encode() - - self._do_GET_helper(method, method_resp, path, headers, body, - expected_resp, trigger_exception) - - def test_do_GET_list_empty(self): - method = 'list' - method_resp = 'There are no pools' - - path = "http://localhost/listPools" - body = jsonutils.dumps({}) - headers = {'Content-Type': 'application/json', 'Connection': 'close'} - headers['Content-Length'] = len(body) - trigger_exception = False - - expected_resp = ('Pools:\n{0}'.format(method_resp)).encode() - - self._do_GET_helper(method, method_resp, path, headers, body, - expected_resp, trigger_exception) - - def test_do_GET_show(self): - method = 'show' - method_resp = "251f748d-2a0d-4143-bce8-2e616f7a6a4a" - path = "http://localhost/showPool" - pool_key = ["10.0.0.6", "9d2b45c4efaa478481c30340b49fd4d2", - ["00efc78c-f11c-414a-bfcd-a82e16dc07d1", - "fd6b13dc-7230-4cbe-9237-36b4614bc6b5"]] - body = jsonutils.dumps({"pool_key": pool_key}) - headers = {'Content-Type': 'application/json', 'Connection': 'close'} - headers['Content-Length'] = len(body) - trigger_exception = False - formated_key = (pool_key[0], pool_key[1], tuple(sorted(pool_key[2]))) - expected_resp = ('Pool {0} ports are:\n{1}' - .format(formated_key, method_resp)).encode() - - self._do_GET_helper(method, method_resp, path, headers, body, - expected_resp, trigger_exception, pool_key) - - def test_do_GET_show_exception(self): - method = 'show' - method_resp = "251f748d-2a0d-4143-bce8-2e616f7a6a4a" - path = "http://localhost/showPool" - pool_key = ["10.0.0.6", "9d2b45c4efaa478481c30340b49fd4d2", - ["00efc78c-f11c-414a-bfcd-a82e16dc07d1", - "fd6b13dc-7230-4cbe-9237-36b4614bc6b5"]] - body = jsonutils.dumps({"pool_key": pool_key}) - headers = {'Content-Type': 'application/json', 'Connection': 'close'} - headers['Content-Length'] = len(body) - trigger_exception = True - formated_key = (pool_key[0], pool_key[1], tuple(sorted(pool_key[2]))) - expected_resp = ('Error showing pool: {0}.' - .format(formated_key)).encode() - - self._do_GET_helper(method, method_resp, path, headers, body, - expected_resp, trigger_exception, pool_key) - - def test_do_GET_show_empty(self): - method = 'show' - method_resp = "Empty pool" - path = "http://localhost/showPool" - pool_key = ["10.0.0.6", "9d2b45c4efaa478481c30340b49fd4d2", - ["00efc78c-f11c-414a-bfcd-a82e16dc07d1", - "fd6b13dc-7230-4cbe-9237-36b4614bc6b5"]] - body = jsonutils.dumps({"pool_key": pool_key}) - headers = {'Content-Type': 'application/json', 'Connection': 'close'} - headers['Content-Length'] = len(body) - trigger_exception = False - formated_key = (pool_key[0], pool_key[1], tuple(sorted(pool_key[2]))) - expected_resp = ('Pool {0} ports are:\n{1}' - .format(formated_key, method_resp)).encode() - - self._do_GET_helper(method, method_resp, path, headers, body, - expected_resp, trigger_exception, pool_key) - - def test_do_GET_show_wrong_key(self): - method = 'show' - method_resp = "" - path = "http://localhost/showPool" - pool_key = ["10.0.0.6", "9d2b45c4efaa478481c30340b49fd4d2"] - body = jsonutils.dumps({"pool_key": pool_key}) - headers = {'Content-Type': 'application/json', 'Connection': 'close'} - headers['Content-Length'] = len(body) - trigger_exception = False - expected_resp = ('Invalid pool key. Proper format is:\n[trunk_ip, ' - 'project_id, [security_groups]]\n').encode() - - self._do_GET_helper(method, method_resp, path, headers, body, - expected_resp, trigger_exception, pool_key) - - def test_do_GET_wrong_action(self): - method = 'fake' - method_resp = "" - path = "http://localhost/fakeMethod" - body = jsonutils.dumps({}) - headers = {'Content-Type': 'application/json', 'Connection': 'close'} - headers['Content-Length'] = len(body) - trigger_exception = False - - expected_resp = ('Method not allowed.').encode() - - self._do_GET_helper(method, method_resp, path, headers, body, - expected_resp, trigger_exception) diff --git a/kuryr_kubernetes/tests/unit/controller/managers/test_prometheus_exporter.py b/kuryr_kubernetes/tests/unit/controller/managers/test_prometheus_exporter.py deleted file mode 100644 index b845078ff..000000000 --- a/kuryr_kubernetes/tests/unit/controller/managers/test_prometheus_exporter.py +++ /dev/null @@ -1,186 +0,0 @@ -# Copyright 2021 Red Hat -# -# 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 prometheus_client -from unittest import mock - -from openstack.load_balancer.v2 import load_balancer as os_lb -from openstack.load_balancer.v2 import pool as os_pool -from openstack.network.v2 import subnet as os_subnet - -from kuryr_kubernetes.controller.managers import prometheus_exporter -from kuryr_kubernetes.tests import base -from kuryr_kubernetes.tests.unit import kuryr_fixtures as k_fix - - -def get_quota_obj(): - return { - 'subnets': { - 'used': 50, - 'limit': 100, - 'reserved': 0 - }, - 'networks': { - 'used': 50, - 'limit': 100, - 'reserved': 0 - }, - 'security_group_rules': { - 'used': 50, - 'limit': 100, - 'reserved': 0 - }, - 'security_groups': { - 'used': 5, - 'limit': 10, - 'reserved': 0 - }, - 'ports': { - 'used': 250, - 'limit': 500, - 'reserved': 0 - } - } - - -class TestControllerPrometheusExporter(base.TestCase): - - def setUp(self): - super(TestControllerPrometheusExporter, self).setUp() - self.cls = prometheus_exporter.ControllerPrometheusExporter - self.srv = mock.MagicMock(spec=self.cls) - self.srv.quota_free_count = mock.MagicMock( - spec=prometheus_client.Gauge) - self.srv.port_quota_per_subnet = mock.MagicMock( - spec=prometheus_client.Gauge) - self.srv.lbs_members_count = mock.MagicMock( - spec=prometheus_client.Gauge) - self.srv.lbs_state = mock.MagicMock( - spec=prometheus_client.Enum) - self.srv._project_id = mock.sentinel.project_id - self.srv._os_net = self.useFixture(k_fix.MockNetworkClient()).client - self.srv._os_lb = self.useFixture(k_fix.MockLBaaSClient()).client - - def test__record_quota_free_count_metric(self): - quota = get_quota_obj() - self.srv._os_net.get_quota.return_value = quota - self.cls._record_quota_free_count_metric(self.srv) - calls = [] - for resource in prometheus_exporter.RESOURCES: - calls.extend( - [mock.call(**{'resource': resource}), - mock.call().set( - quota[resource]['limit']-quota[resource]['used'])]) - self.srv.quota_free_count.labels.assert_has_calls(calls) - - def test__record_no_quota_free_count_metric(self): - quota = get_quota_obj() - for resource in quota: - quota[resource]['used'] = quota[resource]['limit'] - self.srv._os_net.get_quota.return_value = quota - self.cls._record_quota_free_count_metric(self.srv) - calls = [] - for resource in prometheus_exporter.RESOURCES: - calls.extend( - [mock.call(**{'resource': resource}), - mock.call().set(0)]) - self.srv.quota_free_count.labels.assert_has_calls(calls) - - def test__record_ports_quota_per_subnet_metric(self): - subnet_id = mock.sentinel.id - subnet_name = 'ns/cluster-version-net' - network_id = mock.sentinel.network_id - subnets = [ - os_subnet.Subnet( - id=subnet_id, - name=subnet_name, - network_id=network_id, - allocation_pools=[ - {'start': '10.128.70.2', 'end': '10.128.71.254'}, - ], - ), - ] - ports = [mock.MagicMock()] - self.srv._os_net.subnets.return_value = subnets - self.srv._os_net.ports.return_value = ports - self.cls._record_ports_quota_per_subnet_metric(self.srv) - self.srv.port_quota_per_subnet.labels.assert_called_with( - **{'subnet_id': subnet_id, 'subnet_name': subnet_name}) - self.srv.port_quota_per_subnet.labels().set.assert_called_with(509) - - @mock.patch('kuryr_kubernetes.utils.get_kuryrloadbalancer') - def test__record_lbs_metrics(self, m_get_klb): - lb_name = 'default/kubernetes' - lb_id = mock.sentinel.id - pool_name = mock.sentinel.name - pool_id = mock.sentinel.id - lb_state = 'ACTIVE' - m_get_klb.return_value = { - "status": { - "loadbalancer": { - "id": lb_id, - } - } - } - self.srv._os_lb.find_load_balancer.return_value = os_lb.LoadBalancer( - id=lb_id, - name=lb_name, - provisioning_status=lb_state, - pools=[{'id': pool_id}], - ) - self.srv._os_lb.pools.return_value = [ - os_pool.Pool( - id=pool_id, - name=pool_name, - loadbalancers=[{'id': lb_id}], - members=[{'id': mock.sentinel.id}], - ), - ] - - self.cls._record_lbs_metrics(self.srv) - - self.srv.lbs_state.labels.assert_called_with( - **{'lb_name': lb_name}) - self.srv.lbs_state.labels().state.assert_called_with(lb_state) - self.srv.lbs_members_count.labels.assert_called_with( - **{'lb_name': lb_name, 'lb_pool_name': pool_name}) - self.srv.lbs_members_count.labels().set.assert_called_with(1) - - @mock.patch('kuryr_kubernetes.utils.get_kuryrloadbalancer') - def test__record_no_lb_present_metric(self, m_get_klb): - lb_name = 'default/kubernetes' - lb_id = mock.sentinel.id - m_get_klb.return_value = { - "status": { - "loadbalancer": { - "id": lb_id, - } - } - } - self.srv._os_lb.find_load_balancer.return_value = None - self.cls._record_lbs_metrics(self.srv) - self.srv.lbs_state.labels.assert_called_with( - **{'lb_name': lb_name}) - self.srv.lbs_state.labels().state.assert_called_with('DELETED') - - @mock.patch('kuryr_kubernetes.utils.get_kuryrloadbalancer') - def test__no_record_lbs_metrics(self, m_get_klb): - m_get_klb.return_value = {} - - self.cls._record_lbs_metrics(self.srv) - - self.srv.lbs_state.labels.assert_not_called() - self.srv.lbs_state.labels().state.assert_not_called() - self.srv.lbs_members_count.labels.assert_not_called() - self.srv.lbs_members_count.labels().set.assert_not_called() diff --git a/kuryr_kubernetes/tests/unit/controller/test_service.py b/kuryr_kubernetes/tests/unit/controller/test_service.py deleted file mode 100644 index 1f83856c2..000000000 --- a/kuryr_kubernetes/tests/unit/controller/test_service.py +++ /dev/null @@ -1,59 +0,0 @@ -# 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. - -from unittest import mock - -from kuryr_kubernetes.controller import service -from kuryr_kubernetes.tests import base as test_base -from kuryr_kubernetes.tests.unit.controller.handlers import test_fake_handler -from oslo_config import cfg - - -class TestControllerService(test_base.TestCase): - - @mock.patch('oslo_service.service.launch') - @mock.patch('kuryr_kubernetes.config.init') - @mock.patch('kuryr_kubernetes.config.setup_logging') - @mock.patch('kuryr_kubernetes.clients.setup_clients') - @mock.patch('kuryr_kubernetes.controller.service.KuryrK8sService') - def test_start(self, m_svc, m_setup_clients, m_setup_logging, - m_config_init, m_oslo_launch): - m_launcher = mock.Mock() - m_oslo_launch.return_value = m_launcher - - service.start() - - m_config_init.assert_called() - m_setup_logging.assert_called() - m_setup_clients.assert_called() - m_svc.assert_called() - m_oslo_launch.assert_called() - m_launcher.wait.assert_called() - - def test_check_test_handler(self): - cfg.CONF.set_override('enabled_handlers', ['test_handler'], - group='kubernetes') - handlers = service._load_kuryr_ctrlr_handlers() - for handler in handlers: - self.assertEqual(handler.get_watch_path(), - test_fake_handler.TestHandler.OBJECT_WATCH_PATH) - - @mock.patch('kuryr_kubernetes.controller.service._handler_not_found') - def test_handler_not_found(self, m_handler_not_found): - - cfg.CONF.set_override('enabled_handlers', ['fake_handler'], - group='kubernetes') - service._load_kuryr_ctrlr_handlers() - m_handler_not_found.assert_called() diff --git a/kuryr_kubernetes/tests/unit/handlers/__init__.py b/kuryr_kubernetes/tests/unit/handlers/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/kuryr_kubernetes/tests/unit/handlers/test_asynchronous.py b/kuryr_kubernetes/tests/unit/handlers/test_asynchronous.py deleted file mode 100644 index cd99cbaa9..000000000 --- a/kuryr_kubernetes/tests/unit/handlers/test_asynchronous.py +++ /dev/null @@ -1,161 +0,0 @@ -# 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 queue -from unittest import mock - -from kuryr_kubernetes.handlers import asynchronous as h_async -from kuryr_kubernetes.tests import base as test_base - - -class TestAsyncHandler(test_base.TestCase): - - def test_call(self): - event = mock.sentinel.event - group = mock.sentinel.group - m_queue = mock.Mock() - m_handler = mock.Mock() - m_group_by = mock.Mock(return_value=group) - m_info = mock.Mock(return_value=group) - async_handler = h_async.Async(m_handler, mock.Mock(), m_group_by, - m_info) - async_handler._queues[group] = m_queue - - async_handler(event) - - m_handler.assert_not_called() - self.assertEqual({group: m_queue}, async_handler._queues) - m_queue.put.assert_called_once_with((event, (), {})) - - @mock.patch('queue.Queue') - def test_call_new(self, m_queue_type): - event = mock.sentinel.event - group = mock.sentinel.group - queue_depth = mock.sentinel.queue_depth - m_queue = mock.Mock() - m_queue_type.return_value = m_queue - m_handler = mock.Mock() - m_th = mock.Mock() - m_tg = mock.Mock() - m_tg.add_thread.return_value = m_th - m_group_by = mock.Mock(return_value=group) - m_info = mock.Mock(return_value=group) - async_handler = h_async.Async(m_handler, m_tg, m_group_by, m_info, - queue_depth=queue_depth) - - async_handler(event) - - m_handler.assert_not_called() - m_queue_type.assert_called_once_with(queue_depth) - self.assertEqual({group: m_queue}, async_handler._queues) - m_tg.add_thread.assert_called_once_with(async_handler._run, group, - m_queue, group) - m_th.link.assert_called_once_with(async_handler._done, group, group) - m_queue.put.assert_called_once_with((event, (), {})) - - def test_call_injected(self): - event = mock.sentinel.event - group = mock.sentinel.group - m_queue = mock.Mock() - m_handler = mock.Mock() - m_group_by = mock.Mock(return_value=group) - m_info = mock.Mock(return_value=group) - async_handler = h_async.Async(m_handler, mock.Mock(), m_group_by, - m_info) - async_handler._queues[group] = m_queue - - async_handler(event, injected=True) - - m_handler.assert_not_called() - self.assertEqual({group: m_queue}, async_handler._queues) - m_queue.put.assert_not_called() - - @mock.patch('itertools.count') - def test_run(self, m_count): - event = mock.sentinel.event - group = mock.sentinel.group - m_queue = mock.Mock() - m_queue.empty.return_value = True - m_queue.get.return_value = (event, (), {}) - m_handler = mock.Mock() - m_count.return_value = [1] - async_handler = h_async.Async(m_handler, mock.Mock(), mock.Mock(), - mock.Mock(), queue_depth=1) - - with mock.patch('time.sleep'): - async_handler._run(group, m_queue, None) - - m_handler.assert_called_once_with(event) - - @mock.patch('itertools.count') - def test_run_empty(self, m_count): - events = [(x, (), {}) for x in (mock.sentinel.event1, - mock.sentinel.event2)] - group = mock.sentinel.group - m_queue = mock.Mock() - m_queue.empty.return_value = True - m_queue.get.side_effect = events + [queue.Empty()] - m_handler = mock.Mock() - m_count.return_value = list(range(5)) - async_handler = h_async.Async(m_handler, mock.Mock(), mock.Mock(), - mock.Mock()) - - with mock.patch('time.sleep'): - async_handler._run(group, m_queue, None) - - m_handler.assert_has_calls([mock.call(event[0]) for event in events]) - self.assertEqual(len(events), m_handler.call_count) - - @mock.patch('itertools.count') - def test_run_stale(self, m_count): - events = [(x, (), {}) for x in (mock.sentinel.event1, - mock.sentinel.event2)] - group = mock.sentinel.group - m_queue = mock.Mock() - m_queue.empty.side_effect = [False, True, True] - m_queue.get.side_effect = events + [queue.Empty()] - m_handler = mock.Mock() - m_count.return_value = list(range(5)) - async_handler = h_async.Async(m_handler, mock.Mock(), mock.Mock(), - mock.Mock()) - - with mock.patch('time.sleep'): - async_handler._run(group, m_queue, None) - - m_handler.assert_called_once_with(mock.sentinel.event2) - - def test_done(self): - group = mock.sentinel.group - m_queue = mock.Mock() - async_handler = h_async.Async(mock.Mock(), mock.Mock(), mock.Mock(), - mock.Mock()) - async_handler._queues[group] = m_queue - - async_handler._done(mock.Mock(), group, None) - - self.assertFalse(async_handler._queues) - - @mock.patch('kuryr_kubernetes.handlers.asynchronous.LOG.critical') - def test_done_terminated(self, m_critical): - group = mock.sentinel.group - m_queue = mock.Mock() - m_queue.empty.return_value = False - async_handler = h_async.Async(mock.Mock(), mock.Mock(), mock.Mock(), - mock.Mock()) - async_handler._queues[group] = m_queue - - async_handler._done(mock.Mock(), group, None) - - m_critical.assert_called_once() diff --git a/kuryr_kubernetes/tests/unit/handlers/test_dispatch.py b/kuryr_kubernetes/tests/unit/handlers/test_dispatch.py deleted file mode 100644 index 30168bb01..000000000 --- a/kuryr_kubernetes/tests/unit/handlers/test_dispatch.py +++ /dev/null @@ -1,123 +0,0 @@ -# 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. - -from unittest import mock - -from kuryr_kubernetes.handlers import dispatch as h_dis -from kuryr_kubernetes.tests import base as test_base - - -def make_event(name): - return {'object': {'metadata': {'name': str(name)}}} - - -class TestDispatch(test_base.TestCase): - def test_dispatch(self): - events = [make_event(i) for i in range(3)] - handler = mock.Mock() - dispatcher = h_dis.Dispatcher() - dispatcher.register(lambda e: True, True, handler) - - for event in events: - dispatcher(event) - - handler.assert_has_calls([mock.call(e) for e in events]) - - def test_dispatch_broadcast(self): - handlers = [mock.Mock() for _ in range(3)] - dispatcher = h_dis.Dispatcher() - event = make_event(mock.sentinel.event_name) - - for handler in handlers: - dispatcher.register(lambda e: True, True, handler) - - dispatcher(event) - - for handler in handlers: - handler.assert_called_once_with(event) - - def test_dispatch_by_key(self): - def key_fn(event): - return event['object']['metadata']['name'] - - events = {} - for i in range(3): - e = make_event(i) - events[key_fn(e)] = e - handlers = {key: mock.Mock() for key in events} - dispatcher = h_dis.Dispatcher() - for key, handler in handlers.items(): - dispatcher.register(key_fn, key, handler) - - for event in events.values(): - dispatcher(event) - - for key, handler in handlers.items(): - handler.assert_called_once_with(events[key]) - - -class _TestEventPipeline(h_dis.EventPipeline): - def _wrap_dispatcher(self, dispatcher): - pass - - def _wrap_consumer(self, consumer): - pass - - -class TestEventPipeline(test_base.TestCase): - @mock.patch.object(_TestEventPipeline, '_wrap_dispatcher') - @mock.patch('kuryr_kubernetes.handlers.dispatch.Dispatcher') - def test_init(self, m_dispatcher_type, m_wrapper): - m_dispatcher_type.return_value = mock.sentinel.dispatcher - m_wrapper.return_value = mock.sentinel.handler - - pipeline = _TestEventPipeline() - - m_dispatcher_type.assert_called_once() - m_wrapper.assert_called_once_with(mock.sentinel.dispatcher) - self.assertEqual(mock.sentinel.dispatcher, pipeline._dispatcher) - self.assertEqual(mock.sentinel.handler, pipeline._handler) - - @mock.patch.object(_TestEventPipeline, '_wrap_consumer') - @mock.patch.object(_TestEventPipeline, '__init__') - def test_register(self, m_init, m_wrap_consumer): - consumes = {mock.sentinel.key_fn1: mock.sentinel.key1, - mock.sentinel.key_fn2: mock.sentinel.key2, - mock.sentinel.key_fn3: mock.sentinel.key3} - m_dispatcher = mock.Mock() - m_consumer = mock.Mock() - m_consumer.consumes = consumes - m_wrap_consumer.return_value = mock.sentinel.handler - m_init.return_value = None - pipeline = _TestEventPipeline() - pipeline._dispatcher = m_dispatcher - - pipeline.register(m_consumer) - - m_wrap_consumer.assert_called_once_with(m_consumer) - m_dispatcher.register.assert_has_calls([ - mock.call(key_fn, key, mock.sentinel.handler) - for key_fn, key in consumes.items()], any_order=True) - - @mock.patch.object(_TestEventPipeline, '__init__') - def test_call(self, m_init): - m_init.return_value = None - m_handler = mock.Mock() - pipeline = _TestEventPipeline() - pipeline._handler = m_handler - - pipeline(mock.sentinel.event) - - m_handler.assert_called_once_with(mock.sentinel.event) diff --git a/kuryr_kubernetes/tests/unit/handlers/test_health.py b/kuryr_kubernetes/tests/unit/handlers/test_health.py deleted file mode 100644 index 335007de7..000000000 --- a/kuryr_kubernetes/tests/unit/handlers/test_health.py +++ /dev/null @@ -1,48 +0,0 @@ -# Copyright 2018 Maysa de Macedo Souza. -# -# 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 kuryr_kubernetes.handlers import health as h_health -from kuryr_kubernetes.tests import base as test_base -from unittest import mock - - -class _TestHandler(h_health.HealthHandler): - def is_alive(self): - pass - - -class TestHealthRegister(test_base.TestCase): - - def test_register(self): - m_component = mock.Mock() - health_register = h_health.HealthRegister() - health_register.register(m_component) - - self.assertEqual(health_register.registry, [m_component]) - - -class TestHealthHandler(test_base.TestCase): - - @mock.patch.object(h_health.HealthRegister, 'get_instance') - def test_init(self, m_health_register): - cls = h_health.HealthRegister - m_health_register_obj = mock.Mock(spec=cls) - m_health_register.return_value = m_health_register_obj - - health_handler = _TestHandler() - - self.assertTrue(health_handler._alive) - self.assertTrue(health_handler._ready) - m_health_register_obj.register.assert_called_once_with(health_handler) - self.assertEqual(m_health_register_obj, health_handler._manager) diff --git a/kuryr_kubernetes/tests/unit/handlers/test_k8s_base.py b/kuryr_kubernetes/tests/unit/handlers/test_k8s_base.py deleted file mode 100644 index 5e1e398c9..000000000 --- a/kuryr_kubernetes/tests/unit/handlers/test_k8s_base.py +++ /dev/null @@ -1,64 +0,0 @@ -# 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. - -from unittest import mock - -from kuryr_kubernetes.handlers import k8s_base as h_k8s -from kuryr_kubernetes.tests import base as test_base - - -class TestResourceEventHandler(test_base.TestCase): - - @mock.patch.object(h_k8s.ResourceEventHandler, 'on_added') - @mock.patch.object(h_k8s.ResourceEventHandler, 'on_present') - def test_added(self, m_added, m_present): - obj = mock.sentinel.obj - event = {'type': 'ADDED', 'object': obj} - handler = h_k8s.ResourceEventHandler() - - handler(event) - - m_added.assert_called_once_with(obj) - m_present.assert_called_once_with(obj) - - @mock.patch.object(h_k8s.ResourceEventHandler, 'on_modified') - @mock.patch.object(h_k8s.ResourceEventHandler, 'on_present') - def test_modified(self, m_modified, m_present): - obj = mock.sentinel.obj - event = {'type': 'MODIFIED', 'object': obj} - handler = h_k8s.ResourceEventHandler() - - handler(event) - - m_modified.assert_called_once_with(obj) - m_present.assert_called_once_with(obj) - - @mock.patch.object(h_k8s.ResourceEventHandler, 'on_deleted') - def test_deleted(self, m_deleted): - obj = mock.sentinel.obj - event = {'type': 'DELETED', 'object': obj} - handler = h_k8s.ResourceEventHandler() - - handler(event) - - m_deleted.assert_called_once_with(obj) - - def test_unknown(self): - event = {'type': 'UNKNOWN'} - handler = h_k8s.ResourceEventHandler() - - handler(event) - - self.assertTrue(True) diff --git a/kuryr_kubernetes/tests/unit/handlers/test_logging.py b/kuryr_kubernetes/tests/unit/handlers/test_logging.py deleted file mode 100644 index 1860935b8..000000000 --- a/kuryr_kubernetes/tests/unit/handlers/test_logging.py +++ /dev/null @@ -1,65 +0,0 @@ -# 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. - -from unittest import mock - -from kuryr_kubernetes.handlers import logging as h_log -from kuryr_kubernetes.tests import base as test_base - - -class TestLoggingHandler(test_base.TestCase): - - @mock.patch('kuryr_kubernetes.handlers.logging.LOG') - def test_no_exception(self, m_log): - m_handler = mock.Mock() - handler = h_log.LogExceptions(m_handler) - - handler(mock.sentinel.event) - - m_handler.assert_called_once_with(mock.sentinel.event) - m_log.exception.assert_not_called() - - @mock.patch('kuryr_kubernetes.handlers.logging.LOG') - def test_exception(self, m_log): - m_handler = mock.Mock() - m_handler.side_effect = ValueError() - handler = h_log.LogExceptions(m_handler, exceptions=ValueError) - - handler(mock.sentinel.event) - - m_handler.assert_called_once_with(mock.sentinel.event) - m_log.exception.assert_called_once() - - @mock.patch('kuryr_kubernetes.handlers.logging.LOG') - def test_exception_default(self, m_log): - m_handler = mock.Mock() - m_handler.side_effect = ValueError() - handler = h_log.LogExceptions(m_handler) - - handler(mock.sentinel.event) - - m_handler.assert_called_once_with(mock.sentinel.event) - m_log.exception.assert_called_once() - - @mock.patch('kuryr_kubernetes.handlers.logging.LOG') - def test_raises(self, m_log): - m_handler = mock.Mock() - m_handler.side_effect = KeyError() - handler = h_log.LogExceptions(m_handler, exceptions=ValueError) - - self.assertRaises(KeyError, handler, mock.sentinel.event) - - m_handler.assert_called_once_with(mock.sentinel.event) - m_log.exception.assert_not_called() diff --git a/kuryr_kubernetes/tests/unit/handlers/test_retry.py b/kuryr_kubernetes/tests/unit/handlers/test_retry.py deleted file mode 100644 index 2f3837e6d..000000000 --- a/kuryr_kubernetes/tests/unit/handlers/test_retry.py +++ /dev/null @@ -1,163 +0,0 @@ -# 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. - -from unittest import mock - -import fixtures -import time - -from kuryr_kubernetes import exceptions -from kuryr_kubernetes.handlers import retry as h_retry -from kuryr_kubernetes.tests import base as test_base -from kuryr_kubernetes.tests.unit import kuryr_fixtures as k_fix - - -class _EX1(Exception): - pass - - -class _EX11(_EX1): - pass - - -class _EX2(Exception): - pass - - -class TestRetryHandler(test_base.TestCase): - - def setUp(self): - super(TestRetryHandler, self).setUp() - - self.now = time.time() - f_time = self.useFixture(fixtures.MockPatch('time.time')) - f_time.mock.return_value = self.now - - self.k8s = self.useFixture(k_fix.MockK8sClient()).client - f_k8s = self.useFixture(fixtures.MockPatch( - 'kuryr_kubernetes.clients.get_kubernetes_client')) - f_k8s.mock.return_value = self.k8s - - @mock.patch('time.sleep') - def test_should_not_sleep(self, m_sleep): - deadline = self.now - 1 - retry = h_retry.Retry(mock.Mock()) - - ret = retry._sleep(deadline, 1, _EX1()) - - self.assertFalse(ret) - m_sleep.assert_not_called() - - def _test_should_sleep(self, seconds_left, slept): - attempt = 2 - timeout = 20 - interval = 3 - deadline = self.now + seconds_left - retry = h_retry.Retry(mock.Mock(), timeout=timeout, interval=interval) - - with mock.patch('random.randint') as m_randint, \ - mock.patch('time.sleep') as m_sleep: - m_randint.return_value = 0 # Assume 0 as jitter - - ret = retry._sleep(deadline, attempt, _EX2()) - - self.assertEqual(slept, ret) - m_sleep.assert_called_once_with(slept) - - def test_should_sleep(self): - self._test_should_sleep(20, 12) - - def test_should_sleep_last(self): - self._test_should_sleep(5, 5) - - def test_should_sleep_last_capped(self): - self._test_should_sleep(2, 3) - - @mock.patch('itertools.count') - @mock.patch.object(h_retry.Retry, '_sleep') - def test_call(self, m_sleep, m_count): - m_handler = mock.Mock() - m_count.return_value = list(range(1, 5)) - retry = h_retry.Retry(m_handler) - event = {'type': 'DELETED'} - - retry(event) - - m_handler.assert_called_once_with(event, retry_info=mock.ANY) - m_sleep.assert_not_called() - - @mock.patch('itertools.count') - @mock.patch.object(h_retry.Retry, '_sleep') - def test_call_outdated_event(self, m_sleep, m_count): - m_handler = mock.Mock() - m_count.return_value = list(range(1, 5)) - self_link = '/api/v1/namespaces/ns1/services/srv1' - obj = {'apiVersion': 'v1', - 'kind': 'Service', - 'metadata': {'name': 'srv1', - 'namespace': 'ns1'}} - event = {'type': 'MODIFIED', 'object': obj} - self.k8s.get.side_effect = exceptions.K8sResourceNotFound(obj) - - retry = h_retry.Retry(m_handler) - retry(event) - - self.k8s.get.assert_called_once_with(self_link) - m_handler.assert_not_called() - m_sleep.assert_not_called() - - @mock.patch('itertools.count') - @mock.patch.object(h_retry.Retry, '_sleep') - def test_call_retry(self, m_sleep, m_count): - attempts = 3 - timeout = 10 - deadline = self.now + timeout - failures = [_EX1()] * (attempts - 1) - event = {'type': 'DELETED'} - m_handler = mock.Mock() - m_handler.side_effect = failures + [None] - m_sleep.return_value = 1 - m_count.return_value = list(range(1, 5)) - retry = h_retry.Retry(m_handler, timeout=timeout, exceptions=_EX1) - - retry(event) - - m_handler.assert_has_calls([mock.call( - event, retry_info=mock.ANY)] * attempts) - m_sleep.assert_has_calls([ - mock.call(deadline, i + 1, failures[i]) - for i in range(len(failures))]) - - @mock.patch('itertools.count') - @mock.patch.object(h_retry.Retry, '_sleep') - def test_call_retry_raises(self, m_sleep, m_count): - attempts = 3 - timeout = 10 - deadline = self.now + timeout - failures = [_EX1(), _EX1(), _EX11()] - event = {'type': 'DELETED'} - m_handler = mock.Mock() - m_handler.side_effect = failures - m_sleep.side_effect = [1] * (attempts - 1) + [0] - m_count.return_value = list(range(1, 5)) - retry = h_retry.Retry(m_handler, timeout=timeout, exceptions=_EX1) - - self.assertRaises(_EX11, retry, event) - - m_handler.assert_has_calls([mock.call( - event, retry_info=mock.ANY)] * attempts) - m_sleep.assert_has_calls([ - mock.call(deadline, i + 1, failures[i]) - for i in range(len(failures))]) diff --git a/kuryr_kubernetes/tests/unit/kuryr_fixtures.py b/kuryr_kubernetes/tests/unit/kuryr_fixtures.py deleted file mode 100644 index afea61ca4..000000000 --- a/kuryr_kubernetes/tests/unit/kuryr_fixtures.py +++ /dev/null @@ -1,52 +0,0 @@ -# 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. - -from unittest import mock - -import fixtures - -from kuryr_kubernetes import k8s_client - - -class MockK8sClient(fixtures.Fixture): - def _setUp(self): - self.client = mock.Mock(k8s_client.K8sClient) - self.useFixture(fixtures.MockPatch( - 'kuryr_kubernetes.clients.get_kubernetes_client', - lambda: self.client)) - - -class MockLBaaSClient(fixtures.Fixture): - def _setUp(self): - self.client = mock.Mock() - self.useFixture(fixtures.MockPatch( - 'kuryr_kubernetes.clients.get_loadbalancer_client', - lambda: self.client)) - - -class MockNetworkClient(fixtures.Fixture): - def _setUp(self): - self.client = mock.Mock() - self.useFixture(fixtures.MockPatch( - 'kuryr_kubernetes.clients.get_network_client', - lambda: self.client)) - - -class MockComputeClient(fixtures.Fixture): - def _setUp(self): - self.client = mock.Mock() - self.useFixture(fixtures.MockPatch( - 'kuryr_kubernetes.clients.get_compute_client', - lambda: self.client)) diff --git a/kuryr_kubernetes/tests/unit/test_clients.py b/kuryr_kubernetes/tests/unit/test_clients.py deleted file mode 100644 index eb88f497d..000000000 --- a/kuryr_kubernetes/tests/unit/test_clients.py +++ /dev/null @@ -1,49 +0,0 @@ -# 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. - -from unittest import mock - -from kuryr_kubernetes import clients -from kuryr_kubernetes.tests import base as test_base - - -class TestK8sClient(test_base.TestCase): - - @mock.patch('openstack.connection.Connection') - @mock.patch('kuryr_kubernetes.config.CONF') - @mock.patch('kuryr_kubernetes.k8s_client.K8sClient') - def test_setup_clients(self, m_k8s, m_cfg, m_openstack): - k8s_api_root = 'http://127.0.0.1:1234' - - openstacksdk_mock = mock.Mock() - openstacksdk_mock.load_balancer = mock.Mock() - openstacksdk_mock.network = mock.Mock() - openstacksdk_mock.compute = mock.Mock() - k8s_dummy = object() - - m_cfg.kubernetes.api_root = k8s_api_root - m_k8s.return_value = k8s_dummy - m_openstack.return_value = openstacksdk_mock - - clients.setup_clients() - - m_k8s.assert_called_with(k8s_api_root) - self.assertIs(k8s_dummy, clients.get_kubernetes_client()) - self.assertIs(openstacksdk_mock.load_balancer, - clients.get_loadbalancer_client()) - self.assertIs(openstacksdk_mock.network, - clients.get_network_client()) - self.assertIs(openstacksdk_mock.compute, - clients.get_compute_client()) diff --git a/kuryr_kubernetes/tests/unit/test_k8s_client.py b/kuryr_kubernetes/tests/unit/test_k8s_client.py deleted file mode 100644 index 7e2039a02..000000000 --- a/kuryr_kubernetes/tests/unit/test_k8s_client.py +++ /dev/null @@ -1,525 +0,0 @@ -# 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 itertools -import os -import tempfile -from unittest import mock - -from oslo_serialization import jsonutils -import requests - -from kuryr_kubernetes import exceptions as exc -from kuryr_kubernetes import k8s_client -from kuryr_kubernetes.tests import base as test_base -from kuryr_kubernetes.tests import fake - - -class TestK8sClient(test_base.TestCase): - @mock.patch('kuryr_kubernetes.config.CONF') - def setUp(self, m_cfg): - super(TestK8sClient, self).setUp() - self.base_url = 'http://127.0.0.1:12345' - m_cfg.kubernetes.ssl_client_crt_file = None - m_cfg.kubernetes.ssl_client_key_file = None - m_cfg.kubernetes.ssl_ca_crt_file = None - m_cfg.kubernetes.token_file = None - m_cfg.kubernetes.ssl_verify_server_crt = False - self.client = k8s_client.K8sClient(self.base_url) - default_cert = (None, None) - default_token = None - self.assertEqual(default_cert, self.client.cert) - self.assertEqual(False, self.client.verify_server) - self.assertEqual(default_token, self.client.token) - - @mock.patch('os.path.exists') - @mock.patch('kuryr_kubernetes.config.CONF') - def test_https_client_init(self, m_cfg, m_exist): - m_cfg.kubernetes.ssl_client_crt_file = 'dummy_crt_file_path' - m_cfg.kubernetes.ssl_client_key_file = 'dummy_key_file_path' - m_cfg.kubernetes.ssl_ca_crt_file = 'dummy_ca_file_path' - m_cfg.kubernetes.token_file = None - m_cfg.kubernetes.ssl_verify_server_crt = True - m_exist.return_value = True - test_client = k8s_client.K8sClient(self.base_url) - cert = ('dummy_crt_file_path', 'dummy_key_file_path') - self.assertEqual(cert, test_client.cert) - self.assertEqual('dummy_ca_file_path', test_client.verify_server) - - @mock.patch('kuryr_kubernetes.config.CONF') - def test_https_client_init_invalid_client_crt_path(self, m_cfg): - m_cfg.kubernetes.ssl_client_crt_file = 'dummy_crt_file_path' - m_cfg.kubernetes.ssl_client_key_file = 'dummy_key_file_path' - m_cfg.kubernetes.token_file = None - self.assertRaises(RuntimeError, k8s_client.K8sClient, self.base_url) - - @mock.patch('os.path.exists') - @mock.patch('kuryr_kubernetes.config.CONF') - def test_https_client_init_invalid_ca_path(self, m_cfg, m_exist): - m_cfg.kubernetes.ssl_client_crt_file = 'dummy_crt_file_path' - m_cfg.kubernetes.ssl_client_key_file = 'dummy_key_file_path' - m_cfg.kubernetes.ssl_ca_crt_file = None - m_cfg.kubernetes.ssl_verify_server_crt = True - m_cfg.kubernetes.token_file = None - m_exist.return_value = True - self.assertRaises(RuntimeError, k8s_client.K8sClient, self.base_url) - - @mock.patch('requests.sessions.Session.send') - @mock.patch('kuryr_kubernetes.config.CONF') - def test_bearer_token(self, m_cfg, m_send): - token_content = ( - "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJrdWJlcm5ldGVzL3Nl" - "cnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc" - "3BhY2UiOiJrdWJlLXN5c3RlbSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bn" - "Qvc2VjcmV0Lm5hbWUiOiJkZWZhdWx0LXRva2VuLWh4M3QxIiwia3ViZXJuZXRlcy5" - "pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQubmFtZSI6ImRlZmF1bHQi" - "LCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC51a" - "WQiOiIxYTkyM2ZmNi00MDkyLTExZTctOTMwYi1mYTE2M2VkY2ViMDUiLCJzdWIiOi" - "JzeXN0ZW06c2VydmljZWFjY291bnQ6a3ViZS1zeXN0ZW06ZGVmYXVsdCJ9.lzcPef" - "DQ-uzF5cD-5pLwTKpRvtvvxKB4LX8TLymrPLMTth8WGr1vT6jteJPmLiDZM2C5dZI" - "iFJpOw4LL1XLullik-ls-CmnTWq97NvlW1cZolC0mNyRz6JcL7gkH8WfUSjLA7x80" - "ORalanUxtl9-ghMGKCtKIACAgvr5gGT4iznGYQQRx_hKURs4O6Js5vhwNM6UuOKeW" - "GDDAlhgHMG0u59z3bhiBLl6jbQktZsu8c3diXniQb3sYqYQcGKUm1IQFujyA_ByDb" - "5GUtCv1BOPL_-IjYtvdJD8ZzQ_UnPFoYQklpDyJLB7_7qCGcfVEQbnSCh907NdKo4" - "w_8Wkn2y-Tg") - token_file = tempfile.NamedTemporaryFile(mode="w+t", delete=False) - try: - m_cfg.kubernetes.token_file = token_file.name - token_file.write(token_content) - token_file.close() - m_cfg.kubernetes.ssl_verify_server_crt = False - - path = '/test' - client = k8s_client.K8sClient(self.base_url) - client.get(path) - - self.assertEqual(f'Bearer {token_content}', - m_send.call_args[0][0].headers['Authorization']) - finally: - os.unlink(m_cfg.kubernetes.token_file) - - @mock.patch('requests.sessions.Session.get') - def test_get(self, m_get): - path = '/test' - ret = {'kind': 'Pod', 'apiVersion': 'v1'} - - m_resp = mock.MagicMock() - m_resp.ok = True - m_resp.json.return_value = ret - m_get.return_value = m_resp - - self.assertEqual(ret, self.client.get(path)) - m_get.assert_called_once_with(self.base_url + path, headers=None) - - @mock.patch('requests.sessions.Session.get') - def test_get_list(self, m_get): - path = '/test' - ret = {'kind': 'PodList', - 'apiVersion': 'v1', - 'items': [{'metadata': {'name': 'pod1'}, - 'spec': {}, - 'status': {}}]} - res = {'kind': 'PodList', - 'apiVersion': 'v1', - 'items': [{'metadata': {'name': 'pod1'}, - 'spec': {}, - 'status': {}, - 'kind': 'Pod', - 'apiVersion': 'v1'}]} - - m_resp = mock.MagicMock() - m_resp.ok = True - m_resp.json.return_value = ret - m_get.return_value = m_resp - - self.assertDictEqual(res, self.client.get(path)) - m_get.assert_called_once_with(self.base_url + path, headers=None) - - @mock.patch('requests.sessions.Session.get') - def test_get_exception(self, m_get): - path = '/test' - - m_resp = mock.MagicMock() - m_resp.ok = False - m_get.return_value = m_resp - - self.assertRaises(exc.K8sClientException, self.client.get, path) - - @mock.patch('requests.sessions.Session.get') - def test_get_null_on_items_list(self, m_get): - path = '/test' - - req = {'kind': 'PodList', - 'apiVersion': 'v1', - 'metadata': {}, - 'items': None} - - ret = {'kind': 'PodList', - 'apiVersion': 'v1', - 'metadata': {}, - 'items': []} - - m_resp = mock.MagicMock() - m_resp.ok = True - m_resp.json.return_value = req - m_get.return_value = m_resp - - self.assertEqual(self.client.get(path), ret) - - @mock.patch('itertools.count') - @mock.patch('requests.sessions.Session.patch') - def test_annotate(self, m_patch, m_count): - m_count.return_value = list(range(1, 5)) - path = '/test' - annotations = {'a1': 'v1', 'a2': 'v2'} - resource_version = "123" - ret = {'metadata': {'annotations': annotations, - "resourceVersion": resource_version}} - data = jsonutils.dumps(ret, sort_keys=True) - - m_resp = mock.MagicMock() - m_resp.ok = True - m_resp.json.return_value = ret - m_patch.return_value = m_resp - - self.assertEqual(annotations, self.client.annotate( - path, annotations, resource_version=resource_version)) - m_patch.assert_called_once_with(self.base_url + path, - data=data, headers=mock.ANY) - - @mock.patch('itertools.count') - @mock.patch('requests.sessions.Session.patch') - def test_annotate_exception(self, m_patch, m_count): - m_count.return_value = list(range(1, 5)) - path = '/test' - - m_resp = mock.MagicMock() - m_resp.ok = False - m_patch.return_value = m_resp - - self.assertRaises(exc.K8sClientException, self.client.annotate, - path, {}) - - @mock.patch('itertools.count') - @mock.patch('requests.sessions.Session.patch') - def test_annotate_diff_resource_vers_no_conflict(self, m_patch, m_count): - m_count.return_value = list(range(1, 5)) - path = '/test' - annotations = {'a1': 'v1', 'a2': 'v2'} - resource_version = "123" - new_resource_version = "456" - conflicting_obj = {'metadata': { - 'annotations': annotations, - 'resourceVersion': resource_version}} - good_obj = {'metadata': { - 'annotations': annotations, - 'resourceVersion': new_resource_version}} - conflicting_data = jsonutils.dumps(conflicting_obj, sort_keys=True) - - m_resp_conflict = mock.MagicMock() - m_resp_conflict.ok = False - m_resp_conflict.status_code = requests.codes.conflict - m_resp_good = mock.MagicMock() - m_resp_good.ok = True - m_resp_good.json.return_value = conflicting_obj - m_patch.side_effect = [m_resp_conflict, m_resp_good] - - with mock.patch.object(self.client, 'get') as m_get: - m_get.return_value = good_obj - self.assertEqual(annotations, self.client.annotate( - path, annotations, resource_version=resource_version)) - - m_patch.assert_has_calls([ - mock.call(self.base_url + path, - data=conflicting_data, - headers=mock.ANY)]) - - @mock.patch('itertools.count') - @mock.patch('requests.sessions.Session.patch') - def test_annotate_diff_resource_vers_no_annotation(self, m_patch, m_count): - m_count.return_value = list(range(1, 5)) - path = '/test' - annotations = {'a1': 'v1', 'a2': 'v2'} - annotating_resource_version = '123' - annotating_obj = {'metadata': { - 'annotations': annotations, - 'resourceVersion': annotating_resource_version}} - annotating_data = jsonutils.dumps(annotating_obj, sort_keys=True) - - new_resource_version = '456' - new_obj = {'metadata': { - 'resourceVersion': new_resource_version}} - - resolution_obj = annotating_obj.copy() - resolution_obj['metadata']['resourceVersion'] = new_resource_version - resolution_data = jsonutils.dumps(resolution_obj, sort_keys=True) - - m_resp_conflict = mock.MagicMock() - m_resp_conflict.ok = False - m_resp_conflict.status_code = requests.codes.conflict - m_resp_good = mock.MagicMock() - m_resp_good.ok = True - m_resp_good.json.return_value = resolution_obj - m_patch.side_effect = (m_resp_conflict, m_resp_good) - - with mock.patch.object(self.client, 'get') as m_get: - m_get.return_value = new_obj - self.assertEqual(annotations, self.client.annotate( - path, annotations, - resource_version=annotating_resource_version)) - - m_patch.assert_has_calls([ - mock.call(self.base_url + path, - data=annotating_data, - headers=mock.ANY), - mock.call(self.base_url + path, - data=resolution_data, - headers=mock.ANY)]) - - @mock.patch('itertools.count') - @mock.patch('requests.sessions.Session.patch') - def test_annotate_diff_resource_vers_conflict(self, m_patch, m_count): - m_count.return_value = list(range(1, 5)) - path = '/test' - annotations = {'a1': 'v1', 'a2': 'v2'} - resource_version = "123" - new_resource_version = "456" - conflicting_obj = {'metadata': { - 'annotations': annotations, - 'resourceVersion': resource_version}} - actual_obj = {'metadata': { - 'annotations': {'a1': 'v2'}, - 'resourceVersion': new_resource_version}} - good_obj = {'metadata': { - 'annotations': annotations, - 'resourceVersion': new_resource_version}} - conflicting_data = jsonutils.dumps(conflicting_obj, sort_keys=True) - good_data = jsonutils.dumps(good_obj, sort_keys=True) - - m_resp_conflict = mock.MagicMock() - m_resp_conflict.ok = False - m_resp_conflict.status_code = requests.codes.conflict - m_patch.return_value = m_resp_conflict - m_resp_good = mock.MagicMock() - m_resp_good.ok = True - m_resp_good.json.return_value = conflicting_obj - m_patch.side_effect = [m_resp_conflict, m_resp_good] - - with mock.patch.object(self.client, 'get') as m_get: - m_get.return_value = actual_obj - self.assertEqual(annotations, self.client.annotate( - path, annotations, - resource_version=resource_version)) - m_patch.assert_has_calls([ - mock.call(self.base_url + path, - data=conflicting_data, - headers=mock.ANY), - mock.call(self.base_url + path, - data=good_data, - headers=mock.ANY)]) - - @mock.patch('itertools.count') - @mock.patch('requests.sessions.Session.patch') - def test_annotate_resource_not_found(self, m_patch, m_count): - m_count.return_value = list(range(1, 5)) - path = '/test' - annotations = {'a1': 'v1', 'a2': 'v2'} - resource_version = "123" - annotate_obj = {'metadata': { - 'annotations': annotations, - 'resourceVersion': resource_version}} - annotate_data = jsonutils.dumps(annotate_obj, sort_keys=True) - - m_resp_not_found = mock.MagicMock() - m_resp_not_found.ok = False - m_resp_not_found.status_code = requests.codes.not_found - m_patch.return_value = m_resp_not_found - - self.assertRaises(exc.K8sResourceNotFound, - self.client.annotate, - path, - annotations, - resource_version=resource_version) - m_patch.assert_called_once_with(self.base_url + path, - data=annotate_data, - headers=mock.ANY) - - @mock.patch('requests.sessions.Session.get') - def test_watch(self, m_get): - path = '/test' - data = [{'obj': 'obj%s' % i} for i in range(3)] - lines = [jsonutils.dump_as_bytes(i) for i in data] - - m_resp = mock.MagicMock() - m_resp.ok = True - m_resp.iter_lines.return_value = lines - m_get.return_value = m_resp - - cycles = 3 - self.assertEqual( - data * cycles, - list(itertools.islice(self.client.watch(path), - len(data) * cycles))) - - self.assertEqual(cycles, m_get.call_count) - self.assertEqual(cycles, m_resp.close.call_count) - m_get.assert_called_with(self.base_url + path, stream=True, - params={'watch': 'true'}) - - @mock.patch('requests.sessions.Session.get') - def test_watch_restart(self, m_get): - path = '/test' - data = [{'object': {'metadata': {'name': 'obj%s' % i, - 'resourceVersion': i}}} - for i in range(3)] - lines = [jsonutils.dump_as_bytes(i) for i in data] - - m_resp = mock.MagicMock() - m_resp.ok = True - m_resp.iter_lines.side_effect = [lines, requests.ReadTimeout, lines] - m_get.return_value = m_resp - - self.assertEqual(data * 2, - list(itertools.islice(self.client.watch(path), - len(data) * 2))) - self.assertEqual(3, m_get.call_count) - self.assertEqual(3, m_resp.close.call_count) - m_get.assert_any_call( - self.base_url + path, stream=True, params={"watch": "true"}) - m_get.assert_any_call( - self.base_url + path, stream=True, params={"watch": "true", - "resourceVersion": 2}) - - @mock.patch('requests.sessions.Session.get') - def test_watch_exception(self, m_get): - path = '/test' - - m_resp = mock.MagicMock() - m_resp.ok = False - m_get.return_value = m_resp - - self.assertRaises(exc.K8sClientException, next, - self.client.watch(path)) - - @mock.patch('requests.sessions.Session.post') - def test_post(self, m_post): - path = '/test' - body = {'test': 'body'} - ret = {'test': 'value'} - - m_resp = mock.MagicMock() - m_resp.ok = True - m_resp.json.return_value = ret - m_post.return_value = m_resp - - self.assertEqual(ret, self.client.post(path, body)) - m_post.assert_called_once_with(self.base_url + path, json=body, - headers=mock.ANY) - - @mock.patch('requests.sessions.Session.post') - def test_post_exception(self, m_post): - path = '/test' - body = {'test': 'body'} - - m_resp = mock.MagicMock() - m_resp.ok = False - m_post.return_value = m_resp - - self.assertRaises(exc.K8sClientException, - self.client.post, path, body) - - @mock.patch('requests.sessions.Session.delete') - def test_delete(self, m_delete): - path = '/test' - ret = {'test': 'value'} - - m_resp = mock.MagicMock() - m_resp.ok = True - m_resp.json.return_value = ret - m_delete.return_value = m_resp - - self.assertEqual(ret, self.client.delete(path)) - m_delete.assert_called_once_with(self.base_url + path, - headers=mock.ANY) - - @mock.patch('requests.sessions.Session.delete') - def test_delete_exception(self, m_delete): - path = '/test' - - m_resp = mock.MagicMock() - m_resp.ok = False - m_delete.return_value = m_resp - - self.assertRaises(exc.K8sClientException, - self.client.delete, path) - - def test__raise_from_response(self): - m_resp = mock.MagicMock() - m_resp.ok = True - m_resp.status_code = 202 - self.client._raise_from_response(m_resp) - - def test__raise_from_response_404(self): - m_resp = mock.MagicMock() - m_resp.ok = False - m_resp.status_code = 404 - self.assertRaises(exc.K8sResourceNotFound, - self.client._raise_from_response, m_resp) - - def test__raise_from_response_500(self): - m_resp = mock.MagicMock() - m_resp.ok = False - m_resp.status_code = 500 - self.assertRaises(exc.K8sClientException, - self.client._raise_from_response, m_resp) - - def test_add_event(self): - self.client.post = mock.MagicMock() - get_hex_ts = self.client._get_hex_timestamp = mock.MagicMock() - get_hex_ts.return_value = 'deadc0de' - - namespace = 'n1' - uid = 'deadbeef' - name = 'pod-123' - pod = fake.get_k8s_pod(name=name, namespace=namespace, uid=uid) - event_name = f'{name}.deadc0de' - - self.client.add_event(pod, 'reason', 'message') - - # Event path - url = self.client.post.call_args[0][0] - data = self.client.post.call_args[0][1] - self.assertEqual(url, f'/api/v1/namespaces/{namespace}/events') - - # Event fields - self.assertEqual(data['metadata']['name'], event_name) - self.assertEqual(data['reason'], 'reason') - self.assertEqual(data['message'], 'message') - self.assertEqual(data['type'], 'Normal') - - # involvedObject - self.assertDictEqual(data['involvedObject'], - {'apiVersion': pod['apiVersion'], - 'kind': pod['kind'], - 'name': name, - 'namespace': namespace, - 'uid': uid}) - - def test_add_event_k8s_exception(self): - self.client.post = mock.MagicMock() - self.client.post.side_effect = exc.K8sClientException - pod = fake.get_k8s_pod() - - self.assertDictEqual(self.client.add_event(pod, 'reason1', 'message2'), - {}) diff --git a/kuryr_kubernetes/tests/unit/test_linux_net_utils.py b/kuryr_kubernetes/tests/unit/test_linux_net_utils.py deleted file mode 100644 index 6fcca7e3d..000000000 --- a/kuryr_kubernetes/tests/unit/test_linux_net_utils.py +++ /dev/null @@ -1,62 +0,0 @@ -# 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 unittest import mock - -from oslo_concurrency import processutils as utils - -from kuryr_kubernetes import linux_net_utils as linux_net -from kuryr_kubernetes.tests import base as test_base - - -class LinuxNetworkUtilsTestCase(test_base.TestCase): - - def test_ovs_vif_port_cmd(self): - expected = ['--', '--if-exists', - 'del-port', 'fake-dev', '--', 'add-port', - 'fake-bridge', 'fake-dev', - '--', 'set', 'Interface', 'fake-dev', - 'external-ids:iface-id=fake-iface-id', - 'external-ids:iface-status=active', - 'external-ids:attached-mac=fake-mac', - 'external-ids:vm-uuid=fake-instance-uuid'] - cmd = linux_net._create_ovs_vif_cmd('fake-bridge', 'fake-dev', - 'fake-iface-id', 'fake-mac', - 'fake-instance-uuid') - - self.assertEqual(expected, cmd) - - def test_create_ovs_vif_port(self): - calls = [ - mock.call('ovs-vsctl', '--', '--if-exists', - 'del-port', 'fake-dev', '--', 'add-port', - 'fake-bridge', 'fake-dev', - '--', 'set', 'Interface', 'fake-dev', - 'external-ids:iface-id=fake-iface-id', - 'external-ids:iface-status=active', - 'external-ids:attached-mac=fake-mac', - 'external-ids:vm-uuid=fake-instance-uuid', - run_as_root=True)] - with mock.patch.object(utils, 'execute', return_value=('', '')) as ex: - linux_net.create_ovs_vif_port('fake-bridge', 'fake-dev', - 'fake-iface-id', 'fake-mac', - 'fake-instance-uuid') - ex.assert_has_calls(calls) - - def test_delete_ovs_vif_port(self): - calls = [ - mock.call('ovs-vsctl', '--', '--if-exists', - 'del-port', 'fake-bridge', 'fake-dev', - run_as_root=True)] - with mock.patch.object(utils, 'execute', return_value=('', '')) as ex: - linux_net.delete_ovs_vif_port('fake-bridge', 'fake-dev') - ex.assert_has_calls(calls) diff --git a/kuryr_kubernetes/tests/unit/test_object.py b/kuryr_kubernetes/tests/unit/test_object.py deleted file mode 100644 index 7d779ab0a..000000000 --- a/kuryr_kubernetes/tests/unit/test_object.py +++ /dev/null @@ -1,73 +0,0 @@ -# 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 kuryr_kubernetes.objects import base as kuryr_base -from kuryr_kubernetes.tests import base as test_base -from oslo_versionedobjects import base -from oslo_versionedobjects import fixture - -# NOTE(danms): The hashes in this list should only be changed if -# they come with a corresponding version bump in the affected -# objects -object_data = { - 'LBaaSListener': '1.0-a9e2d5c73687f5edc66fdb2f48650e15', - 'LBaaSLoadBalancer': '1.4-835c38599fa4692ad26726342c36ccb4', - 'LBaaSMember': '1.0-a770c6884c27d6d8c21186b27d0e2ccb', - 'LBaaSPool': '1.1-6e77370d7632a902445444249eb77b01', - 'LBaaSPortSpec': '1.1-1b307f34630617086c7af70f2cb8b215', - 'LBaaSPubIp': '1.0-83992edec2c60fb4ab8998ea42a4ff74', - 'LBaaSServiceSpec': '1.0-d430ecd443f2b1999196bfe531e56f7e', - 'LBaaSState': '1.0-a0ff7dce2d3f6ce1ffab4ff95a344361', -} - - -def get_kuryr_objects(): - """Get Kuryr versioned objects - - This returns a dict of versioned objects which are - in the Kuryr project namespace only (excludes objects - from os-vif and other 3rd party modules) - - :return: a dict mapping class names to lists of versioned objects - """ - - all_classes = base.VersionedObjectRegistry.obj_classes() - kuryr_classes = {} - for name in all_classes: - objclasses = all_classes[name] - if (objclasses[0].OBJ_PROJECT_NAMESPACE == - kuryr_base.KuryrK8sObjectBase.OBJ_PROJECT_NAMESPACE): - kuryr_classes[name] = objclasses - return kuryr_classes - - -class TestObjectVersions(test_base.TestCase): - def test_versions(self): - """Test Versions - - Ensures that modified objects had their versions bumped - """ - - checker = fixture.ObjectVersionChecker( - get_kuryr_objects()) - expected, actual = checker.test_hashes(object_data) - self.assertEqual(expected, actual, - """Some objects have changed; please make sure the - versions have been bumped and backporting - compatibility code has been added to - obj_make_compatible if necessary, and then update - their hashes in the object_data map in this test - module. If we don't need to add backporting code then - it means we also don't need the version bump and we - just have to change the hash in this module.""") diff --git a/kuryr_kubernetes/tests/unit/test_os_vif_plug_noop.py b/kuryr_kubernetes/tests/unit/test_os_vif_plug_noop.py deleted file mode 100644 index 13bb42e86..000000000 --- a/kuryr_kubernetes/tests/unit/test_os_vif_plug_noop.py +++ /dev/null @@ -1,93 +0,0 @@ -# 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 stevedore import extension -from unittest import mock - -import os_vif -from os_vif import objects - -from kuryr_kubernetes.objects import vif as k_vif -from kuryr_kubernetes.os_vif_plug_noop import NoOpPlugin -from kuryr_kubernetes.tests import base - - -class TestNoOpPlugin(base.TestCase): - - def setUp(self): - super(TestNoOpPlugin, self).setUp() - os_vif._EXT_MANAGER = None - - @mock.patch('stevedore.extension.ExtensionManager') - def test_initialize(self, mock_EM): - self.assertIsNone(os_vif._EXT_MANAGER) - os_vif.initialize() - mock_EM.assert_called_once_with( - invoke_on_load=False, namespace='os_vif') - self.assertIsNotNone(os_vif._EXT_MANAGER) - - @mock.patch.object(NoOpPlugin, "plug") - def test_plug(self, mock_plug): - plg = extension.Extension(name="noop", - entry_point="os-vif", - plugin=NoOpPlugin, - obj=None) - with mock.patch('stevedore.extension.ExtensionManager.names', - return_value=['foobar']),\ - mock.patch('stevedore.extension.ExtensionManager.__getitem__', - return_value=plg): - os_vif.initialize() - info = mock.sentinel.info - vif = mock.MagicMock() - vif.plugin_name = 'noop' - os_vif.plug(vif, info) - mock_plug.assert_called_once_with(vif, info) - - @mock.patch.object(NoOpPlugin, "unplug") - def test_unplug(self, mock_unplug): - plg = extension.Extension(name="demo", - entry_point="os-vif", - plugin=NoOpPlugin, - obj=None) - with mock.patch('stevedore.extension.ExtensionManager.names', - return_value=['foobar']),\ - mock.patch('stevedore.extension.ExtensionManager.__getitem__', - return_value=plg): - os_vif.initialize() - info = mock.sentinel.info - vif = mock.MagicMock() - vif.plugin_name = 'noop' - os_vif.unplug(vif, info) - mock_unplug.assert_called_once_with(vif, info) - - def test_describe_noop_plugin(self): - os_vif.initialize() - noop_plugin = NoOpPlugin.load('noop') - result = noop_plugin.describe() - - expected = objects.host_info.HostPluginInfo( - plugin_name='noop', - vif_info=[ - objects.host_info.HostVIFInfo( - vif_object_name=k_vif.VIFVlanNested.__name__, - min_version="1.0", - max_version="1.0"), - objects.host_info.HostVIFInfo( - vif_object_name=k_vif.VIFMacvlanNested.__name__, - min_version="1.0", - max_version="1.0"), - objects.host_info.HostVIFInfo( - vif_object_name=k_vif.VIFDPDKNested.__name__, - min_version="1.0", - max_version="1.0"), - ]) - self.assertEqual(expected, result) diff --git a/kuryr_kubernetes/tests/unit/test_os_vif_util.py b/kuryr_kubernetes/tests/unit/test_os_vif_util.py deleted file mode 100644 index 150bed2cf..000000000 --- a/kuryr_kubernetes/tests/unit/test_os_vif_util.py +++ /dev/null @@ -1,658 +0,0 @@ -# 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. - -from unittest import mock -import uuid - -from openstack.network.v2 import network as os_network -from openstack.network.v2 import subnet as os_subnet -from os_vif.objects import fixed_ip as osv_fixed_ip -from os_vif.objects import network as osv_network -from os_vif.objects import route as osv_route -from os_vif.objects import subnet as osv_subnet -from oslo_config import cfg as o_cfg - -from kuryr_kubernetes import constants as const -from kuryr_kubernetes import exceptions as k_exc -from kuryr_kubernetes import os_vif_util as ovu -from kuryr_kubernetes.tests import base as test_base -from kuryr_kubernetes.tests import fake -from kuryr_kubernetes import utils - - -# REVISIT(ivc): move to kuryr-lib along with 'os_vif_util' - - -class TestOSVIFUtils(test_base.TestCase): - def test_neutron_to_osvif_network(self): - network_id = str(uuid.uuid4()) - network_name = 'test-net' - network_mtu = 1500 - neutron_network = os_network.Network( - id=network_id, - name=network_name, - mtu=network_mtu, - provider_network_type=None, - ) - - network = ovu.neutron_to_osvif_network(neutron_network) - - self.assertEqual(network_id, network.id) - self.assertEqual(network_name, network.label) - self.assertEqual(network_mtu, network.mtu) - - def test_neutron_to_osvif_network_no_name(self): - network_id = str(uuid.uuid4()) - network_mtu = 1500 - neutron_network = os_network.Network( - id=network_id, - name=None, - mtu=network_mtu, - provider_network_type=None, - ) - - network = ovu.neutron_to_osvif_network(neutron_network) - - self.assertFalse(network.obj_attr_is_set('label')) - - def test_neutron_to_osvif_network_no_mtu(self): - network_id = str(uuid.uuid4()) - network_name = 'test-net' - neutron_network = os_network.Network( - id=network_id, - name=network_name, - mtu=None, - provider_network_type=None, - ) - - network = ovu.neutron_to_osvif_network(neutron_network) - - self.assertIsNone(network.mtu) - - @mock.patch('kuryr_kubernetes.os_vif_util._neutron_to_osvif_routes') - def test_neutron_to_osvif_subnet(self, m_conv_routes): - gateway = '1.1.1.1' - cidr = '1.1.1.1/8' - dns = ['2.2.2.2', '3.3.3.3'] - host_routes = [mock.sentinel.host_route] - route_list = osv_route.RouteList(objects=[ - osv_route.Route(cidr='4.4.4.4/8', gateway='5.5.5.5')]) - m_conv_routes.return_value = route_list - neutron_subnet = os_subnet.Subnet( - cidr=cidr, - dns_nameservers=dns, - host_routes=host_routes, - gateway_ip=gateway, - ) - - subnet = ovu.neutron_to_osvif_subnet(neutron_subnet) - - self.assertEqual(cidr, str(subnet.cidr)) - self.assertEqual(route_list, subnet.routes) - self.assertEqual(set(dns), set([str(addr) for addr in subnet.dns])) - self.assertEqual(gateway, str(subnet.gateway)) - m_conv_routes.assert_called_once_with(host_routes) - - @mock.patch('kuryr_kubernetes.os_vif_util._neutron_to_osvif_routes') - def test_neutron_to_osvif_subnet_no_gateway(self, m_conv_routes): - cidr = '1.1.1.1/8' - route_list = osv_route.RouteList() - m_conv_routes.return_value = route_list - neutron_subnet = os_subnet.Subnet( - cidr=cidr, - dns_nameservers=[], - host_routes=[], - gateway_ip=None, - ) - - subnet = ovu.neutron_to_osvif_subnet(neutron_subnet) - - self.assertFalse(subnet.obj_attr_is_set('gateway')) - - def test_neutron_to_osvif_routes(self): - routes_map = {'%s.0.0.0/8' % i: '10.0.0.%s' % i for i in range(3)} - routes = [{'destination': k, 'nexthop': v} - for k, v in routes_map.items()] - - route_list = ovu._neutron_to_osvif_routes(routes) - - self.assertEqual(len(routes), len(route_list.objects)) - for route in route_list.objects: - self.assertEqual(routes_map[str(route.cidr)], str(route.gateway)) - - @mock.patch('kuryr_kubernetes.os_vif_util._VIF_MANAGERS') - def test_neutron_to_osvif_vif(self, m_mgrs): - vif_plugin = mock.sentinel.vif_plugin - port = mock.sentinel.port - subnets = mock.sentinel.subnets - m_mgr = mock.Mock() - m_mgrs.__getitem__.return_value = m_mgr - - ovu.neutron_to_osvif_vif(vif_plugin, port, subnets) - - m_mgrs.__getitem__.assert_called_with(vif_plugin) - m_mgr.driver.assert_called_with(vif_plugin, port, subnets) - - @mock.patch('stevedore.driver.DriverManager') - @mock.patch('kuryr_kubernetes.os_vif_util._VIF_MANAGERS') - def test_neutron_to_osvif_vif_load(self, m_mgrs, m_stv_drm): - vif_plugin = mock.sentinel.vif_plugin - port = mock.sentinel.port - subnets = mock.sentinel.subnets - m_mgr = mock.Mock() - m_mgrs.__getitem__.side_effect = KeyError - m_stv_drm.return_value = m_mgr - - ovu.neutron_to_osvif_vif(vif_plugin, port, subnets) - - m_stv_drm.assert_called_once_with( - namespace=ovu._VIF_TRANSLATOR_NAMESPACE, - name=vif_plugin, - invoke_on_load=False) - m_mgrs.__setitem__.assert_called_once_with(vif_plugin, m_mgr) - m_mgr.driver.assert_called_once_with(vif_plugin, port, subnets) - - @mock.patch('kuryr_kubernetes.os_vif_util._get_ovs_hybrid_bridge_name') - @mock.patch('kuryr_kubernetes.os_vif_util._get_vif_name') - @mock.patch('kuryr_kubernetes.os_vif_util._is_port_active') - @mock.patch('kuryr_kubernetes.os_vif_util._make_vif_network') - @mock.patch('os_vif.objects.vif.VIFBridge') - @mock.patch('os_vif.objects.vif.VIFPortProfileOpenVSwitch') - def test_neutron_to_osvif_vif_ovs_hybrid(self, - m_mk_profile, - m_mk_vif, - m_make_vif_network, - m_is_port_active, - m_get_vif_name, - m_get_ovs_hybrid_bridge_name): - vif_plugin = 'ovs' - port_id = mock.sentinel.port_id - ovs_bridge = mock.sentinel.ovs_bridge - port_filter = mock.sentinel.port_filter - subnets = mock.sentinel.subnets - port_profile = mock.sentinel.port_profile - network = mock.sentinel.network - port_active = mock.sentinel.port_active - vif_name = "vhu01234567-89" - hybrid_bridge = mock.sentinel.hybrid_bridge - vif = mock.sentinel.vif - port = fake.get_port_obj(port_id=port_id, - vif_details={'ovs_hybrid_plug': True, - 'bridge_name': ovs_bridge, - 'port_filter': port_filter}) - - m_mk_profile.return_value = port_profile - m_make_vif_network.return_value = network - m_is_port_active.return_value = port_active - m_get_vif_name.return_value = vif_name - m_get_ovs_hybrid_bridge_name.return_value = hybrid_bridge - m_mk_vif.return_value = vif - - self.assertEqual(vif, ovu.neutron_to_osvif_vif_ovs(vif_plugin, port, - subnets)) - - m_mk_profile.assert_called_once_with(interface_id=port_id) - m_make_vif_network.assert_called_once_with(port, subnets) - m_is_port_active.assert_called_once_with(port) - m_get_ovs_hybrid_bridge_name.assert_called_once_with(port) - m_get_vif_name.assert_called_once_with(port) - self.assertEqual(ovs_bridge, network.bridge) - m_mk_vif.assert_called_once_with( - id=port_id, - address=port.mac_address, - network=network, - has_traffic_filtering=port.binding_vif_details['port_filter'], - preserve_on_delete=False, - active=port_active, - port_profile=port_profile, - plugin=vif_plugin, - vif_name=vif_name, - bridge_name=hybrid_bridge) - - @mock.patch('kuryr_kubernetes.os_vif_util._get_vif_name') - @mock.patch('kuryr_kubernetes.os_vif_util._is_port_active') - @mock.patch('kuryr_kubernetes.os_vif_util._make_vif_network') - @mock.patch('os_vif.objects.vif.VIFOpenVSwitch') - @mock.patch('os_vif.objects.vif.VIFPortProfileOpenVSwitch') - def test_neutron_to_osvif_vif_ovs_native(self, - m_mk_profile, - m_mk_vif, - m_make_vif_network, - m_is_port_active, - m_get_vif_name): - vif_plugin = 'ovs' - vif_details = {'ovs_hybrid_plug': False, - 'bridge_name': mock.sentinel.ovs_bridge} - port = fake.get_port_obj(vif_details=vif_details) - port.active = mock.sentinel.port_active - port.profile = mock.sentinel.port_profile - - subnets = mock.sentinel.subnets - network = mock.sentinel.network - vif_name = "vhu01234567-89" - vif = mock.sentinel.vif - - m_mk_profile.return_value = port.profile - m_make_vif_network.return_value = network - m_is_port_active.return_value = port.active - m_get_vif_name.return_value = vif_name - m_mk_vif.return_value = vif - - self.assertEqual(vif, ovu.neutron_to_osvif_vif_ovs(vif_plugin, port, - subnets)) - m_mk_profile.assert_called_once_with(interface_id=port.id) - m_make_vif_network.assert_called_once_with(port, subnets) - m_is_port_active.assert_called_once_with(port) - m_get_vif_name.assert_called_once_with(port) - self.assertEqual(network.bridge, - port.binding_vif_details['bridge_name']) - - @mock.patch('kuryr_kubernetes.os_vif_util._get_vhu_vif_name') - @mock.patch('kuryr_kubernetes.os_vif_util._is_port_active') - @mock.patch('kuryr_kubernetes.os_vif_util._make_vif_network') - @mock.patch('os_vif.objects.vif.VIFVHostUser') - @mock.patch('os_vif.objects.vif.VIFPortProfileOpenVSwitch') - def test_neutron_to_osvif_vif_ovs_vu_client(self, m_mk_profile, m_mk_vif, - m_make_vif_network, - m_is_port_active, - m_get_vif_name): - vif_plugin = 'vhostuser' - o_cfg.CONF.set_override('mount_point', - '/var/lib/cni/vhostuser', - group='vhostuser') - port_id = mock.sentinel.port_id - mac_address = mock.sentinel.mac_address - ovs_bridge = mock.sentinel.ovs_bridge - subnets = mock.sentinel.subnets - port_profile = mock.sentinel.port_profile - network = mock.sentinel.network - port_active = mock.sentinel.port_active - vif_name = "vhu01234567-89" - vif = mock.sentinel.vif - - m_mk_profile.return_value = port_profile - m_make_vif_network.return_value = network - m_is_port_active.return_value = port_active - m_get_vif_name.return_value = vif_name - m_mk_vif.return_value = vif - - port = fake.get_port_obj(port_id=port_id, - vif_details={'ovs_hybrid_plug': False, - 'bridge_name': ovs_bridge, - 'vhostuser_mode': 'client'}) - port.mac_address = mac_address - - self.assertEqual(vif, ovu.neutron_to_osvif_vif_ovs(vif_plugin, port, - subnets)) - m_mk_profile.assert_called_once_with(interface_id=port_id) - m_make_vif_network.assert_called_once_with(port, subnets) - m_is_port_active.assert_called_once_with(port) - m_get_vif_name.assert_called_once_with(port_id) - self.assertEqual(ovs_bridge, network.bridge) - - @mock.patch('kuryr_kubernetes.os_vif_util._get_vhu_vif_name') - @mock.patch('kuryr_kubernetes.os_vif_util._is_port_active') - @mock.patch('kuryr_kubernetes.os_vif_util._make_vif_network') - @mock.patch('os_vif.objects.vif.VIFVHostUser') - @mock.patch('os_vif.objects.vif.VIFPortProfileOpenVSwitch') - def test_neutron_to_osvif_vif_ovs_vu_server(self, m_mk_profile, m_mk_vif, - m_make_vif_network, - m_is_port_active, - m_get_vif_name): - vif_plugin = 'vhostuser' - o_cfg.CONF.set_override('mount_point', - '/var/lib/cni/vhostuser', - group='vhostuser') - port_id = mock.sentinel.port_id - mac_address = mock.sentinel.mac_address - ovs_bridge = mock.sentinel.ovs_bridge - subnets = mock.sentinel.subnets - port_profile = mock.sentinel.port_profile - network = mock.sentinel.network - port_active = mock.sentinel.port_active - vif_name = "vhu01234567-89" - vif = mock.sentinel.vif - - m_mk_profile.return_value = port_profile - m_make_vif_network.return_value = network - m_is_port_active.return_value = port_active - m_get_vif_name.return_value = vif_name - m_mk_vif.return_value = vif - - port = fake.get_port_obj(port_id=port_id, - vif_details={'ovs_hybrid_plug': False, - 'bridge_name': ovs_bridge, - 'vhostuser_mode': 'server'}) - port.mac_address = mac_address - - self.assertEqual(vif, ovu.neutron_to_osvif_vif_ovs(vif_plugin, port, - subnets)) - m_mk_profile.assert_called_once_with(interface_id=port_id) - m_make_vif_network.assert_called_once_with(port, subnets) - m_is_port_active.assert_called_once_with(port) - m_get_vif_name.assert_called_once_with(port_id) - self.assertEqual(ovs_bridge, network.bridge) - - @mock.patch('kuryr_kubernetes.os_vif_util._get_vif_name') - @mock.patch('kuryr_kubernetes.os_vif_util._is_port_active') - @mock.patch('kuryr_kubernetes.os_vif_util._make_vif_network') - @mock.patch('kuryr_kubernetes.objects.vif.VIFVlanNested') - def test_neutron_to_osvif_nested_vlan(self, m_mk_vif, m_make_vif_network, - m_is_port_active, m_get_vif_name): - vif_plugin = const.K8S_OS_VIF_NOOP_PLUGIN - port_id = mock.sentinel.port_id - mac_address = mock.sentinel.mac_address - port_filter = mock.sentinel.port_filter - subnets = mock.sentinel.subnets - network = mock.sentinel.network - port_active = mock.sentinel.port_active - vif_name = mock.sentinel.vif_name - vif = mock.sentinel.vif - vlan_id = mock.sentinel.vlan_id - port = fake.get_port_obj(port_id=port_id, - vif_details={'port_filter': port_filter}) - port.mac_address = mac_address - - m_make_vif_network.return_value = network - m_is_port_active.return_value = port_active - m_get_vif_name.return_value = vif_name - m_mk_vif.return_value = vif - - self.assertEqual(vif, ovu.neutron_to_osvif_vif_nested_vlan(port, - subnets, vlan_id)) - - m_make_vif_network.assert_called_once_with(port, subnets) - m_is_port_active.assert_called_once_with(port) - m_get_vif_name.assert_called_once_with(port) - m_mk_vif.assert_called_once_with( - id=port_id, - address=mac_address, - network=network, - has_traffic_filtering=port_filter, - preserve_on_delete=False, - active=port_active, - plugin=vif_plugin, - vif_name=vif_name, - vlan_id=vlan_id) - - @mock.patch('kuryr_kubernetes.os_vif_util._get_vif_name') - @mock.patch('kuryr_kubernetes.os_vif_util._is_port_active') - @mock.patch('kuryr_kubernetes.os_vif_util._make_vif_network') - @mock.patch('kuryr_kubernetes.objects.vif.VIFMacvlanNested') - def test_neutron_to_osvif_nested_macvlan(self, m_mk_vif, - m_make_vif_network, - m_is_port_active, m_get_vif_name): - vif_plugin = const.K8S_OS_VIF_NOOP_PLUGIN - port_id = mock.sentinel.port_id - mac_address = mock.sentinel.mac_address - port_filter = mock.sentinel.port_filter - subnets = mock.sentinel.subnets - network = mock.sentinel.network - port_active = mock.sentinel.port_active - vif_name = mock.sentinel.vif_name - vif = mock.sentinel.vif - - m_make_vif_network.return_value = network - m_is_port_active.return_value = port_active - m_get_vif_name.return_value = vif_name - m_mk_vif.return_value = vif - - port = {'id': port_id, - 'mac_address': mac_address, - 'binding:vif_details': { - 'port_filter': port_filter}, - } - - self.assertEqual(vif, ovu.neutron_to_osvif_vif_nested_macvlan(port, - subnets)) - - m_make_vif_network.assert_called_once_with(port, subnets) - m_is_port_active.assert_called_once_with(port) - m_get_vif_name.assert_called_once_with(port) - m_mk_vif.assert_called_once_with( - id=port_id, - address=mac_address, - network=network, - has_traffic_filtering=port_filter, - preserve_on_delete=False, - active=port_active, - plugin=vif_plugin, - vif_name=vif_name) - - @mock.patch('kuryr_kubernetes.os_vif_util._get_vif_name') - @mock.patch('kuryr_kubernetes.os_vif_util._is_port_active') - @mock.patch('kuryr_kubernetes.os_vif_util._make_vif_network') - @mock.patch('kuryr_kubernetes.objects.vif.VIFDPDKNested') - @mock.patch('os_vif.objects.vif.VIFPortProfileK8sDPDK') - def test_neutron_to_osvif_nested_dpdk(self, m_mk_port_profile, m_mk_vif, - m_make_vif_network, - m_is_port_active, m_get_vif_name): - vif_plugin = const.K8S_OS_VIF_NOOP_PLUGIN - port_id = mock.sentinel.port_id - mac_address = mock.sentinel.mac_address - port_filter = mock.sentinel.port_filter - subnets = mock.sentinel.subnets - network = mock.sentinel.network - port_active = mock.sentinel.port_active - vif_name = mock.sentinel.vif_name - vif = mock.sentinel.vif - port_profile = mock.sentinel.port_profile - - m_make_vif_network.return_value = network - m_is_port_active.return_value = port_active - m_get_vif_name.return_value = vif_name - m_mk_vif.return_value = vif - m_mk_port_profile.return_value = port_profile - - pod = fake.get_k8s_pod() - - port = {'id': port_id, - 'mac_address': mac_address, - 'binding:vif_details': { - 'port_filter': port_filter}, - } - - self.assertEqual(vif, ovu.neutron_to_osvif_vif_dpdk(port, - subnets, pod)) - - m_make_vif_network.assert_called_once_with(port, subnets) - m_is_port_active.assert_called_once_with(port) - m_get_vif_name.assert_called_once_with(port) - m_mk_port_profile.assert_called_once_with( - l3_setup=False, - selflink=utils.get_res_link(pod)) - - m_mk_vif.assert_called_once_with( - id=port_id, - port_profile=port_profile, - address=mac_address, - network=network, - has_traffic_filtering=port_filter, - preserve_on_delete=False, - active=port_active, - plugin=vif_plugin, - pci_address="", - dev_driver="", - vif_name=vif_name) - - def test_neutron_to_osvif_vif_ovs_no_bridge(self): - vif_plugin = 'ovs' - port = fake.get_port_obj(port_id=str(uuid.uuid4())) - subnets = {} - - self.assertRaises(o_cfg.RequiredOptError, - ovu.neutron_to_osvif_vif_ovs, - vif_plugin, port, subnets) - - def test_get_ovs_hybrid_bridge_name(self): - port = fake.get_port_obj(port_id=str(uuid.uuid4())) - - self.assertEqual("qbr" + port.id[:11], - ovu._get_ovs_hybrid_bridge_name(port)) - - def test_is_port_active(self): - port = fake.get_port_obj(port_id=str(uuid.uuid4())) - port.status = 'ACTIVE' - - self.assertTrue(ovu._is_port_active(port)) - - def test_is_port_inactive(self): - port = fake.get_port_obj(port_id=str(uuid.uuid4())) - - self.assertFalse(ovu._is_port_active(port)) - - @mock.patch('kuryr.lib.binding.drivers.utils.get_veth_pair_names') - def test_get_vif_name(self, m_get_veth_pair_names): - vif_name = mock.sentinel.vif_name - port = fake.get_port_obj(port_id=str(uuid.uuid4())) - m_get_veth_pair_names.return_value = (vif_name, mock.sentinel.any) - - self.assertEqual(vif_name, ovu._get_vif_name(port)) - m_get_veth_pair_names.assert_called_once_with(port.id) - - @mock.patch('kuryr_kubernetes.os_vif_util._make_vif_subnets') - @mock.patch('os_vif.objects.subnet.SubnetList') - def test_make_vif_network(self, m_mk_subnet_list, m_make_vif_subnets): - network_id = mock.sentinel.network_id - network = mock.Mock() - orig_network = mock.Mock() - orig_network.id = network_id - orig_network.obj_clone.return_value = network - subnet_id = mock.sentinel.subnet_id - subnets = {subnet_id: orig_network} - vif_subnets = mock.sentinel.vif_subnets - subnet_list = mock.sentinel.subnet_list - m_make_vif_subnets.return_value = vif_subnets - m_mk_subnet_list.return_value = subnet_list - port = {'network_id': network_id} - - self.assertEqual(network, ovu._make_vif_network(port, subnets)) - self.assertEqual(subnet_list, network.subnets) - m_make_vif_subnets.assert_called_once_with(port, subnets) - m_mk_subnet_list.assert_called_once_with(objects=vif_subnets) - - def test_make_vif_network_not_found(self): - network_id = mock.sentinel.network_id - port = {'network_id': network_id} - subnets = {} - - self.assertRaises(k_exc.IntegrityError, ovu._make_vif_network, - port, subnets) - - @mock.patch('kuryr_kubernetes.os_vif_util._make_vif_subnet') - @mock.patch('os_vif.objects.fixed_ip.FixedIP') - def test_make_vif_subnets(self, m_mk_fixed_ip, m_make_vif_subnet): - subnet_id = mock.sentinel.subnet_id - ip_address = mock.sentinel.ip_address - fixed_ip = mock.sentinel.fixed_ip - subnet = mock.Mock() - subnets = mock.MagicMock() - subnets.__contains__.return_value = True - m_mk_fixed_ip.return_value = fixed_ip - m_make_vif_subnet.return_value = subnet - port = {'fixed_ips': [ - {'subnet_id': subnet_id, 'ip_address': ip_address}]} - - self.assertEqual([subnet], ovu._make_vif_subnets(port, subnets)) - m_make_vif_subnet.assert_called_once_with(subnets, subnet_id) - m_mk_fixed_ip.assert_called_once_with(address=ip_address) - subnet.ips.objects.append.assert_called_once_with(fixed_ip) - - def test_make_vif_subnets_not_found(self): - subnet_id = mock.sentinel.subnet_id - ip_address = mock.sentinel.ip_address - subnets = mock.MagicMock() - subnets.__contains__.return_value = False - port = {'fixed_ips': [ - {'subnet_id': subnet_id, 'ip_address': ip_address}]} - - self.assertRaises(k_exc.IntegrityError, ovu._make_vif_subnets, - port, subnets) - - @mock.patch('os_vif.objects.fixed_ip.FixedIPList') - def test_make_vif_subnet(self, m_mk_fixed_ip_list): - subnet_id = mock.sentinel.subnet_id - fixed_ip_list = mock.sentinel.fixed_ip_list - subnet = mock.Mock() - orig_subnet = mock.Mock() - orig_subnet.obj_clone.return_value = subnet - orig_network = mock.Mock() - orig_network.subnets.objects = [orig_subnet] - m_mk_fixed_ip_list.return_value = fixed_ip_list - subnets = {subnet_id: orig_network} - - self.assertEqual(subnet, ovu._make_vif_subnet(subnets, subnet_id)) - self.assertEqual(fixed_ip_list, subnet.ips) - m_mk_fixed_ip_list.assert_called_once_with(objects=[]) - - def test_make_vif_subnet_invalid(self): - subnet_id = mock.sentinel.subnet_id - orig_network = mock.Mock() - orig_network.subnets.objects = [] - subnets = {subnet_id: orig_network} - - self.assertRaises(k_exc.IntegrityError, ovu._make_vif_subnet, - subnets, subnet_id) - - def test_osvif_to_neutron_fixed_ips(self): - ip11 = '1.1.1.1' - ip12 = '2.2.2.2' - ip3 = '3.3.3.3' - subnet_id_1 = str(uuid.uuid4()) - subnet_id_2 = str(uuid.uuid4()) - subnet_id_3 = str(uuid.uuid4()) - - subnet_1 = osv_subnet.Subnet(ips=osv_fixed_ip.FixedIPList( - objects=[osv_fixed_ip.FixedIP(address=ip11), - osv_fixed_ip.FixedIP(address=ip12)])) - subnet_2 = osv_subnet.Subnet() - subnet_3 = osv_subnet.Subnet(ips=osv_fixed_ip.FixedIPList( - objects=[osv_fixed_ip.FixedIP(address=ip3)])) - - net1 = osv_network.Network(subnets=osv_subnet.SubnetList( - objects=[subnet_1])) - net2 = osv_network.Network(subnets=osv_subnet.SubnetList( - objects=[subnet_2])) - net3 = osv_network.Network(subnets=osv_subnet.SubnetList( - objects=[subnet_3])) - - subnets = {subnet_id_1: net1, subnet_id_2: net2, subnet_id_3: net3} - - expected = [{'subnet_id': subnet_id_1, 'ip_address': ip11}, - {'subnet_id': subnet_id_1, 'ip_address': ip12}, - {'subnet_id': subnet_id_2}, - {'subnet_id': subnet_id_3, 'ip_address': ip3}] - - ret = ovu.osvif_to_neutron_fixed_ips(subnets) - - def _sort_key(e): - return (e.get('subnet_id'), e.get('ip_address')) - - self.assertEqual(sorted(expected, key=_sort_key), - sorted(ret, key=_sort_key)) - - def test_osvif_to_neutron_fixed_ips_invalid(self): - subnet_id = str(uuid.uuid4()) - - subnet_1 = osv_subnet.Subnet() - subnet_2 = osv_subnet.Subnet() - - net = osv_network.Network(subnets=osv_subnet.SubnetList( - objects=[subnet_1, subnet_2])) - - subnets = {subnet_id: net} - - self.assertRaises(k_exc.IntegrityError, - ovu.osvif_to_neutron_fixed_ips, subnets) diff --git a/kuryr_kubernetes/tests/unit/test_utils.py b/kuryr_kubernetes/tests/unit/test_utils.py deleted file mode 100644 index 5ac48f215..000000000 --- a/kuryr_kubernetes/tests/unit/test_utils.py +++ /dev/null @@ -1,577 +0,0 @@ -# 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 unittest import mock -import uuid - -from openstack import exceptions as os_exc -from openstack.network.v2 import port as os_port -from openstack.network.v2 import subnet as os_subnet -from os_vif import objects -from oslo_config import cfg -from oslo_utils import timeutils - -from kuryr_kubernetes import constants as k_const -from kuryr_kubernetes import exceptions as k_exc -from kuryr_kubernetes.objects import vif -from kuryr_kubernetes.tests import base as test_base -from kuryr_kubernetes.tests import fake -from kuryr_kubernetes.tests.unit import kuryr_fixtures as k_fix -from kuryr_kubernetes import utils - -CONF = cfg.CONF - - -class TestUtils(test_base.TestCase): - - def setUp(self): - super().setUp() - cfg.CONF.set_override('resource_tags', [], group='neutron_defaults') - - @mock.patch('socket.gethostname') - def test_get_node_name(self, m_gethostname): - m_gethostname.return_value = 'foo' - res = utils.get_node_name() - self.assertEqual('foo', res) - m_gethostname.assert_called_once_with() - - @mock.patch('requests.get') - def test_get_leader_name(self, m_get): - m_get.return_value = mock.Mock(json=mock.Mock( - return_value={'name': 'foo'})) - res = utils.get_leader_name() - m_get.assert_called_once_with( - 'http://localhost:%d' % CONF.kubernetes.controller_ha_elector_port) - self.assertEqual('foo', res) - - @mock.patch('requests.get') - def test_get_leader_name_malformed(self, m_get): - m_get.return_value = mock.Mock(json=mock.Mock( - return_value={'name2': 'foo'})) - res = utils.get_leader_name() - m_get.assert_called_once_with( - 'http://localhost:%d' % CONF.kubernetes.controller_ha_elector_port) - self.assertIsNone(res) - - @mock.patch('requests.get') - def test_get_leader_name_exc(self, m_get): - m_get.side_effect = Exception - res = utils.get_leader_name() - m_get.assert_called_once_with( - 'http://localhost:%d' % CONF.kubernetes.controller_ha_elector_port) - self.assertIsNone(res) - - @mock.patch('kuryr_kubernetes.os_vif_util.neutron_to_osvif_network') - @mock.patch('kuryr_kubernetes.os_vif_util.neutron_to_osvif_subnet') - def test_get_subnet(self, m_osv_subnet, m_osv_network): - os_net = self.useFixture(k_fix.MockNetworkClient()).client - - subnet = mock.MagicMock() - network = mock.MagicMock() - subnet_id = mock.sentinel.subnet_id - network_id = mock.sentinel.network_id - - neutron_subnet = os_subnet.Subnet(**{'network_id': network_id}) - neutron_network = mock.sentinel.neutron_network - - os_net.get_subnet.return_value = neutron_subnet - os_net.get_network.return_value = neutron_network - - m_osv_subnet.return_value = subnet - m_osv_network.return_value = network - - ret = utils.get_subnet(subnet_id) - - self.assertEqual(network, ret) - os_net.get_subnet.assert_called_once_with(subnet_id) - os_net.get_network.assert_called_once_with(network_id) - m_osv_subnet.assert_called_once_with(neutron_subnet) - m_osv_network.assert_called_once_with(neutron_network) - network.subnets.objects.append.assert_called_once_with(subnet) - - def test_extract_pod_annotation(self): - vif_obj = objects.vif.VIFBase() - ps = vif.PodState(default_vif=vif_obj) - d = ps.obj_to_primitive() - result = utils.extract_pod_annotation(d) - self.assertEqual(vif.PodState.obj_name(), result.obj_name()) - self.assertEqual(vif_obj, result.default_vif) - - def test_extract_pod_annotation_convert(self): - vif_obj = objects.vif.VIFBase() - d = vif_obj.obj_to_primitive() - result = utils.extract_pod_annotation(d) - self.assertEqual(vif.PodState.obj_name(), result.obj_name()) - self.assertEqual(vif_obj, result.default_vif) - - def test__has_kuryrnetwork_crd(self): - kuryrnet_crd = { - "apiVersion": "openstack.org/v1", - "items": [ - - ], - "kind": "KuryrNetworkList", - "metadata": { - "continue": "", - "resourceVersion": "33018", - } - } - - kubernetes = self.useFixture(k_fix.MockK8sClient()).client - kubernetes.get.return_value = kuryrnet_crd - - kuryrnets_url = k_const.K8S_API_CRD_KURYRNETWORKS - resp = utils.has_kuryr_crd(kuryrnets_url) - - self.assertEqual(resp, True) - - def test__has_kuryr_crd_error(self): - crds = [k_const.K8S_API_CRD_KURYRNETWORKS, - k_const.K8S_API_CRD_KURYRNETWORKPOLICIES, - k_const.K8S_API_CRD_KURYRLOADBALANCERS] - - for crd_url in crds: - kubernetes = self.useFixture(k_fix.MockK8sClient()).client - kubernetes.get.side_effect = k_exc.K8sClientException - - resp = utils.has_kuryr_crd(crd_url) - self.assertEqual(resp, False) - - kubernetes.get.assert_called_once() - - def test_get_endpoints_link(self): - service = {'apiVersion': 'v1', - 'kind': 'Service', - 'metadata': {'namespace': 'default', - 'name': 'test'}} - ret = utils.get_endpoints_link(service) - expected_link = "/api/v1/namespaces/default/endpoints/test" - self.assertEqual(expected_link, ret) - - def test_get_service_ports(self): - service = {'spec': {'ports': [ - {'port': 1, 'targetPort': 1}, - {'port': 2, 'name': 'X', 'protocol': 'UDP', 'targetPort': 2}, - {'port': 3, 'name': 'Y', 'protocol': 'SCTP', 'targetPort': 3} - ]}} - expected_ret = [ - {'port': 1, 'name': None, 'protocol': 'TCP', 'targetPort': '1'}, - {'port': 2, 'name': 'X', 'protocol': 'UDP', 'targetPort': '2'}, - {'port': 3, 'name': 'Y', 'protocol': 'SCTP', 'targetPort': '3'}] - - ret = utils.get_service_ports(service) - self.assertEqual(expected_ret, ret) - - @mock.patch('kuryr_kubernetes.utils.get_service_ports') - def test_has_port_changes(self, m_get_service_ports): - service = { - 'apiVersion': 'v1', - 'kind': 'Service', - 'metadata': { - 'name': 'serv-1', - 'namespace': 'ns1' - }, - 'spec': { - 'ports': [ - { - 'port': 1, - 'name': 'X', - 'protocol': 'TCP', - 'targetPort': '1' - } - ] - } - } - lb_crd_spec = { - 'spec': { - 'ports': [ - { - 'name': 'Y', - 'protocol': 'TCP', - 'port': 2, - 'targetPort': 2 - } - ] - } - } - ret = utils.has_port_changes(service, lb_crd_spec) - self.assertTrue(ret) - - @mock.patch('kuryr_kubernetes.utils.get_service_ports') - def test_has_port_changes_more_ports(self, m_get_service_ports): - service = { - 'apiVersion': 'v1', - 'kind': 'Service', - 'metadata': { - 'name': 'serv-1', - 'namespace': 'ns1' - }, - 'spec': { - 'ports': [ - { - 'port': 1, - 'name': 'X', - 'protocol': 'TCP', - 'targetPort': '1' - } - ] - } - } - lb_crd_spec = { - 'spec': { - 'ports': [ - { - 'name': 'X', - 'protocol': 'TCP', - 'port': 1, - 'targetPort': 1 - }, - { - 'name': 'Y', - 'protocol': 'TCP', - 'port': 2, - 'targetPort': 2 - } - ] - } - } - - ret = utils.has_port_changes(service, lb_crd_spec) - self.assertTrue(ret) - - @mock.patch('kuryr_kubernetes.utils.get_service_ports') - def test_has_port_changes_no_changes(self, m_get_service_ports): - - service = { - 'apiVersion': 'v1', - 'kind': 'Service', - 'metadata': { - 'name': 'serv-1', - 'namespace': 'ns1' - }, - 'spec': { - 'ports': [ - { - 'port': 1, - 'name': 'X', - 'protocol': 'TCP', - 'targetPort': '1' - }, - { - 'name': 'Y', - 'protocol': 'TCP', - 'port': 2, - 'targetPort': '2' - } - ] - } - } - - lb_crd_spec = { - 'spec': { - 'ports': [ - { - 'name': 'X', - 'protocol': 'TCP', - 'port': 1, - 'targetPort': '1' - }, - { - 'name': 'Y', - 'protocol': 'TCP', - 'port': 2, - 'targetPort': '2' - } - ] - } - } - - ret = utils.has_port_changes(service, lb_crd_spec) - self.assertFalse(ret) - - def test_get_nodes_ips(self): - os_net = self.useFixture(k_fix.MockNetworkClient()).client - ip1 = os_port.Port( - fixed_ips=[{'ip_address': '10.0.0.1', 'subnet_id': 'foo'}], - trunk_details={'trunk_id': 'wow', 'sub_ports': []}, - ) - ip2 = os_port.Port( - fixed_ips=[{'ip_address': '10.0.0.2', 'subnet_id': 'bar'}], - trunk_details={'trunk_id': 'odd', 'sub_ports': []}, - ) - ip3 = os_port.Port( - fixed_ips=[{'ip_address': '10.0.0.3', 'subnet_id': 'baz'}], - trunk_details=None, - ) - ip4 = os_port.Port( - fixed_ips=[{'ip_address': '10.0.0.4', 'subnet_id': 'zab'}], - trunk_details={'trunk_id': 'eek', 'sub_ports': []}, - ) - ports = (p for p in [ip1, ip2, ip3, ip4]) - - os_net.ports.return_value = ports - trunk_ips = utils.get_nodes_ips(['foo', 'bar']) - os_net.ports.assert_called_once_with(status='ACTIVE') - self.assertEqual(trunk_ips, [ip1.fixed_ips[0]['ip_address'], - ip2.fixed_ips[0]['ip_address']]) - - def test_get_nodes_ips_tagged(self): - CONF.set_override('resource_tags', ['foo'], group='neutron_defaults') - self.addCleanup(CONF.clear_override, 'resource_tags', - group='neutron_defaults') - - os_net = self.useFixture(k_fix.MockNetworkClient()).client - ip1 = os_port.Port( - fixed_ips=[{'ip_address': '10.0.0.1', 'subnet_id': 'foo'}], - trunk_details={'trunk_id': 'wow', 'sub_ports': []}, - ) - ip2 = os_port.Port( - fixed_ips=[{'ip_address': '10.0.0.2', 'subnet_id': 'bar'}], - trunk_details=None, - ) - ports = (p for p in [ip1, ip2]) - - os_net.ports.return_value = ports - trunk_ips = utils.get_nodes_ips(['foo']) - os_net.ports.assert_called_once_with(status='ACTIVE', tags=['foo']) - self.assertEqual(trunk_ips, [ip1.fixed_ips[0]['ip_address']]) - - def test_get_subnet_cidr(self): - os_net = self.useFixture(k_fix.MockNetworkClient()).client - subnet_id = mock.sentinel.subnet_id - subnet = os_subnet.Subnet(cidr='10.0.0.0/24') - os_net.get_subnet.return_value = subnet - - result = utils.get_subnet_cidr(subnet_id) - os_net.get_subnet.assert_called_once_with(subnet_id) - self.assertEqual(result, '10.0.0.0/24') - - def test_get_subnet_cidr_no_such_subnet(self): - os_net = self.useFixture(k_fix.MockNetworkClient()).client - subnet_id = mock.sentinel.subnet_id - os_net.get_subnet.side_effect = os_exc.ResourceNotFound - - self.assertRaises(os_exc.ResourceNotFound, utils.get_subnet_cidr, - subnet_id) - os_net.get_subnet.assert_called_once_with(subnet_id) - - def test_get_current_endpoints_target_with_target_ref(self): - ep = {'addresses': ['10.0.2.107'], 'conditions': {'ready': True}, - 'targetRef': {'kind': 'Pod', 'name': 'test-868d9cbd68-xq2fl', - 'namespace': 'test2'}} - port = {'port': 8080, 'protocol': 'TCP'} - spec_ports = {None: '31d59e41-05db-4a39-8aca-6a9a572c83cd'} - ep_name = 'test' - target = utils.get_current_endpoints_target( - ep, port, spec_ports, ep_name) - self.assertEqual( - target, ('10.0.2.107', 'test-868d9cbd68-xq2fl', 8080, - '31d59e41-05db-4a39-8aca-6a9a572c83cd')) - - def test_get_current_endpoints_target_without_target_ref(self): - ep = {'addresses': ['10.0.1.208'], 'conditions': {'ready': True}} - port = {'port': 8080, 'protocol': 'TCP'} - spec_ports = {None: '4472fab1-f01c-46a7-b197-5cba4f2d7135'} - ep_name = 'test' - target = utils.get_current_endpoints_target( - ep, port, spec_ports, ep_name) - self.assertEqual( - target, ('10.0.1.208', 'test', 8080, - '4472fab1-f01c-46a7-b197-5cba4f2d7135')) - - def test_get_klb_crd_path(self): - res = {'apiVersion': 'v1', - 'kind': 'Endpoints', - 'metadata': {'name': 'my-service', - 'namespace': 'default'}} - self.assertEqual(utils.get_klb_crd_path(res), - '/apis/openstack.org/v1/namespaces/default/' - 'kuryrloadbalancers/my-service') - - def test_get_res_link_core_res(self): - res = {'apiVersion': 'v1', - 'kind': 'Pod', - 'metadata': {'name': 'pod-1', - 'namespace': 'default'}} - self.assertEqual(utils.get_res_link(res), - '/api/v1/namespaces/default/pods/pod-1') - - def test_get_res_link_no_existent(self): - res = {'apiVersion': 'customapi/v1', - 'kind': 'ItsATrap!', - 'metadata': {'name': 'pod-1', - 'namespace': 'default'}} - self.assertRaises(KeyError, utils.get_res_link, res) - - def test_get_res_link_beta_res(self): - res = {'apiVersion': 'networking.k8s.io/v2beta2', - 'kind': 'NetworkPolicy', - 'metadata': {'name': 'np-1', - 'namespace': 'default'}} - self.assertEqual(utils.get_res_link(res), '/apis/networking.k8s.io/' - 'v2beta2/namespaces/default/networkpolicies/np-1') - - def test_get_res_link_no_namespace(self): - res = {'apiVersion': 'v1', - 'kind': 'Namespace', - 'metadata': {'name': 'ns-1'}} - - self.assertEqual(utils.get_res_link(res), '/api/v1/namespaces/ns-1') - - def test_get_res_link_custom_api(self): - res = {'apiVersion': 'openstack.org/v1', - 'kind': 'KuryrPort', - 'metadata': {'name': 'kp-1', - 'namespace': 'default'}} - - self.assertEqual(utils.get_res_link(res), - '/apis/openstack.org/v1/namespaces/default/' - 'kuryrports/kp-1') - - def test_get_res_link_no_apiversion(self): - res = {'kind': 'KuryrPort', - 'metadata': {'name': 'kp-1', - 'namespace': 'default'}} - self.assertRaises(KeyError, utils.get_res_link, res) - - def test_get_api_ver_core_api(self): - path = '/api/v1/namespaces/default/pods/pod-123' - self.assertEqual(utils.get_api_ver(path), 'v1') - - def test_get_api_ver_custom_resource(self): - path = '/apis/openstack.org/v1/namespaces/default/kuryrport/pod-123' - self.assertEqual(utils.get_api_ver(path), 'openstack.org/v1') - - def test_get_api_ver_random_path(self): - path = '/?search=foo' - self.assertRaises(ValueError, utils.get_api_ver, path) - - def test_get_res_selflink_still_available(self): - res = {'metadata': {'selfLink': '/foo'}} - - self.assertEqual(utils.get_res_link(res), '/foo') - - @mock.patch('kuryr_kubernetes.clients.get_network_client') - def test_get_subnet_id(self, m_get_net): - m_net = mock.Mock() - m_get_net.return_value = m_net - subnets = (mock.Mock(id=mock.sentinel.subnet1), - mock.Mock(id=mock.sentinel.subnet2)) - m_net.subnets.return_value = iter(subnets) - filters = {'name': 'foo', 'tags': 'bar'} - sub = utils.get_subnet_id(**filters) - m_net.subnets.assert_called_with(**filters) - self.assertEqual(mock.sentinel.subnet1, sub) - - @mock.patch('kuryr_kubernetes.clients.get_network_client') - def test_get_subnet_not_found(self, m_get_net): - m_net = mock.Mock() - m_get_net.return_value = m_net - m_net.subnets.return_value = iter(()) - filters = {'name': 'foo', 'tags': 'bar'} - sub = utils.get_subnet_id(**filters) - m_net.subnets.assert_called_with(**filters) - self.assertIsNone(sub) - - def test_is_pod_completed_pending(self): - self.assertFalse(utils.is_pod_completed({'status': {'phase': - k_const.K8S_POD_STATUS_PENDING}})) - - def test_is_pod_completed_succeeded(self): - self.assertTrue(utils.is_pod_completed({'status': {'phase': - k_const.K8S_POD_STATUS_SUCCEEDED}})) - - def test_is_pod_completed_failed(self): - self.assertTrue(utils.is_pod_completed({'status': {'phase': - k_const.K8S_POD_STATUS_FAILED}})) - - @mock.patch('kuryr_kubernetes.clients.get_network_client') - def test_cleanup_dead_ports_no_tags(self, m_get_net): - utils.cleanup_dead_ports() - m_get_net.assert_not_called() - - @mock.patch('oslo_utils.timeutils.utcnow') - @mock.patch('kuryr_kubernetes.clients.get_network_client') - @mock.patch('kuryr_kubernetes.clients.get_kubernetes_client') - def test_cleanup_dead_ports(self, m_get_k8s, m_get_net, m_utcnow): - cfg.CONF.set_override('resource_tags', ['foo'], - group='neutron_defaults') - m_net = mock.Mock() - time1 = '2022-04-14T09:00:00Z' - now = '2022-04-14T09:00:00Z' - m_utcnow.return_value = timeutils.parse_isotime(now) - port = os_port.Port(updated_at=time1, tags=['foo']) - m_net.ports.return_value = iter((port,)) - m_get_net.return_value = m_net - - m_k8s = mock.Mock() - m_k8s.get.return_value = {'items': [{'status': {'netId': 'netid'}}]} - m_get_k8s.return_value = m_k8s - - utils.cleanup_dead_ports() - - m_get_net.assert_called_once() - - @mock.patch('oslo_utils.timeutils.utcnow') - @mock.patch('kuryr_kubernetes.clients.get_network_client') - @mock.patch('kuryr_kubernetes.clients.get_kubernetes_client') - def test_cleanup_dead_no_tagged_ports(self, m_get_k8s, m_get_net, - m_utcnow): - cfg.CONF.set_override('resource_tags', ['foo'], - group='neutron_defaults') - m_net = mock.Mock() - time1 = '2022-04-14T09:00:00Z' - now = '2022-04-14T09:16:00Z' - m_utcnow.return_value = timeutils.parse_isotime(now) - port = os_port.Port(updated_at=time1, tags=[]) - m_net.ports.return_value = iter((port,)) - m_get_net.return_value = m_net - - m_k8s = mock.Mock() - m_k8s.get.return_value = {'items': [{'status': {'netId': 'netid'}}]} - m_get_k8s.return_value = m_k8s - - utils.cleanup_dead_ports() - - m_get_net.assert_called_once() - m_net.delete_port.assert_called_once_with(port) - - @mock.patch('kuryr_kubernetes.clients.get_network_client') - @mock.patch('kuryr_kubernetes.clients.get_kubernetes_client') - def test_cleanup_dead_no_networks(self, m_get_k8s, m_get_net): - cfg.CONF.set_override('resource_tags', ['foo'], - group='neutron_defaults') - m_net = mock.Mock() - m_net.ports.return_value = iter([]) - m_get_net.return_value = m_net - - m_k8s = mock.Mock() - m_k8s.get.return_value = {'items': []} - m_get_k8s.return_value = m_k8s - - utils.cleanup_dead_ports() - - m_get_net.assert_called_once() - m_net.delete_port.assert_not_called() - - def test__get_parent_port_ip(self): - os_net = self.useFixture(k_fix.MockNetworkClient()).client - - port_id = str(uuid.uuid4()) - ip_address = mock.sentinel.ip_address - - port_obj = fake.get_port_obj(ip_address=ip_address) - os_net.get_port.return_value = port_obj - - self.assertEqual(ip_address, utils.get_parent_port_ip(port_id)) diff --git a/kuryr_kubernetes/tests/unit/test_watcher.py b/kuryr_kubernetes/tests/unit/test_watcher.py deleted file mode 100644 index 7ec110f86..000000000 --- a/kuryr_kubernetes/tests/unit/test_watcher.py +++ /dev/null @@ -1,347 +0,0 @@ -# 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. - -from eventlet import greenlet -from unittest import mock - -from kuryr_kubernetes.tests import base as test_base -from kuryr_kubernetes.tests.unit import kuryr_fixtures -from kuryr_kubernetes import watcher -from requests import exceptions - - -class TestWatcher(test_base.TestCase): - def setUp(self): - super(TestWatcher, self).setUp() - mock_client = self.useFixture(kuryr_fixtures.MockK8sClient()) - self.client = mock_client.client - - @mock.patch.object(watcher.Watcher, '_start_watch') - def test_add(self, m_start_watch): - paths = ['/test%s' % i for i in range(3)] - m_handler = mock.Mock() - watcher_obj = watcher.Watcher(m_handler) - - for path in paths: - watcher_obj.add(path) - - self.assertEqual(set(paths), watcher_obj._resources) - m_start_watch.assert_not_called() - - @mock.patch.object(watcher.Watcher, '_start_watch') - def test_add_running(self, m_start_watch): - paths = ['/test%s' % i for i in range(3)] - m_handler = mock.Mock() - watcher_obj = watcher.Watcher(m_handler) - watcher_obj._running = True - - for path in paths: - watcher_obj.add(path) - - self.assertEqual(set(paths), watcher_obj._resources) - m_start_watch.assert_has_calls([mock.call(path) for path in paths], - any_order=True) - - @mock.patch.object(watcher.Watcher, '_start_watch') - def test_add_watching(self, m_start_watch): - paths = ['/test%s' % i for i in range(3)] - m_handler = mock.Mock() - watcher_obj = watcher.Watcher(m_handler) - watcher_obj._running = True - m_watching = watcher_obj._watching = mock.MagicMock() - m_watching.__contains__.return_value = True - - for path in paths: - watcher_obj.add(path) - - self.assertEqual(set(paths), watcher_obj._resources) - m_start_watch.assert_not_called() - - @mock.patch.object(watcher.Watcher, '_stop_watch') - def test_remove(self, m_stop_watch): - path = '/test' - m_handler = mock.Mock() - watcher_obj = watcher.Watcher(m_handler) - watcher_obj._resources.add(path) - - watcher_obj.remove(path) - - self.assertEqual(set(), watcher_obj._resources) - m_stop_watch.assert_not_called() - - @mock.patch.object(watcher.Watcher, '_stop_watch') - def test_remove_watching(self, m_stop_watch): - path = '/test' - m_handler = mock.Mock() - watcher_obj = watcher.Watcher(m_handler) - watcher_obj._resources.add(path) - m_watching = watcher_obj._watching = mock.MagicMock() - m_watching.__contains__.return_value = True - - watcher_obj.remove(path) - - self.assertEqual(set(), watcher_obj._resources) - m_stop_watch.assert_called_once_with(path) - - @mock.patch.object(watcher.Watcher, '_start_watch') - def test_start(self, m_start_watch): - paths = ['/test%s' % i for i in range(3)] - m_handler = mock.Mock() - watcher_obj = watcher.Watcher(m_handler) - watcher_obj._resources.update(paths) - - watcher_obj.start() - - self.assertTrue(watcher_obj._running) - m_start_watch.assert_has_calls([mock.call(path) for path in paths], - any_order=True) - - @mock.patch.object(watcher.Watcher, '_start_watch') - def test_start_already_watching(self, m_start_watch): - paths = ['/test%s' % i for i in range(3)] - m_handler = mock.Mock() - watcher_obj = watcher.Watcher(m_handler) - watcher_obj._resources.update(paths) - m_watching = watcher_obj._watching = mock.MagicMock() - m_watching.__iter__.return_value = paths - - watcher_obj.start() - - self.assertTrue(watcher_obj._running) - m_start_watch.assert_not_called() - - @mock.patch.object(watcher.Watcher, '_stop_watch') - def test_stop(self, m_stop_watch): - paths = ['/test%s' % i for i in range(3)] - m_handler = mock.Mock() - watcher_obj = watcher.Watcher(m_handler) - watcher_obj._resources.update(paths) - - watcher_obj.stop() - - self.assertFalse(watcher_obj._running) - m_stop_watch.assert_not_called() - - @mock.patch.object(watcher.Watcher, '_stop_watch') - def test_stop_watching(self, m_stop_watch): - paths = ['/test%s' % i for i in range(3)] - m_handler = mock.Mock() - watcher_obj = watcher.Watcher(m_handler) - watcher_obj._resources.update(paths) - m_watching = watcher_obj._watching = mock.MagicMock() - m_watching.__iter__.return_value = paths - - watcher_obj.stop() - - self.assertFalse(watcher_obj._running) - m_stop_watch.assert_has_calls([mock.call(path) for path in paths], - any_order=True) - - @mock.patch.object(watcher.Watcher, '_watch') - def test_start_watch(self, m_watch): - path = '/test' - m_handler = mock.Mock() - watcher_obj = watcher.Watcher(m_handler) - - watcher_obj._start_watch(path) - - m_watch.assert_called_once_with(path) - self.assertTrue(watcher_obj._idle.get(path)) - self.assertIn(path, watcher_obj._watching) - - def test_start_watch_threaded(self): - path = '/test' - m_tg = mock.Mock() - m_tg.add_thread.return_value = mock.sentinel.watch_thread - m_handler = mock.Mock() - watcher_obj = watcher.Watcher(m_handler, m_tg) - - watcher_obj._start_watch(path) - - m_tg.add_thread.assert_called_once_with(watcher_obj._watch, path) - self.assertTrue(watcher_obj._idle.get(path)) - self.assertEqual(mock.sentinel.watch_thread, - watcher_obj._watching.get(path)) - - def test_stop_watch_threaded(self): - path = '/test' - m_tg = mock.Mock() - m_th = mock.Mock() - m_tt = mock.Mock() - m_handler = mock.Mock() - watcher_obj = watcher.Watcher(m_handler, m_tg) - watcher_obj._idle[path] = True - watcher_obj._watching[path] = m_th - watcher_obj._timers[path] = m_tt - - watcher_obj._stop_watch(path) - - m_tt.stop.assert_called() - m_th.stop.assert_called() - - def test_stop_watch_idle(self): - path = '/test' - m_tg = mock.Mock() - m_th = mock.Mock() - m_handler = mock.Mock() - watcher_obj = watcher.Watcher(m_handler, m_tg) - watcher_obj._idle[path] = False - watcher_obj._watching[path] = m_th - - watcher_obj._stop_watch(path) - - m_th.kill.assert_not_called() - - def _test_watch_mock_events(self, watcher_obj, events): - def client_watch(client_path): - for e in events: - self.assertTrue(watcher_obj._idle[client_path]) - yield e - self.assertTrue(watcher_obj._idle[client_path]) - self.client.watch.side_effect = client_watch - - @staticmethod - def _test_watch_create_watcher(path, handler, timeout=0): - watcher_obj = watcher.Watcher(handler, timeout=timeout) - watcher_obj._running = True - watcher_obj._resources.add(path) - watcher_obj._idle[path] = True - watcher_obj._watching[path] = None - return watcher_obj - - @mock.patch('sys.exit') - def test_watch(self, m_sys_exit): - path = '/test' - events = [{'e': i} for i in range(3)] - - def handler(event): - self.assertFalse(watcher_obj._idle[path]) - - m_handler = mock.Mock() - m_handler.side_effect = handler - watcher_obj = self._test_watch_create_watcher(path, m_handler) - self._test_watch_mock_events(watcher_obj, events) - - watcher_obj._watch(path) - - self.assertEqual(0, watcher_obj._timeout) - m_handler.assert_has_calls([mock.call(e) for e in events]) - # After all events have been "handled", since there is only - # one handler, we'll gracefully exit - m_sys_exit.assert_called_once_with(1) - - @mock.patch('sys.exit') - def test_watch_stopped(self, m_sys_exit): - path = '/test' - events = [{'e': i} for i in range(3)] - - def handler(event): - self.assertFalse(watcher_obj._idle[path]) - watcher_obj._running = False - - m_handler = mock.Mock() - m_handler.side_effect = handler - watcher_obj = self._test_watch_create_watcher(path, m_handler) - self._test_watch_mock_events(watcher_obj, events) - - watcher_obj._watch(path) - - m_handler.assert_called_once_with(events[0]) - self.assertNotIn(path, watcher_obj._idle) - self.assertNotIn(path, watcher_obj._watching) - m_sys_exit.assert_called_once_with(1) - - @mock.patch('sys.exit') - def test_watch_removed(self, m_sys_exit): - path = '/test' - events = [{'e': i} for i in range(3)] - - def handler(event): - self.assertFalse(watcher_obj._idle[path]) - watcher_obj._resources.remove(path) - - m_handler = mock.Mock() - m_handler.side_effect = handler - watcher_obj = self._test_watch_create_watcher(path, m_handler) - self._test_watch_mock_events(watcher_obj, events) - - watcher_obj._watch(path) - - m_handler.assert_called_once_with(events[0]) - self.assertNotIn(path, watcher_obj._idle) - self.assertNotIn(path, watcher_obj._watching) - m_sys_exit.assert_called_once_with(1) - - @mock.patch('sys.exit') - def test_watch_interrupted(self, m_sys_exit): - path = '/test' - events = [{'e': i} for i in range(3)] - - def handler(event): - self.assertFalse(watcher_obj._idle[path]) - raise greenlet.GreenletExit() - - m_handler = mock.Mock() - m_handler.side_effect = handler - watcher_obj = self._test_watch_create_watcher(path, m_handler) - self._test_watch_mock_events(watcher_obj, events) - - self.assertRaises(greenlet.GreenletExit, watcher_obj._watch, path) - - m_handler.assert_called_once_with(events[0]) - self.assertNotIn(path, watcher_obj._idle) - self.assertNotIn(path, watcher_obj._watching) - m_sys_exit.assert_called_once_with(1) - - @mock.patch('sys.exit') - def test_watch_client_request_failed(self, m_sys_exit): - path = '/test' - m_handler = mock.Mock() - watcher_obj = self._test_watch_create_watcher(path, m_handler) - watcher_obj._watch(path) - self.client.watch.side_effect = exceptions.ChunkedEncodingError( - "Connection Broken") - - self.client.watch.assert_called_once() - self.assertFalse(watcher_obj._alive) - m_sys_exit.assert_called_once_with(1) - - @mock.patch('sys.exit') - def test_watch_retry(self, m_sys_exit): - path = '/test' - events = [{'e': i} for i in range(3)] - side_effects = [exceptions.ChunkedEncodingError("Connection Broken")] - side_effects.extend(None for _ in events) - - m_handler = mock.Mock() - m_handler.side_effect = side_effects - watcher_obj = self._test_watch_create_watcher(path, m_handler, 10) - self._test_watch_mock_events(watcher_obj, events) - - watcher_obj._watch(path) - - m_handler.assert_has_calls([mock.call(e) for e in events]) - m_sys_exit.assert_called_once_with(1) - - def test_watch_restart(self): - tg = mock.Mock() - w = watcher.Watcher(lambda e: None, tg) - w.add('/test') - w.start() - tg.add_thread.assert_called_once_with(mock.ANY, '/test') - w.stop() - tg.add_thread = mock.Mock() # Reset mock. - w.start() - tg.add_thread.assert_called_once_with(mock.ANY, '/test') diff --git a/kuryr_kubernetes/utils.py b/kuryr_kubernetes/utils.py deleted file mode 100644 index cfac007ac..000000000 --- a/kuryr_kubernetes/utils.py +++ /dev/null @@ -1,846 +0,0 @@ -# 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 ipaddress -import os -import random -import re -import socket -import time - -import requests - -from kuryr.lib._i18n import _ -from kuryr.lib import constants as kl_const -from openstack import exceptions as os_exc -from os_vif import objects -from oslo_cache import core as cache -from oslo_config import cfg -from oslo_log import log -from oslo_serialization import jsonutils -from oslo_utils import timeutils - -from kuryr_kubernetes import clients -from kuryr_kubernetes import constants -from kuryr_kubernetes import exceptions -from kuryr_kubernetes.objects import lbaas as obj_lbaas -from kuryr_kubernetes.objects import vif -from kuryr_kubernetes import os_vif_util - -CONF = cfg.CONF -LOG = log.getLogger(__name__) - -VALID_MULTI_POD_POOLS_OPTS = {'noop': ['neutron-vif', - 'nested-vlan', - 'nested-macvlan', - 'nested-dpdk'], - 'neutron': ['neutron-vif'], - 'nested': ['nested-vlan'], - } -DEFAULT_TIMEOUT = 500 -DEFAULT_INTERVAL = 1 -DEFAULT_JITTER = 3 -MAX_BACKOFF = 60 -MAX_ATTEMPTS = 10 -ZOMBIE_AGE = 600 - - -subnet_caching_opts = [ - cfg.BoolOpt('caching', default=True, - help=_('Enable caching of subnets.')), - cfg.IntOpt('cache_time', default=3600, - help=_('TTL, in seconds, for cached subnets')), -] - -nodes_caching_opts = [ - cfg.BoolOpt('caching', default=True, - help=_('Enable caching of nodes.')), - cfg.IntOpt('cache_time', default=3600, - help=_('TTL, in seconds, for cached nodes')), -] - -CONF.register_opts(subnet_caching_opts, "subnet_caching") -CONF.register_opts(nodes_caching_opts, "nodes_caching") - -cache.configure(CONF) -subnet_cache_region = cache.create_region() -MEMOIZE = cache.get_memoization_decorator( - CONF, subnet_cache_region, "subnet_caching") -cache.configure_cache_region(CONF, subnet_cache_region) - -nodes_cache_region = cache.create_region() -MEMOIZE_NODE = cache.get_memoization_decorator( - CONF, nodes_cache_region, "nodes_caching") -cache.configure_cache_region(CONF, nodes_cache_region) - -RESOURCE_MAP = {'Endpoints': 'endpoints', - 'KuryrLoadBalancer': 'kuryrloadbalancers', - 'KuryrNetwork': 'kuryrnetworks', - 'KuryrNetworkPolicy': 'kuryrnetworkpolicies', - 'KuryrPort': 'kuryrports', - 'Namespace': 'namespaces', - 'NetworkPolicy': 'networkpolicies', - 'Node': 'nodes', - 'Pod': 'pods', - 'Service': 'services', - 'Machine': 'machines'} -API_VER_MAP = {'NetworkPolicy': 'networking.k8s.io/v1', - 'Pod': 'v1', - 'Service': 'v1'} -API_RE = re.compile(r'v\d+') - - -def get_klb_crd_path(obj): - """Return klb crd path from provided resource""" - namespace = obj['metadata']['namespace'] - lb_name = obj['metadata']['name'] - - return (f"{constants.K8S_API_CRD_NAMESPACES}/" - f"{namespace}/" - f"kuryrloadbalancers/" - f"{lb_name}") - - -def get_res_link(obj): - """Return selfLink equivalent for provided resource""" - # First try, if we still have it - try: - return obj['metadata']['selfLink'] - except KeyError: - pass - - # If not, let's proceed with the path assembling. - try: - res_type = RESOURCE_MAP[obj['kind']] - except KeyError: - LOG.error('Unknown resource kind: %s', obj.get('kind')) - raise - - namespace = '' - if obj['metadata'].get('namespace'): - namespace = f"/namespaces/{obj['metadata']['namespace']}" - - try: - api = f"/apis/{obj['apiVersion']}" - if API_RE.match(obj['apiVersion']): - api = f"/api/{obj['apiVersion']}" - except KeyError: - LOG.error("Object doesn't have an apiVersion available: %s", obj) - raise - - return f"{api}{namespace}/{res_type}/{obj['metadata']['name']}" - - -def get_api_ver(path): - """Get apiVersion out of resource path. - - Path usually is something simillar to: - - /api/v1/namespaces/default/pods/pod-5bb648d658-55n76 - - in case of core resources, and: - - /apis/openstack.org/v1/namespaces/default/kuryrloadbalancers/lb-324 - - in case of custom resoures. - """ - if path.startswith('/api/'): - return path.split('/')[2] - - if path.startswith('/apis/'): - return '/'.join(path.split('/')[2:4]) - - raise ValueError('Provided path is not Kubernetes api path: %s', path) - - -def utf8_json_decoder(byte_data): - """Deserializes the bytes into UTF-8 encoded JSON. - - :param byte_data: The bytes to be converted into the UTF-8 encoded JSON. - :returns: The UTF-8 encoded JSON represented by Python dictionary format. - """ - return jsonutils.loads(byte_data.decode('utf8')) - - -def convert_netns(netns): - """Convert /proc based netns path to Docker-friendly path. - - When CONF.docker_mode is set this method will change /proc to - /CONF.netns_proc_dir. This allows netns manipulations to work when running - in Docker container on Kubernetes host. - - :param netns: netns path to convert. - :return: Converted netns path. - """ - if CONF.cni_daemon.docker_mode: - return netns.replace('/proc', CONF.cni_daemon.netns_proc_dir) - else: - return netns - - -def get_res_unique_name(resource): - """Returns a unique name for the resource like pod or CRD. - - It returns a unique name for the resource composed of its name and the - namespace it is created in or just name for cluster-scoped resources. - - :returns: String with name of the resource - """ - try: - return "%(namespace)s/%(name)s" % resource['metadata'] - except KeyError: - return "%(name)s" % resource['metadata'] - - -def check_suitable_multi_pool_driver_opt(pool_driver, pod_driver): - return pod_driver in VALID_MULTI_POD_POOLS_OPTS.get(pool_driver, []) - - -def exponential_sleep(deadline, attempt, interval=DEFAULT_INTERVAL, - max_backoff=MAX_BACKOFF, jitter=DEFAULT_JITTER): - """Sleep for exponential duration. - - :param deadline: sleep timeout duration in seconds. - :param attempt: attempt count of sleep function. - :param interval: minimal time interval to sleep - :param max_backoff: maximum time to sleep - :param jitter: max value of jitter added to the sleep time - :return: the actual time that we've slept - """ - now = time.time() - seconds_left = deadline - now - - if seconds_left <= 0: - return 0 - - to_sleep = exponential_backoff(attempt, interval, max_backoff=max_backoff, - jitter=jitter) - - if to_sleep > seconds_left: - to_sleep = seconds_left - - if to_sleep < interval: - to_sleep = interval - - time.sleep(to_sleep) - return to_sleep - - -def exponential_backoff(attempt, interval=DEFAULT_INTERVAL, - max_backoff=MAX_BACKOFF, jitter=DEFAULT_JITTER): - """Return exponential backoff duration with jitter. - - This implements a variation of exponential backoff algorithm [1] (expected - backoff E(c) = interval * 2 ** attempt / 2). - - [1] https://en.wikipedia.org/wiki/Exponential_backoff - """ - - if attempt >= MAX_ATTEMPTS: - # No need to calculate very long intervals - attempt = MAX_ATTEMPTS - - backoff = 2 ** attempt * interval - - if max_backoff is not None and backoff > max_backoff: - backoff = max_backoff - - if jitter: - backoff += random.randint(0, jitter) - - return backoff - - -def get_node_name(): - # leader-elector container based on K8s way of doing leader election is - # assuming that hostname it sees is the node id. Containers within a pod - # are sharing the hostname, so this will match what leader-elector returns. - return socket.gethostname() - - -def get_leader_name(): - url = 'http://localhost:%d' % CONF.kubernetes.controller_ha_elector_port - try: - return requests.get(url).json()['name'] - except Exception: - LOG.exception('Error when fetching current leader pod name.') - # NOTE(dulek): Assuming there's no leader when we can't contact leader - # elector container. - return None - - -@MEMOIZE_NODE -def get_nodes_ips(node_subnets): - """Get the IPs of the trunk ports associated to the deployment.""" - trunk_ips = [] - os_net = clients.get_network_client() - tags = CONF.neutron_defaults.resource_tags - if tags: - ports = os_net.ports(status='ACTIVE', tags=tags) - else: - # NOTE(ltomasbo: if tags are not used, assume all the trunk ports are - # part of the kuryr deployment - ports = os_net.ports(status='ACTIVE') - for port in ports: - if (port.trunk_details and port.fixed_ips and - port.fixed_ips[0]['subnet_id'] in node_subnets): - trunk_ips.append(port.fixed_ips[0]['ip_address']) - return trunk_ips - - -@MEMOIZE -def get_subnet(subnet_id): - os_net = clients.get_network_client() - - n_subnet = os_net.get_subnet(subnet_id) - n_network = os_net.get_network(n_subnet.network_id) - - subnet = os_vif_util.neutron_to_osvif_subnet(n_subnet) - network = os_vif_util.neutron_to_osvif_network(n_network) - network.subnets.objects.append(subnet) - return network - - -@MEMOIZE -def get_subnet_cidr(subnet_id): - os_net = clients.get_network_client() - try: - subnet_obj = os_net.get_subnet(subnet_id) - except os_exc.ResourceNotFound: - LOG.exception("Subnet %s CIDR not found!", subnet_id) - raise - return subnet_obj.cidr - - -def get_subnet_id(**filters): - os_net = clients.get_network_client() - subnets = os_net.subnets(**filters) - - try: - return next(subnets).id - except StopIteration: - return None - - -@MEMOIZE -def get_subnets_id_cidrs(subnet_ids): - os_net = clients.get_network_client() - subnets = os_net.subnets() - cidrs = [(subnet.id, subnet.cidr) for subnet in subnets - if subnet.id in subnet_ids] - if len(cidrs) != len(subnet_ids): - existing = {subnet.id for subnet in subnets} - missing = set(subnet_ids) - existing - LOG.exception("CIDRs of subnets %s not found!", missing) - raise os_exc.ResourceNotFound() - return cidrs - - -def get_subnets_cidrs(subnet_ids): - return [x[1] for x in get_subnets_id_cidrs(subnet_ids)] - - -@MEMOIZE -def _get_subnetpool(subnetpool_id): - os_net = clients.get_network_client() - try: - subnetpool_obj = os_net.get_subnet_pool(subnetpool_id) - except os_exc.ResourceNotFound: - LOG.exception("Subnetpool %s not found!", subnetpool_id) - raise - return subnetpool_obj - - -def get_subnetpool_version(subnetpool_id): - subnetpool_obj = _get_subnetpool(subnetpool_id) - return subnetpool_obj.ip_version - - -def get_subnetpool_cidrs(subnetpool_id): - subnetpool_obj = _get_subnetpool(subnetpool_id) - return subnetpool_obj.prefixes - - -def extract_pod_annotation(annotation): - obj = objects.base.VersionedObject.obj_from_primitive(annotation) - # FIXME(dulek): This is code to maintain compatibility with Queens. We can - # remove it once we stop supporting upgrading from Queens, - # most likely in Stein. Note that this requires being sure - # that *all* the pod annotations are in new format. - if obj.obj_name() != vif.PodState.obj_name(): - # This is old format of annotations - single VIF object. We need to - # pack it in PodState object. - obj = vif.PodState(default_vif=obj) - - return obj - - -def has_limit(quota): - NO_LIMIT = -1 - return quota['limit'] != NO_LIMIT - - -def is_available(resource, resource_quota): - availability = resource_quota['limit'] - resource_quota['used'] - if availability <= 0: - LOG.error("Neutron quota exceeded for %s. Used %d out of %d limit.", - resource, resource_quota['used'], resource_quota['limit']) - return False - elif availability <= 3: - LOG.warning("Neutron quota low for %s. Used %d out of %d limit.", - resource, resource_quota['used'], resource_quota['limit']) - return True - - -def has_kuryr_crd(crd_url): - k8s = clients.get_kubernetes_client() - try: - k8s.get(crd_url, json=False, headers={'Connection': 'close'}) - except exceptions.K8sResourceNotFound: - LOG.error('CRD %s does not exists.', crd_url) - except exceptions.K8sClientException: - LOG.exception('Error fetching CRD %s, assuming it does not exist.', - crd_url) - return False - return True - - -def get_lbaas_spec(k8s_object): - # k8s_object can be service or endpoint - try: - annotations = k8s_object['metadata']['annotations'] - annotation = annotations[constants.K8S_ANNOTATION_LBAAS_SPEC] - except KeyError: - return None - obj_dict = jsonutils.loads(annotation) - obj = obj_lbaas.LBaaSServiceSpec.obj_from_primitive(obj_dict) - LOG.debug("Got LBaaSServiceSpec from annotation: %r", obj) - return obj - - -def set_lbaas_spec(service, lbaas_spec): - # TODO(ivc): extract annotation interactions - if lbaas_spec is None: - LOG.debug("Removing LBaaSServiceSpec annotation: %r", lbaas_spec) - annotation = None - else: - lbaas_spec.obj_reset_changes(recursive=True) - LOG.debug("Setting LBaaSServiceSpec annotation: %r", lbaas_spec) - annotation = jsonutils.dumps(lbaas_spec.obj_to_primitive(), - sort_keys=True) - svc_link = get_res_link(service) - ep_link = get_endpoints_link(service) - k8s = clients.get_kubernetes_client() - - try: - k8s.annotate(ep_link, - {constants.K8S_ANNOTATION_LBAAS_SPEC: annotation}) - except exceptions.K8sResourceNotFound as ex: - LOG.debug("Failed to annotate svc: %s", ex) - raise exceptions.ResourceNotReady(ep_link) - except exceptions.K8sClientException: - LOG.debug("Failed to annotate endpoint %r", ep_link) - raise - try: - k8s.annotate(svc_link, - {constants.K8S_ANNOTATION_LBAAS_SPEC: annotation}, - resource_version=service['metadata']['resourceVersion']) - except exceptions.K8sResourceNotFound as ex: - LOG.debug("Failed to annotate svc: %s", ex) - raise exceptions.ResourceNotReady(svc_link) - except exceptions.K8sClientException: - LOG.exception("Failed to annotate svc: %r", svc_link) - raise - - -def get_lbaas_state(endpoint): - try: - annotations = endpoint['metadata']['annotations'] - annotation = annotations[constants.K8S_ANNOTATION_LBAAS_STATE] - except KeyError: - return None - obj_dict = jsonutils.loads(annotation) - obj = obj_lbaas.LBaaSState.obj_from_primitive(obj_dict) - LOG.debug("Got LBaaSState from annotation: %r", obj) - return obj - - -def set_lbaas_state(endpoints, lbaas_state): - # TODO(ivc): extract annotation interactions - if lbaas_state is None: - LOG.debug("Removing LBaaSState annotation: %r", lbaas_state) - annotation = None - else: - lbaas_state.obj_reset_changes(recursive=True) - LOG.debug("Setting LBaaSState annotation: %r", lbaas_state) - annotation = jsonutils.dumps(lbaas_state.obj_to_primitive(), - sort_keys=True) - k8s = clients.get_kubernetes_client() - k8s.annotate(get_res_link(endpoints), - {constants.K8S_ANNOTATION_LBAAS_STATE: annotation}, - resource_version=endpoints['metadata']['resourceVersion']) - - -def get_endpoints_link(service): - svc_link = get_res_link(service) - link_parts = svc_link.split('/') - - if link_parts[-2] != 'services': - raise exceptions.IntegrityError( - f"Unsupported service link: {svc_link}") - link_parts[-2] = 'endpoints' - - return "/".join(link_parts) - - -def get_service_link(endpoints): - endpoints_link = get_res_link(endpoints) - link_parts = endpoints_link.split('/') - - if link_parts[-2] != 'endpoints': - raise exceptions.IntegrityError( - f"Unsupported endpoints link: {endpoints_link}") - link_parts[-2] = 'services' - - return "/".join(link_parts) - - -def has_port_changes(service, loadbalancer_crd): - if not loadbalancer_crd: - return False - link = get_res_link(service) - svc_port_set = service['spec'].get('ports') - - for port in svc_port_set: - port['targetPort'] = str(port['targetPort']) - spec_port_set = loadbalancer_crd['spec'].get('ports', []) - if spec_port_set: - if len(svc_port_set) != len(spec_port_set): - return True - pairs = zip(svc_port_set, spec_port_set) - diff = any(x != y for x, y in pairs) - if diff: - LOG.debug("LBaaS spec ports %(spec_ports)s != %(svc_ports)s " - "for %(link)s" % {'spec_ports': spec_port_set, - 'svc_ports': svc_port_set, - 'link': link}) - return diff - return False - - -def get_service_ports(service): - return [{'name': port.get('name'), - 'protocol': port.get('protocol', 'TCP'), - 'port': port['port'], - 'targetPort': str(port['targetPort'])} - for port in service['spec']['ports']] - - -@MEMOIZE -def get_service_subnet_version(): - os_net = clients.get_network_client() - svc_subnet_id = CONF.neutron_defaults.service_subnet - try: - svc_subnet = os_net.get_subnet(svc_subnet_id) - except os_exc.ResourceNotFound: - LOG.exception("Service subnet %s not found", svc_subnet_id) - raise - return svc_subnet.ip_version - - -def clean_lb_crd_status(loadbalancer_name): - namespace, name = loadbalancer_name.split('/') - k8s = clients.get_kubernetes_client() - try: - k8s.patch_crd('status', f'{constants.K8S_API_CRD_NAMESPACES}' - f'/{namespace}/kuryrloadbalancers/{name}', {}) - except exceptions.K8sResourceNotFound: - LOG.debug('KuryrLoadbalancer CRD not found %s', - name) - except exceptions.K8sClientException: - LOG.exception('Error updating KuryrLoadbalancer CRD %s', - name) - raise - - -def is_kubernetes_default_resource(obj): - """Check if Object is a resource associated to the API - - Verifies if the Object is on the default namespace - and has the name kubernetes. Those name and namespace - are given to Kubernetes Service and Endpoints for the API. - - :param obj: Kubernetes object dict - :returns: True if is default resource for the API, false - otherwise. - """ - return (obj['metadata']['name'] == 'kubernetes' and - obj['metadata']['namespace'] == 'default') - - -def get_pod_by_ip(pod_ip, namespace=None): - k8s = clients.get_kubernetes_client() - pod = {} - try: - if namespace: - pods = k8s.get(f'{constants.K8S_API_BASE}/namespaces/{namespace}/' - f'pods?fieldSelector=status.phase=Running,' - f'status.podIP={pod_ip}') - else: - pods = k8s.get(f'{constants.K8S_API_BASE}/' - f'pods?fieldSelector=status.phase=Running,' - f'status.podIP={pod_ip}') - except exceptions.K8sClientException: - LOG.exception('Error retrieving Pod with IP %s', pod_ip) - raise - if pods.get('items'): - # Only one Pod should have the IP - return pods['items'][0] - return pod - - -def get_current_endpoints_target(ep, port, spec_ports, ep_name): - """Retrieve details about one specific Endpoint target - - Defines the details about the Endpoint target, such as the - target address, name, port value and the Pool ID. In case, - the Endpoints has no targetRef defined, the name of the - target will be the same as the Endpoint. - - :param ep: Endpoint on the Endpoints object - :param port: Endpoint port - :param spec_ports: dict of port name associated to pool ID - :param ep_name: Name of the Endpoints object - :returns: Tuple with target address, target name, port number - and pool ID. - """ - target_ref = ep.get('targetRef', {}) - pod_name = ep_name - # NOTE(maysams): As we don't support dual-stack, we assume - # only one address is possible on the addresses field. - address = ep['addresses'][0] - if target_ref: - pod_name = target_ref.get('name', '') - return (address, pod_name, port['port'], - spec_ports.get(port.get('name'))) - - -def get_subnet_by_ip(nodes_subnets, target_ip): - ip = ipaddress.ip_address(target_ip) - for nodes_subnet in nodes_subnets: - if ip in ipaddress.ip_network(nodes_subnet[1]): - return nodes_subnet - - return None - - -def get_kuryrloadbalancer(name, namespace): - k8s = clients.get_kubernetes_client() - try: - return k8s.get(f'{constants.K8S_API_CRD_NAMESPACES}/' - f'{namespace}/kuryrloadbalancers/' - f'{name}') - except exceptions.K8sResourceNotFound: - return {} - - -def is_pod_completed(pod): - try: - return (pod['status']['phase'] in - (constants.K8S_POD_STATUS_SUCCEEDED, - constants.K8S_POD_STATUS_FAILED)) - except KeyError: - return False - - -def is_host_network(pod): - return pod['spec'].get('hostNetwork', False) - - -def is_pod_static(pod): - """Checks if Pod is static by comparing annotations.""" - try: - annotations = pod['metadata']['annotations'] - config_source = annotations[constants.K8S_ANNOTATION_CONFIG_SOURCE] - return config_source != 'api' - except KeyError: - return False - - -def get_nodename(): - # NOTE(dulek): At first try to get it using environment variable, - # otherwise assume hostname is the nodename. - try: - nodename = os.environ['KUBERNETES_NODE_NAME'] - except KeyError: - # NOTE(dulek): By default K8s nodeName is lowercased hostname. - nodename = socket.gethostname().lower() - return nodename - - -def get_referenced_object(obj, kind): - """Get referenced object. - - Helper function for getting objects out of the CRDs like - KuryrLoadBalancer, KuryrNetworkPolicy or KuryrPort needed solely for - creating Event object, so there will be no exceptions raises from this - function. - """ - for ref in obj['metadata'].get('ownerReferences', []): - if ref['kind'] != kind: - continue - - try: - return {'kind': kind, - 'apiVersion': ref['apiVersion'], - 'metadata': {'namespace': obj['metadata']['namespace'], - 'name': ref['name'], - 'uid': ref['uid']}} - except KeyError: - LOG.debug("Not all needed keys was found in ownerReferences " - "list: %s", ref) - - # There was no ownerReferences field, let's query API - k8s = clients.get_kubernetes_client() - data = {'metadata': {'name': obj['metadata']['name']}, - 'kind': kind, - 'apiVersion': API_VER_MAP[kind]} - if obj['metadata'].get('namespace'): - data['metadata']['namespace'] = obj['metadata']['namespace'] - try: - url = get_res_link(data) - except KeyError: - LOG.debug("Not all needed data was found in provided object: %s", - data) - return - - try: - return k8s.get(url) - except exceptions.K8sClientException: - LOG.debug('Error when fetching %s to add an event %s, ignoring', - kind, get_res_unique_name(obj)) - - -def cleanup_dead_ports(): - tags = set(CONF.neutron_defaults.resource_tags) - if not tags: - # NOTE(gryf): there is no reliable way for removing kuryr-related - # ports if there are no tags enabled - without tags there is a chance, - # that ports are down, created by someone/something else and would - # be deleted. - # Perhaps a be better idea to would be to have some mark in other - # field during port creation to identify "our" ports. - return - - os_net = clients.get_network_client() - k8s = clients.get_kubernetes_client() - - try: - crds = k8s.get(constants.K8S_API_CRD_KURYRNETWORKS) - except exceptions.K8sClientException as ex: - LOG.exception('Error fetching KuryrNetworks: %s', ex) - return - - for item in crds['items']: - network_id = item.get('status', {}).get('netId') - if not network_id: - continue - - for port in os_net.ports(status='DOWN', network_id=network_id, - device_owner=kl_const.DEVICE_OWNER, - not_tags=list(tags)): - now = timeutils.utcnow(True) - port_time = timeutils.parse_isotime(port.updated_at) - # NOTE(gryf): if port hanging more than 10 minutes already in DOWN - # state, consider it as a dead one. - if (now - port_time).seconds > ZOMBIE_AGE: - try: - os_net.delete_port(port) - except os_exc.SDKException as ex: - LOG.warning('There was an issue with port "%s" ' - 'removal: %s', port, ex) - - -def cleanup_dead_networks(): - """Cleanup all the dead networks and subnets without ports""" - - tags = set(CONF.neutron_defaults.resource_tags) - if not tags: - return - - os_net = clients.get_network_client() - k8s = clients.get_kubernetes_client() - - desc = ",".join(CONF.neutron_defaults.resource_tags) - - try: - crds = k8s.get(constants.K8S_API_CRD_KURYRNETWORKS) - except exceptions.K8sClientException as ex: - LOG.exception('Error fetching KuryrNetworks: %s', ex) - return - - kuryr_net_ids = [i['status']['netId'] for i in crds['items'] - if i.get('status', {}).get('netId')] - - for net in os_net.networks(description=desc): - - if net.id in kuryr_net_ids: - # Find out, if there are more subnets than expected, which suppose - # to not have tags. - for subnet in os_net.subnets(network_id=net.id, - not_tags=list(tags)): - now = timeutils.utcnow(True) - subnet_time = timeutils.parse_isotime(subnet.updated_at) - if (now - subnet_time).seconds > ZOMBIE_AGE: - try: - os_net.delete_subnet(subnet) - except os_exc.SDKException as ex: - LOG.warning('There was an issue with removing subnet ' - '"%s": %s', subnet, ex) - continue - - if len(list(os_net.ports(network_id=net.id))) > 0: - continue - - now = timeutils.utcnow(True) - net_time = timeutils.parse_isotime(net.updated_at) - # NOTE(gryf): if network hanging more than 10 minutes consider it as a - # orphaned. - if (now - net_time).seconds > ZOMBIE_AGE: - try: - os_net.delete_network(net) - except os_exc.SDKException as ex: - LOG.warning('There was an issue with network "%s" ' - 'removal: %s', net, ex) - - -def get_parent_port_id(vif_obj): - os_net = clients.get_network_client() - tags = [] - - if CONF.neutron_defaults.resource_tags: - tags = CONF.neutron_defaults.resource_tags - - trunks = os_net.trunks(tags=tags) - - for trunk in trunks: - for sp in trunk.sub_ports: - if sp['port_id'] == vif_obj.id: - return trunk.port_id - - return None - - -def get_parent_port_ip(port_id): - os_net = clients.get_network_client() - parent_port = os_net.get_port(port_id) - return parent_port.fixed_ips[0]['ip_address'] diff --git a/kuryr_kubernetes/version.py b/kuryr_kubernetes/version.py deleted file mode 100644 index 89900c997..000000000 --- a/kuryr_kubernetes/version.py +++ /dev/null @@ -1,15 +0,0 @@ -# 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 pbr.version - -version_info = pbr.version.VersionInfo('kuryr_kubernetes') diff --git a/kuryr_kubernetes/watcher.py b/kuryr_kubernetes/watcher.py deleted file mode 100644 index c17fa3f0e..000000000 --- a/kuryr_kubernetes/watcher.py +++ /dev/null @@ -1,239 +0,0 @@ -# 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 sys -import time - -from oslo_config import cfg -from oslo_log import log as logging - -from kuryr_kubernetes import clients -from kuryr_kubernetes import exceptions -from kuryr_kubernetes.handlers import health -from kuryr_kubernetes import utils - -LOG = logging.getLogger(__name__) -CONF = cfg.CONF - - -class Watcher(health.HealthHandler): - """Observes K8s resources' events using K8s '?watch=true' API. - - The `Watcher` maintains a list of K8s resources and manages the event - processing loops for those resources. Event handling is delegated to the - `callable` object passed as the `handler` initialization parameter that - will be run for each K8s event observed by the `Watcher`. - - The `Watcher` can operate in two different modes based on the - `thread_group` initialization parameter: - - - synchronous, when the event processing loops run on the same thread - that called 'add' or 'start' methods - - - asynchronous, when each event processing loop runs on its own thread - (`oslo_service.threadgroup.Thread`) from the `thread_group` - - When started, the `Watcher` will run the event processing loops for each - of the K8s resources on the list. Adding a K8s resource to the running - `Watcher` also ensures that the event processing loop for that resource is - running. - - Stopping the `Watcher` or removing the specific K8s resource from the - list will request the corresponding running event processing loops to - stop gracefully, but will not interrupt any running `handler`. Forcibly - stopping any 'stuck' `handler` is not supported by the `Watcher` and - should be handled externally (e.g. by using `thread_group.stop( - graceful=False)` for asynchronous `Watcher`). - """ - - def __init__(self, handler, thread_group=None, timeout=None): - """Initializes a new Watcher instance. - - :param handler: a `callable` object to be invoked for each observed - K8s event with the event body as a single argument. - Calling `handler` should never raise any exceptions - other than `eventlet.greenlet.GreenletExit` caused by - `eventlet.greenthread.GreenThread.kill` when the - `Watcher` is operating in asynchronous mode. - :param thread_group: an `oslo_service.threadgroup.ThreadGroup` - object used to run the event processing loops - asynchronously. If `thread_group` is not - specified, the `Watcher` will operate in a - synchronous mode. - """ - super(Watcher, self).__init__() - self._client = clients.get_kubernetes_client() - self._handler = handler - self._thread_group = thread_group - self._running = False - self._resources = set() - self._watching = {} - self._timers = {} - self._idle = {} - - if timeout is None: - timeout = CONF.kubernetes.watch_retry_timeout - self._timeout = timeout - - def add(self, path): - """Adds ths K8s resource to the Watcher. - - Adding a resource to a running `Watcher` also ensures that the event - processing loop for that resource is running. This method could block - for `Watcher`s operating in synchronous mode. - - :param path: K8s resource URL path - """ - self._resources.add(path) - if self._running and path not in self._watching: - self._start_watch(path) - - def remove(self, path): - """Removes the K8s resource from the Watcher. - - Also requests the corresponding event processing loop to stop if it - is running. - - :param path: K8s resource URL path - """ - self._resources.discard(path) - if path in self._watching: - self._stop_watch(path) - - def is_running(self): - return self._running - - def start(self): - """Starts the Watcher. - - Also ensures that the event processing loops are running. This method - could block for `Watcher`s operating in synchronous mode. - """ - self._running = True - for path in self._resources - set(self._watching): - self._start_watch(path) - - def stop(self): - """Stops the Watcher. - - Also requests all running event processing loops to stop. - """ - self._running = False - for path in list(self._watching): - self._stop_watch(path) - - def _reconcile(self, path): - LOG.debug(f'Getting {path} for reconciliation.') - try: - response = self._client.get(path) - resources = response['items'] - except exceptions.K8sClientException: - LOG.exception(f'Error getting path when reconciling.') - return - - # NOTE(gryf): For some resources (like pods) we could observe that - # 'items' is set to None. I'm not sure if that's a K8s issue, since - # accroding to the documentation is should be list. - if not resources: - return - - for resource in resources: - event = { - 'type': 'MODIFIED', - 'object': resource, - } - self._handler(event, injected=True) - - def _start_watch(self, path): - tg = self._thread_group - self._idle[path] = True - if tg: - self._watching[path] = tg.add_thread(self._watch, path) - period = CONF.kubernetes.watch_reconcile_period - if period > 0: - # Let's make sure handlers won't reconcile at the same time. - initial_delay = period + 5 * len(self._timers) - self._timers[path] = tg.add_timer_args( - period, self._reconcile, args=(path,), - initial_delay=initial_delay, stop_on_exception=False) - else: - self._watching[path] = None - self._watch(path) - - def _stop_watch(self, path): - if self._idle.get(path): - if self._thread_group and path in self._watching: - if CONF.kubernetes.watch_reconcile_period: - self._timers[path].stop() - self._watching[path].stop() - # NOTE(dulek): Thread gets killed immediately, so we need to - # take care of this ourselves. - if CONF.kubernetes.watch_reconcile_period: - self._timers.pop(path, None) - self._watching.pop(path, None) - self._idle.pop(path, None) - - def _graceful_watch_exit(self, path): - try: - self._watching.pop(path, None) - if CONF.kubernetes.watch_reconcile_period: - self._timers.pop(path, None) - self._idle.pop(path, None) - LOG.info("Stopped watching '%s'", path) - except KeyError: - LOG.error("Failed to exit watch gracefully") - finally: - if not self._watching and not self._idle: - self.stop() - LOG.info("No remaining active watchers, Exiting...") - sys.exit(1) - - def _watch(self, path): - attempts = 0 - deadline = 0 - while self._running and path in self._resources: - try: - retry = False - if attempts == 1: - deadline = time.time() + self._timeout - - if (attempts > 0 and - utils.exponential_sleep(deadline, attempts) == 0): - LOG.error("Failed watching '%s': deadline exceeded", path) - self._alive = False - return - - LOG.info("Started watching '%s'", path) - for event in self._client.watch(path): - # NOTE(esevan): Watcher retries watching for - # `self._timeout` duration with exponential backoff - # algorithm to tolerate against temporal exception such as - # temporal disconnection to the k8s api server. - attempts = 0 - self._idle[path] = False - self._handler(event) - self._idle[path] = True - if not (self._running and path in self._resources): - return - except Exception: - LOG.exception("Caught exception while watching.") - LOG.warning("Restarting(%s) watching '%s'.", - attempts, path) - attempts += 1 - retry = True - self._idle[path] = True - finally: - if not retry: - self._graceful_watch_exit(path) diff --git a/playbooks/copy-crio-logs.yaml b/playbooks/copy-crio-logs.yaml deleted file mode 100644 index 048c54613..000000000 --- a/playbooks/copy-crio-logs.yaml +++ /dev/null @@ -1,14 +0,0 @@ -- hosts: all - tasks: - - set_fact: - devstack_base_dir: /opt/stack - when: devstack_base_dir is not defined - - - name: Copy CRI-O logs - shell: - cmd: "{{ devstack_base_dir }}/kuryr-kubernetes/tools/gate/copy_crio_logs.sh" - executable: /bin/bash - chdir: "{{ zuul.project.src_dir }}" - environment: - DEVSTACK_BASE_DIR: "{{ devstack_base_dir }}" - become: true diff --git a/playbooks/copy-k8s-logs.yaml b/playbooks/copy-k8s-logs.yaml deleted file mode 100644 index 866566c34..000000000 --- a/playbooks/copy-k8s-logs.yaml +++ /dev/null @@ -1,14 +0,0 @@ -- hosts: all - tasks: - - set_fact: - devstack_base_dir: /opt/stack - when: devstack_base_dir is not defined - - - name: Copy Kubernetes resources and pods logs - shell: - cmd: "{{ devstack_base_dir }}/kuryr-kubernetes/tools/gate/copy_k8s_logs.sh" - executable: /bin/bash - chdir: "{{ zuul.project.src_dir }}" - environment: - DEVSTACK_BASE_DIR: "{{ devstack_base_dir }}" - become: true diff --git a/playbooks/e2e-tests.patch b/playbooks/e2e-tests.patch deleted file mode 100644 index 483a239f4..000000000 --- a/playbooks/e2e-tests.patch +++ /dev/null @@ -1,58 +0,0 @@ -diff --git a/test/e2e/framework/pod/wait.go b/test/e2e/framework/pod/wait.go -index 61ab7997ce6..eabf38006ad 100644 ---- a/test/e2e/framework/pod/wait.go -+++ b/test/e2e/framework/pod/wait.go -@@ -51,7 +51,7 @@ const ( - podScheduledBeforeTimeout = podListTimeout + (20 * time.Second) - - // podStartTimeout is how long to wait for the pod to be started. -- podStartTimeout = 5 * time.Minute -+ podStartTimeout = 2 * time.Minute - - // poll is how often to poll pods, nodes and claims. - poll = 2 * time.Second -diff --git a/test/e2e/network/netpol/network_legacy.go b/test/e2e/network/netpol/network_legacy.go -index fb52460560c..895f4c3df85 100644 ---- a/test/e2e/network/netpol/network_legacy.go -+++ b/test/e2e/network/netpol/network_legacy.go -@@ -435,6 +435,7 @@ var _ = common.SIGDescribe("NetworkPolicyLegacy [LinuxOnly]", func() { - - policy, err = f.ClientSet.NetworkingV1().NetworkPolicies(nsA.Name).Create(context.TODO(), policy, metav1.CreateOptions{}) - framework.ExpectNoError(err, "Error creating Network Policy %v: %v", policy.ObjectMeta.Name, err) -+ time.Sleep(60 * time.Second) - defer cleanupNetworkPolicy(f, policy) - - testCannotConnect(f, nsB, "client-a", service, 80) -@@ -957,6 +958,7 @@ var _ = common.SIGDescribe("NetworkPolicyLegacy [LinuxOnly]", func() { - // Client cannot connect to service after updating the server pod's labels to match the network policy's selector. - ginkgo.By(fmt.Sprintf("Updating server pod %s to be selected by network policy %s.", podServer.Name, policy.Name)) - updatePodLabel(f, f.Namespace, podServer.Name, "add", "/metadata/labels/isolated", nil) -+ time.Sleep(60 * time.Second) - testCannotConnect(f, f.Namespace, "client-a", service, allowedPort) - }) - -@@ -1103,6 +1105,7 @@ var _ = common.SIGDescribe("NetworkPolicyLegacy [LinuxOnly]", func() { - } - - policyAllowToServerInNSB, err = f.ClientSet.NetworkingV1().NetworkPolicies(f.Namespace.Name).Create(context.TODO(), policyAllowToServerInNSB, metav1.CreateOptions{}) -+ time.Sleep(60 * time.Second) - framework.ExpectNoError(err, "Error occurred while creating policy: policyAllowToServerInNSB.") - defer cleanupNetworkPolicy(f, policyAllowToServerInNSB) - -@@ -1807,6 +1810,7 @@ var _ = common.SIGDescribe("NetworkPolicy [Feature:SCTPConnectivity][LinuxOnly][ - - ginkgo.By("Testing pods can connect only to the port allowed by the policy.") - testCannotConnectProtocol(f, f.Namespace, "client-a", service, 80, v1.ProtocolSCTP) -+ time.Sleep(60 * time.Second) - testCanConnectProtocol(f, f.Namespace, "client-b", service, 81, v1.ProtocolSCTP) - }) - -@@ -2143,7 +2147,7 @@ func createNetworkClientPodWithRestartPolicy(f *framework.Framework, namespace * - Command: []string{"/bin/sh"}, - Args: []string{ - "-c", -- fmt.Sprintf("for i in $(seq 1 5); do /agnhost connect %s --protocol %s --timeout 8s && exit 0 || sleep 1; done; exit 1", net.JoinHostPort(targetService.Spec.ClusterIP, strconv.Itoa(targetPort)), connectProtocol), -+ fmt.Sprintf("sleep 30; for i in $(seq 1 300); do /agnhost connect %s --protocol %s --timeout 8s && exit 0 || sleep 1; done; exit 1", net.JoinHostPort(targetService.Spec.ClusterIP, strconv.Itoa(targetPort)), connectProtocol), - }, - }, - }, diff --git a/playbooks/get_amphora_tarball.yaml b/playbooks/get_amphora_tarball.yaml deleted file mode 100644 index e400adf9d..000000000 --- a/playbooks/get_amphora_tarball.yaml +++ /dev/null @@ -1,6 +0,0 @@ -- hosts: controller - tasks: - - name: Download amphora tarball - get_url: - url: "https://tarballs.openstack.org/octavia/test-images/test-only-amphora-x64-haproxy-ubuntu-focal.qcow2" - dest: /tmp/test-only-amphora-x64-haproxy-ubuntu-focal.qcow2 diff --git a/playbooks/run_k8s_e2e_tests.yaml b/playbooks/run_k8s_e2e_tests.yaml deleted file mode 100644 index 3cd450894..000000000 --- a/playbooks/run_k8s_e2e_tests.yaml +++ /dev/null @@ -1,106 +0,0 @@ -- hosts: all - tasks: - # NOTE(maysams): Revisit this package removal step - # once other operating systems are supported on the gates - - name: Remove old installation of Go - shell: | - apt remove -y --purge golang - apt autoremove -y - become: yes - ignore_errors: yes - - - name: Download GO {{ gopkg }} - get_url: - url: https://dl.google.com/go/{{ gopkg }} - dest: /tmp/{{ gopkg }} - force: yes - - - name: Unarchive GO - unarchive: - src: /tmp/{{ gopkg }} - dest: /usr/local - remote_src: yes - become: true - - - name: Clone K8s test-infra repository - git: - repo: https://github.com/kubernetes/test-infra - dest: ~/test-infra - force: yes - - - name: Install kubetest - shell: go install ./kubetest - args: - chdir: ~/test-infra - environment: - GO111MODULE: "on" - PATH: "{{ ansible_env.PATH }}:/usr/local/go/bin:{{ ansible_env.HOME }}/go/bin" - - - name: Clone kubernetes repository - git: - repo: https://github.com/kubernetes/kubernetes.git - version: "{{ kubetest_version }}" - dest: ~/kubernetes - force: yes - - - name: Patch e2e tests - # TODO(gryf): for some reason 'patch' plugin doesn't work - block: - - name: patch the kubernetes tests - shell: patch -Np1 < /opt/stack/kuryr-kubernetes/playbooks/e2e-tests.patch - args: - chdir: ~/kubernetes - - - name: Build e2e tests - block: - - name: Install make package - become: true - package: - name: "make" - state: present - - name: Build e2e tests - shell: | - make WHAT=cmd/kubectl - make WHAT=vendor/github.com/onsi/ginkgo/ginkgo - make WHAT=test/e2e/e2e.test - args: - chdir: ~/kubernetes - environment: - PATH: "{{ ansible_env.PATH }}:/usr/local/go/bin:{{ ansible_env.HOME }}/go/bin" - - - name: Create .kube folder within BASE - file: - path: "{{ ansible_env.HOME }}/.kube" - state: directory - become: yes - - - name: Copy kubeconfig file - shell: "cp /etc/kubernetes/admin.conf {{ ansible_env.HOME }}/.kube/config" - become: yes - - - name: Change kubeconfig file permission - file: - path: "{{ ansible_env.HOME }}/.kube/config" - owner: zuul - group: zuul - become: yes - - - name: Run Network Policy legacy tests - block: - - name: Run Network Policy tests without SCTPConnectivity - shell: kubetest --provider=local --check-version-skew=false --test --ginkgo-parallel={{ np_parallel_number }} --test_args="--ginkgo.focus=NetworkPolicyLegacy --ginkgo.skip=\[Feature:SCTPConnectivity|should.enforce.policies.to.check.ingress.and.egress.policies.can.be.controlled.independently.based.on.PodSelector" --dump=/tmp > ~/np_kubetest.log - args: - chdir: ~/kubernetes - environment: - GINKGO_NO_COLOR: "y" - KUBECONFIG: "{{ ansible_env.HOME }}/.kube/config" - PATH: "{{ ansible_env.PATH }}:/usr/local/go/bin:{{ ansible_env.HOME }}/go/bin" - always: - - name: Run Network Policy SCTPConnectivity tests - shell: kubetest --provider=local --check-version-skew=false --test --ginkgo-parallel={{ np_parallel_number }} --test_args="--ginkgo.focus=NetworkPolicy.\[Feature:SCTPConnectivity" --dump=/tmp > ~/np_sctp_kubetest.log - args: - chdir: ~/kubernetes - environment: - GINKGO_NO_COLOR: "y" - KUBECONFIG: "{{ ansible_env.HOME }}/.kube/config" - PATH: "{{ ansible_env.PATH }}:/usr/local/go/bin:{{ ansible_env.HOME }}/go/bin" diff --git a/releasenotes/notes/active-passive-ha-cfbda8e6b527b48e.yaml b/releasenotes/notes/active-passive-ha-cfbda8e6b527b48e.yaml deleted file mode 100644 index 266769496..000000000 --- a/releasenotes/notes/active-passive-ha-cfbda8e6b527b48e.yaml +++ /dev/null @@ -1,9 +0,0 @@ ---- -features: - - | - Kuryr-Kubernetes now supports running kuryr-controller service in - **Active/Passive HA mode**. This is only possible when running those services - as Pods on Kubernetes cluster, as Kubernetes is used for leader election. - Also it is required to add leader-elector container to the kuryr-controller - Pods. HA is controlled by ``[kubernetes]controller_ha`` option, which - defaults to ``False``. diff --git a/releasenotes/notes/add-tagging-ce56231f58bf7ad0.yaml b/releasenotes/notes/add-tagging-ce56231f58bf7ad0.yaml deleted file mode 100644 index e02e538c5..000000000 --- a/releasenotes/notes/add-tagging-ce56231f58bf7ad0.yaml +++ /dev/null @@ -1,11 +0,0 @@ ---- -features: - - | - Added possibility to ensure all OpenStack resources created by Kuryr are - tagged. In case of Neutron regular ``tags`` field is used. If Octavia - supports tagging (from Octavia API 2.5, i.e. Stein), ``tags`` field is used - as well, otherwise tags are put on ``description`` field. All this is - controlled by ``[neutron_defaults]resource_tags`` config option that can - hold a list of tags to be put on resources. This feature is useful to - correctly identify any leftovers in OpenStack after K8s cluster Kuryr was - serving gets deleted. diff --git a/releasenotes/notes/bp-openshift-router-support-5f28108b39a2826f.yaml b/releasenotes/notes/bp-openshift-router-support-5f28108b39a2826f.yaml deleted file mode 100644 index 651273b98..000000000 --- a/releasenotes/notes/bp-openshift-router-support-5f28108b39a2826f.yaml +++ /dev/null @@ -1,15 +0,0 @@ ---- -features: - - | - An OpenShift route is a way to expose a service by giving it an - externally-reachable hostname like www.example.com. - A defined route and the endpoints identified by its service can be - consumed by a router to provide named connectivity that allows external - clients to reach your applications. - Each route consists of a route name , target service details. - To enable it the following handlers should be added : - - .. code-block:: ini - - [kubernetes] - enabled_handlers=vif,lb,lbaasspec,ingresslb,ocproute diff --git a/releasenotes/notes/change-cni-daemon-default-port-e968a83fa1bf30b5.yaml b/releasenotes/notes/change-cni-daemon-default-port-e968a83fa1bf30b5.yaml deleted file mode 100644 index 7dff0e397..000000000 --- a/releasenotes/notes/change-cni-daemon-default-port-e968a83fa1bf30b5.yaml +++ /dev/null @@ -1,8 +0,0 @@ ---- -upgrade: - - | - kuryr-daemon used to listen on port 50036, but that's a port from local - range (on Ubuntu and RHEL default range is 32768-60999). This means that - there might have been a port conflict ("address already in use"). To avoid - that the default value of ``[cni_daemon]bind_address`` option was changed - to ``127.0.0.1:5036``. diff --git a/releasenotes/notes/changing-default-url-for-k8s-api-42c3b90183783291.yaml b/releasenotes/notes/changing-default-url-for-k8s-api-42c3b90183783291.yaml deleted file mode 100644 index a546f503f..000000000 --- a/releasenotes/notes/changing-default-url-for-k8s-api-42c3b90183783291.yaml +++ /dev/null @@ -1,16 +0,0 @@ ---- -upgrade: - - | - Option 'api_root' from kubernetes section changed default value from: - - .. code-block:: ini - - [kubernetes] - api_root=http://localhost:8080 - - to: - - .. code-block:: ini - - [kubernetes] - api_root=https://localhost:6443 diff --git a/releasenotes/notes/cni-health-checks-d2b70f2f2551a9fc.yaml b/releasenotes/notes/cni-health-checks-d2b70f2f2551a9fc.yaml deleted file mode 100644 index 75ee286d0..000000000 --- a/releasenotes/notes/cni-health-checks-d2b70f2f2551a9fc.yaml +++ /dev/null @@ -1,16 +0,0 @@ ---- -features: - - | - The CNI daemon now provides health checks allowing the deployer or the - orchestration layer to probe it for readiness and liveness. - - These health checks are served and executed by a Manager that runs - as part of CNI daemon, and offers two endpoints indicating whether - it is ready and alive. - - The Manager validates presence of NET_ADMIN capabilities, health status - of a transactional database, connectivity with Kubernetes API, quantity of - CNI add failures, health of CNI components and amount of memory - being consumed. The health checks fails if any of the presented checks - are not validated, causing the orchestration layer to restart. - More information can be found in the kuryr-kubernetes documentation. diff --git a/releasenotes/notes/containerization-2fba4dac5c097b19.yaml b/releasenotes/notes/containerization-2fba4dac5c097b19.yaml deleted file mode 100644 index 4d491edcf..000000000 --- a/releasenotes/notes/containerization-2fba4dac5c097b19.yaml +++ /dev/null @@ -1,6 +0,0 @@ ---- -features: - - | - Kuryr can now be run in containers on top of K8s cluster it is providing - networking for. A tool to generate K8s resource definitions is provided. - More information can be found in the kuryr-kubernetes documentation. diff --git a/releasenotes/notes/cri-o-support-ab7e810775754ea7.yaml b/releasenotes/notes/cri-o-support-ab7e810775754ea7.yaml deleted file mode 100644 index d2c8e4830..000000000 --- a/releasenotes/notes/cri-o-support-ab7e810775754ea7.yaml +++ /dev/null @@ -1,5 +0,0 @@ ---- -features: - - | - Added support for using cri-o (and podman & buildah) as container engine in - both container images and DevStack. diff --git a/releasenotes/notes/deprecate-handlers-caching-9cdfd772aba9a7ce.yaml b/releasenotes/notes/deprecate-handlers-caching-9cdfd772aba9a7ce.yaml deleted file mode 100644 index 19380647b..000000000 --- a/releasenotes/notes/deprecate-handlers-caching-9cdfd772aba9a7ce.yaml +++ /dev/null @@ -1,9 +0,0 @@ ---- -deprecations: - - | - Configuration sections ``[namespace_handler_caching]``, ``[np_handler_caching]`` - and ``[vif_handler_caching]`` have been deprecated due to simplifying quota usage - calculation for readiness checks. Instead of counting Neutron objects - (ports, sg, subnets, and networks), the quota_details extension is used, - which includes used, limit and reserved counts per resource. - In this way, caching becomes unnecessary. diff --git a/releasenotes/notes/deprecate-non-daemonized-6dd2154238b1628c.yaml b/releasenotes/notes/deprecate-non-daemonized-6dd2154238b1628c.yaml deleted file mode 100644 index 1888863bd..000000000 --- a/releasenotes/notes/deprecate-non-daemonized-6dd2154238b1628c.yaml +++ /dev/null @@ -1,18 +0,0 @@ ---- -upgrade: - - | - Legacy Kuryr deployment without running kuryr-daemon is now considered - deprecated. That possibility will be completely removed in one of the next - releases. Please note that this means that ``[cni_daemon]daemon_enabled`` - option will default to ``True``. -deprecations: - - | - Running Kuryr-Kubernetes without kuryr-daemon service is now deprecated. - Motivations for that move include: - - * Discoveries of bugs that are much easier to fix in kuryr-daemon. - * Further improvements in Kuryr scalability (e.g. moving choosing VIF from - pool into kuryr-daemon) are only possible when kuryr-daemon is present. - - Possibility of running Kuryr-Kubernetes without kuryr-daemon will be - removed in one of the future releases. diff --git a/releasenotes/notes/deprecate-sg-mode-option-96824c33335cd74b.yaml b/releasenotes/notes/deprecate-sg-mode-option-96824c33335cd74b.yaml deleted file mode 100644 index 7648a14f7..000000000 --- a/releasenotes/notes/deprecate-sg-mode-option-96824c33335cd74b.yaml +++ /dev/null @@ -1,10 +0,0 @@ ---- -deprecations: - - | - Setting the ``sg_mode`` option for octavia is being deprecated. - Main reason is that when ``sg_mode`` is create a new load balancer - security group is created. However, when ovn-octavia provider is - used that security group is not enforced, and thus there is no - need to have been created. - To address the other operation handled on this config, the - ``enforce_sg_rules`` config can be used instead. diff --git a/releasenotes/notes/deprecate-worker-nodes-subnet-e452c84df5b5ed5c.yaml b/releasenotes/notes/deprecate-worker-nodes-subnet-e452c84df5b5ed5c.yaml deleted file mode 100644 index a4cfcca0e..000000000 --- a/releasenotes/notes/deprecate-worker-nodes-subnet-e452c84df5b5ed5c.yaml +++ /dev/null @@ -1,12 +0,0 @@ ---- -features: - - | - Kuryr will now support nested mode with nodes VMs running in multiple - subnets. In order to use that functionality a new option - `[pod_vif_nested]worker_nodes_subnets` is introduced and will accept a list - of subnet IDs. -deprecations: - - | - Option `[pod_vif_nested]worker_nodes_subnet` is deprecated in favor of - `[pod_vif_nested]worker_nodes_subnets` that accepts a list instead of a - single ID. diff --git a/releasenotes/notes/deprecate_lbaasv2-a524aedf5d3a36bc.yaml b/releasenotes/notes/deprecate_lbaasv2-a524aedf5d3a36bc.yaml deleted file mode 100644 index a4d5d0ddd..000000000 --- a/releasenotes/notes/deprecate_lbaasv2-a524aedf5d3a36bc.yaml +++ /dev/null @@ -1,15 +0,0 @@ ---- -upgrade: - - | - Legacy Kuryr deployment relying on neutron-lbaas as the LBaaSv2 endpoint is - now deprecated. The possibility of using it as Kuryr's lbaasv2 endpoint - will be totally removed in one of the next releases. -deprecations: - - | - Running Kuryr-Kubernetes with neutron-lbaasv2 is now deprecated. The main - motivation for this is the deprecation of the neutron-lbaas implementation - in favour to Octavia. - - Possibility of running Kuryr-Kubernetes with the lbaas handler pointing to - anything but Octavia or SDN lbaas implementations will be removed in - future releases. diff --git a/releasenotes/notes/drop-ingress-d78a7a9be8f20da1.yaml b/releasenotes/notes/drop-ingress-d78a7a9be8f20da1.yaml deleted file mode 100644 index 7c4bc5f36..000000000 --- a/releasenotes/notes/drop-ingress-d78a7a9be8f20da1.yaml +++ /dev/null @@ -1,9 +0,0 @@ ---- -deprecations: - - | - Support for OpenShift's Routes (Ingress) gets removed as is not mantained - nor tested, and openshift route pods can be used instead. - - | - Support for namespace isolation is now deprecated and will be removed on - the first occasion as the same effect can now be achieved using Network - Policies support. diff --git a/releasenotes/notes/drop-py27-60f55b6bc1d082bc.yaml b/releasenotes/notes/drop-py27-60f55b6bc1d082bc.yaml deleted file mode 100644 index dc805e2b5..000000000 --- a/releasenotes/notes/drop-py27-60f55b6bc1d082bc.yaml +++ /dev/null @@ -1,6 +0,0 @@ ---- -upgrade: - - | - Python 2.7 support has been dropped. Last release of Kuryr-Kubernetes to support - py2.7 is OpenStack Train. The minimum version of Python now supported by - Kuryr-Kubernetes is Python 3.6. diff --git a/releasenotes/notes/fault-tolerable-watcher-24c51dbccabf5f17.yaml b/releasenotes/notes/fault-tolerable-watcher-24c51dbccabf5f17.yaml deleted file mode 100644 index b349793d7..000000000 --- a/releasenotes/notes/fault-tolerable-watcher-24c51dbccabf5f17.yaml +++ /dev/null @@ -1,21 +0,0 @@ ---- -upgrade: - - | - For the kuryr kubernetes watcher, - a new option 'watch_retry_timeout' has been added. - The following should be modified at kuryr.conf:: - - - [kubernetes] - # 'watch_retry_timeout' field is optional, - # default = 60 if not set. - watch_retry_timeout =

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