├── scripts ├── opencontrail-kubelet │ ├── requirements.txt │ ├── opencontrail_kubelet │ │ ├── __init__.py │ │ ├── tests │ │ │ └── __init__.py │ │ ├── shell.py │ │ ├── vrouter_api.py │ │ ├── lxc_manager.py │ │ └── plugin.py │ ├── test-requirements.txt │ ├── setup.cfg │ ├── .gitignore │ ├── MANIFEST.in │ ├── README.md │ ├── LICENSE.txt │ └── setup.py ├── go-k8s-builder │ └── Dockerfile ├── kube-vrouter-restart.sh └── opencontrail-install │ ├── util.rb │ ├── ifup-vhost │ ├── ubuntu1404 │ └── install.rb │ ├── fedora21 │ └── install.rb │ └── fedora20 │ └── install.rb ├── .gitignore ├── cluster ├── update_manifests_hash.sh ├── redis.manifest ├── ifmap-server.manifest ├── rabbitmq.manifest ├── contrail-schema.manifest ├── contrail-api.manifest ├── cassandra.manifest ├── zookeeper.manifest ├── contrail-web.manifest ├── manifests.hash ├── contrail-control.manifest ├── contrail-vrouter-agent.manifest ├── contrail-collector.manifest ├── contrail-analytics-api.manifest ├── contrail-query-engine.manifest ├── kube-network-manager.manifest.sls └── provision_master.sh ├── Jenkinsfile ├── pkg └── network │ ├── cni │ ├── test │ │ ├── contrail.conf │ │ ├── contrail-test.sh │ │ └── contrail-server.go │ ├── opencontrail.go │ ├── args │ │ └── args.go │ └── cni │ │ └── cni.go │ ├── opencontrail │ ├── cni │ │ ├── test │ │ │ ├── contrail.conf │ │ │ ├── contrail-test.sh │ │ │ └── contrail-server.go │ │ ├── opencontrail.go │ │ └── args │ │ │ └── args.go │ ├── mocks │ │ ├── AddressAllocator.go │ │ ├── Store.go │ │ ├── KubeServiceInterface.go │ │ ├── KubeNamespaceInterface.go │ │ ├── KubePodInterface.go │ │ ├── NetworkManager.go │ │ └── KubeClient.go │ ├── address_allocator_test.go │ ├── util_test.go │ ├── namespace.go │ ├── util.go │ ├── service_test.go │ ├── address_allocator.go │ ├── virtual_router.go │ ├── config_test.go │ ├── opencontrail.go │ ├── policy.go │ ├── network_test.go │ ├── controller_dispatch.go │ ├── config.go │ ├── instance.go │ ├── random_test.go │ └── service.go │ ├── factory.go │ ├── controller.go │ ├── config_test.go │ └── config.go ├── cmd └── kube-network-manager │ ├── app │ ├── providers.go │ └── server.go │ └── manager.go ├── Dockerfile ├── origin.docker ├── README.md └── .travis.yml /scripts/opencontrail-kubelet/requirements.txt: -------------------------------------------------------------------------------- 1 | requests 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | **/.DS_Store 3 | /kube-network-manager 4 | -------------------------------------------------------------------------------- /scripts/opencontrail-kubelet/opencontrail_kubelet/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /scripts/opencontrail-kubelet/opencontrail_kubelet/tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /scripts/opencontrail-kubelet/test-requirements.txt: -------------------------------------------------------------------------------- 1 | mock 2 | 3 | -------------------------------------------------------------------------------- /scripts/opencontrail-kubelet/setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | description-file = README.md -------------------------------------------------------------------------------- /scripts/opencontrail-kubelet/.gitignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | opencontrail_kubelet.egg-info/ 3 | -------------------------------------------------------------------------------- /scripts/opencontrail-kubelet/MANIFEST.in: -------------------------------------------------------------------------------- 1 | include requirements.txt 2 | include test-requirements.txt 3 | -------------------------------------------------------------------------------- /scripts/opencontrail-kubelet/README.md: -------------------------------------------------------------------------------- 1 | OpenContrail kubelet plugin. 2 | 3 | Supports kubelet setup, status and teardown commands. -------------------------------------------------------------------------------- /cluster/update_manifests_hash.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | rm -rf manifests.hash 4 | md5sum *.* | \grep -v $(basename $0) | awk '{print $2 " md5="$1}' > manifests.hash 5 | -------------------------------------------------------------------------------- /Jenkinsfile: -------------------------------------------------------------------------------- 1 | node { 2 | checkout scm 3 | docker.withRegistry('https://localhost:5000') { 4 | def k8s_manager = docker.build('opencontrail/kube-network-manager') 5 | k8s_manager.push() 6 | } 7 | } 8 | 9 | -------------------------------------------------------------------------------- /pkg/network/cni/test/contrail.conf: -------------------------------------------------------------------------------- 1 | { 2 | "cniVersion": "0.2.0", 3 | "contrail" : { 4 | "vrouter" : { 5 | "ip" : "127.0.0.1", 6 | "port" : 9090 7 | }, 8 | "dir" : "/var/lib/contrail/ports" 9 | }, 10 | 11 | "name": "contrail", 12 | "type": "contrail" 13 | } 14 | -------------------------------------------------------------------------------- /pkg/network/opencontrail/cni/test/contrail.conf: -------------------------------------------------------------------------------- 1 | { 2 | "cniVersion": "0.2.0", 3 | "contrail" : { 4 | "vrouter" : { 5 | "ip" : "127.0.0.1", 6 | "port" : 9090 7 | }, 8 | "dir" : "/var/lib/contrail/ports" 9 | }, 10 | 11 | "name": "contrail", 12 | "type": "contrail" 13 | } 14 | -------------------------------------------------------------------------------- /pkg/network/opencontrail/mocks/AddressAllocator.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | import "github.com/stretchr/testify/mock" 4 | 5 | type AddressAllocator struct { 6 | mock.Mock 7 | } 8 | 9 | func (m *AddressAllocator) LocateIpAddress(uid string) (string, error) { 10 | ret := m.Called(uid) 11 | 12 | r0 := ret.Get(0).(string) 13 | r1 := ret.Error(1) 14 | 15 | return r0, r1 16 | } 17 | func (m *AddressAllocator) ReleaseIpAddress(uid string) { 18 | m.Called(uid) 19 | } 20 | -------------------------------------------------------------------------------- /pkg/network/cni/test/contrail-test.sh: -------------------------------------------------------------------------------- 1 | #/bin/bash 2 | #ip netns add TestNS 3 | export GOPATH=$HOME/k8s/lib 4 | export CNI_COMMAND=$1 5 | export CNI_NETNS="/var/run/netns/TestNS" 6 | export CNI_IFNAME="eth0" 7 | export CNI_PATH="/test" 8 | export CNI_CONTAINERID="1234" 9 | export CNI_ARGS="IgnoreUnknown=1;K8S_POD_NAMESPACE=default;K8S_POD_NAME=hello-world-1-81nl8;K8S_POD_INFRA_CONTAINER_ID=4209d77cedfc320540b55d52a7d19b3c5f8cb99e29ed9842eeb8e426608b0f6a" 10 | $HOME/k8s/go/bin/go run contrail.go < contrail.conf 11 | -------------------------------------------------------------------------------- /pkg/network/opencontrail/cni/test/contrail-test.sh: -------------------------------------------------------------------------------- 1 | #/bin/bash 2 | #ip netns add TestNS 3 | export GOPATH=$HOME/k8s/lib 4 | export CNI_COMMAND=$1 5 | export CNI_NETNS="/var/run/netns/TestNS" 6 | export CNI_IFNAME="eth0" 7 | export CNI_PATH="/test" 8 | export CNI_CONTAINERID="1234" 9 | export CNI_ARGS="IgnoreUnknown=1;K8S_POD_NAMESPACE=default;K8S_POD_NAME=hello-world-1-81nl8;K8S_POD_INFRA_CONTAINER_ID=4209d77cedfc320540b55d52a7d19b3c5f8cb99e29ed9842eeb8e426608b0f6a" 10 | $HOME/k8s/go/bin/go run contrail.go < contrail.conf 11 | -------------------------------------------------------------------------------- /scripts/go-k8s-builder/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.5 2 | MAINTAINER Pedro Marques 3 | RUN mkdir -p src/github.com/Juniper 4 | RUN (cd src/github.com/Juniper && git clone https://github.com/Juniper/contrail-go-api -b 1.0.0) 5 | RUN wget https://github.com/Juniper/contrail-go-api/releases/download/1.0.0/contrail-go-api-generated-types-r2.20.tar.gz 6 | RUN (cd src && tar zxvf ../contrail-go-api-generated-types-r2.20.tar.gz) 7 | RUN mkdir -p src/k8s.io 8 | RUN (cd src/k8s.io && git clone https://github.com/kubernetes/kubernetes.git) 9 | -------------------------------------------------------------------------------- /pkg/network/cni/opencontrail.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "./cni" 5 | "github.com/containernetworking/cni/pkg/skel" 6 | "github.com/containernetworking/cni/pkg/version" 7 | ) 8 | 9 | /**************************************************************************** 10 | * Main 11 | ****************************************************************************/ 12 | func main() { 13 | // Let CNI skeletal code handle demux based on env variables 14 | skel.PluginMain(cni.CmdAdd, cni.CmdDel, 15 | version.PluginSupports(cni.CniVersion)) 16 | } 17 | -------------------------------------------------------------------------------- /pkg/network/opencontrail/cni/opencontrail.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "./cni" 5 | "github.com/containernetworking/cni/pkg/skel" 6 | "github.com/containernetworking/cni/pkg/version" 7 | ) 8 | 9 | /**************************************************************************** 10 | * Main 11 | ****************************************************************************/ 12 | func main() { 13 | // Let CNI skeletal code handle demux based on env variables 14 | skel.PluginMain(cni.CmdAdd, cni.CmdDel, 15 | version.PluginSupports(cni.CniVersion)) 16 | } 17 | -------------------------------------------------------------------------------- /cluster/redis.manifest: -------------------------------------------------------------------------------- 1 | { 2 | "apiVersion": "v1", 3 | "kind": "Pod", 4 | "metadata": {"name":"redis"}, 5 | "spec":{ 6 | "hostNetwork": true, 7 | "containers":[{ 8 | "name": "redis", 9 | "image": "redis", 10 | "ports": [{ 11 | "name": "redis", 12 | "containerPort": 6379, 13 | "hostPort": 6379 14 | }], 15 | "volumeMounts": [{ 16 | "name": "logs", 17 | "mountPath": "/var/log/redis" 18 | }] 19 | }], 20 | "volumes": [ 21 | { 22 | "name": "logs", 23 | "hostPath": {"path": "/var/log/redis"} 24 | } 25 | ] 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /scripts/opencontrail-kubelet/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright 2015 Juniper Networks, Inc. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /scripts/opencontrail-kubelet/opencontrail_kubelet/shell.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2015 Juniper Networks, Inc. 3 | # 4 | 5 | import subprocess 6 | import logging 7 | 8 | 9 | class Shell: 10 | # Run a shell command. Log the command run and its output. 11 | @staticmethod 12 | def run(cmd, ignore=False): 13 | logging.debug('sh: %s' % cmd) 14 | try: 15 | cmd = subprocess.check_output(cmd, shell=True) 16 | except: 17 | if not ignore: 18 | logging.error('command failed: %s' % cmd.rstrip()) 19 | raise 20 | logging.debug('output: %s' % cmd.rstrip()) 21 | return cmd 22 | -------------------------------------------------------------------------------- /cluster/ifmap-server.manifest: -------------------------------------------------------------------------------- 1 | { 2 | "apiVersion": "v1", 3 | "kind": "Pod", 4 | "metadata": {"name":"ifmap-server"}, 5 | "spec":{ 6 | "hostNetwork": true, 7 | "containers":[{ 8 | "name": "ifmap-server", 9 | "image": "opencontrail/ifmap-server:2.20", 10 | "ports": [{ 11 | "name": "ifmap", 12 | "containerPort": 8443, 13 | "hostPort": 8443 14 | }], 15 | "volumeMounts": [ 16 | { 17 | "name": "logs", 18 | "mountPath": "/var/log/contrail", 19 | "readOnly": false 20 | }] 21 | }], 22 | "volumes": [ 23 | { 24 | "name": "logs", 25 | "hostPath": {"path": "/var/log/ifmap-server"} 26 | } 27 | ] 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /cluster/rabbitmq.manifest: -------------------------------------------------------------------------------- 1 | { 2 | "apiVersion": "v1", 3 | "kind": "Pod", 4 | "metadata": {"name":"rabbitmq"}, 5 | "spec":{ 6 | "hostNetwork": true, 7 | "containers":[{ 8 | "name": "rabbitmq", 9 | "image": "rabbitmq:3.5.4", 10 | "ports": [{ 11 | "name": "rabbitmq", 12 | "containerPort": 5672, 13 | "hostPort": 5672 14 | }], 15 | "env": [{ 16 | "name": "RABBITMQ_NODENAME", 17 | "value": "localhost" 18 | }], 19 | "volumeMounts": [{ 20 | "name": "logs", 21 | "mountPath": "/var/log/rabbitmq" 22 | }] 23 | }], 24 | "volumes": [ 25 | { 26 | "name": "logs", 27 | "hostPath": {"path": "/var/log/rabbitmq"} 28 | } 29 | ] 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /scripts/kube-vrouter-restart.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Run the kubebet plugin script for all running containers in the system. 4 | # This can be run as a PostExec script for the vrouter-agent when running 5 | # on a kubernetes node. 6 | # 7 | KUBE_PLUGIN=/usr/libexec/kubernetes/kubelet-plugins/net/exec/opencontrail/opencontrail 8 | 9 | CONTAINERS=$(docker ps | grep -v "/pause" | awk '/[0-9a-z]{12} /{print $1;}') 10 | 11 | for i in $CONTAINERS; do 12 | NAME=$(docker inspect -f '{{.Name}}' $i) 13 | ID=$(docker inspect -f '{{.Id}}' $i) 14 | PODNAME=$(echo $NAME | awk '//{split($0, arr, "_"); print arr[3]}') 15 | NAMESPACE=$(echo $NAME | awk '//{split($0, arr, "_"); print arr[4]}') 16 | $KUBE_PLUGIN setup $NAMESPACE $PODNAME $ID 17 | done 18 | -------------------------------------------------------------------------------- /cmd/kube-network-manager/app/providers.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015 Juniper Networks, Inc. All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package app 18 | 19 | import ( 20 | _ "github.com/Juniper/contrail-kubernetes/pkg/network/opencontrail" 21 | ) 22 | -------------------------------------------------------------------------------- /cluster/contrail-schema.manifest: -------------------------------------------------------------------------------- 1 | { 2 | "apiVersion": "v1", 3 | "kind": "Pod", 4 | "metadata": {"name":"contrail-schema"}, 5 | "spec":{ 6 | "hostNetwork": true, 7 | "containers":[{ 8 | "name": "contrail-schema", 9 | "image": "opencontrail/config:2.20", 10 | "command": ["/usr/bin/contrail-schema"], 11 | "ports": [{ 12 | "name": "schema-ui", 13 | "containerPort": 8087, 14 | "hostPort": 8087 15 | }], 16 | "volumeMounts": [{ 17 | "name": "config", 18 | "mountPath": "/etc/contrail" 19 | }, { 20 | "name": "logs", 21 | "mountPath": "/var/log/contrail", 22 | "readOnly": false 23 | }] 24 | }], 25 | "volumes": [ 26 | { 27 | "name": "config", 28 | "hostPath": {"path": "/etc/contrail"} 29 | }, 30 | { 31 | "name": "logs", 32 | "hostPath": {"path": "/var/log/contrail"} 33 | } 34 | ] 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /cluster/contrail-api.manifest: -------------------------------------------------------------------------------- 1 | { 2 | "apiVersion": "v1", 3 | "kind": "Pod", 4 | "metadata": {"name":"contrail-api"}, 5 | "spec":{ 6 | "hostNetwork": true, 7 | "containers":[{ 8 | "name": "contrail-api", 9 | "image": "opencontrail/config:2.20", 10 | "command": ["/usr/bin/contrail-api"], 11 | "ports": [{ 12 | "name": "contrail-api", 13 | "containerPort": 8082, 14 | "hostPort": 8082 15 | }], 16 | "volumeMounts": [ 17 | { 18 | "name": "config", 19 | "mountPath": "/etc/contrail" 20 | }, 21 | { 22 | "name": "logs", 23 | "mountPath": "/var/log/contrail", 24 | "readOnly": false 25 | }] 26 | }], 27 | "volumes": [ 28 | { 29 | "name": "config", 30 | "hostPath": {"path": "/etc/contrail"} 31 | }, 32 | { 33 | "name": "logs", 34 | "hostPath": {"path": "/var/log/contrail"} 35 | } 36 | ] 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.5 2 | MAINTAINER Pedro Marques 3 | RUN mkdir -p src/github.com/Juniper 4 | RUN (cd src/github.com/Juniper && git clone https://github.com/Juniper/contrail-go-api) 5 | RUN wget https://github.com/Juniper/contrail-go-api/releases/download/1.0.0/contrail-go-api-generated-types-r2.20.tar.gz 6 | RUN (cd src && tar zxvf ../contrail-go-api-generated-types-r2.20.tar.gz) 7 | RUN mkdir -p src/k8s.io 8 | RUN (cd src/k8s.io && git clone https://github.com/kubernetes/kubernetes.git) 9 | RUN mkdir -p src/github.com/Juniper/contrail-kubernetes 10 | ADD cmd /go/src/github.com/Juniper/contrail-kubernetes/cmd 11 | ADD pkg /go/src/github.com/Juniper/contrail-kubernetes/pkg 12 | RUN GOPATH=$GOPATH:$GOPATH/src/k8s.io/kubernetes/Godeps/_workspace go build github.com/Juniper/contrail-kubernetes/cmd/kube-network-manager 13 | RUN rm -rf src/github.com 14 | ENTRYPOINT ["/go/kube-network-manager"] 15 | -------------------------------------------------------------------------------- /cluster/cassandra.manifest: -------------------------------------------------------------------------------- 1 | { 2 | "apiVersion": "v1", 3 | "kind": "Pod", 4 | "metadata": {"name":"cassandra"}, 5 | "spec":{ 6 | "containers":[{ 7 | "name": "opencontrail-config-db", 8 | "image": "cassandra:2.2.0", 9 | "command": [ 10 | "/bin/sh", 11 | "-c", 12 | "sed -ri 's/^(start_rpc:) .*/\\1 true/' /etc/cassandra/cassandra.yaml && /docker-entrypoint.sh cassandra -f" 13 | ], 14 | "ports": [{ 15 | "name": "cassandra", 16 | "containerPort": 9160, 17 | "hostPort": 9160 18 | }], 19 | "env": [{ 20 | "name": "CASSANDRA_CLUSTER_NAME", 21 | "value": "OpenContrail-config" 22 | }], 23 | "volumeMounts": [{ 24 | "name": "data", 25 | "mountPath": "/var/lib/cassandra", 26 | "readOnly": false 27 | }] 28 | }], 29 | "volumes": [ 30 | { 31 | "name": "data", 32 | "hostPath": {"path": "/var/lib/cassandra"} 33 | } 34 | ] 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /origin.docker: -------------------------------------------------------------------------------- 1 | FROM golang:1.5 2 | MAINTAINER Pedro Marques 3 | RUN mkdir -p src/github.com/Juniper 4 | RUN (cd src/github.com/Juniper && git clone https://github.com/Juniper/contrail-go-api -b 1.0.0) 5 | RUN wget https://github.com/Juniper/contrail-go-api/releases/download/1.0.0/contrail-go-api-generated-types-r2.20.tar.gz 6 | RUN (cd src && tar zxvf ../contrail-go-api-generated-types-r2.20.tar.gz) 7 | RUN mkdir -p src/github.com/openshift 8 | RUN (cd src/github.com/openshift && git clone https://github.com/openshift/origin) 9 | RUN mkdir -p src/github.com/Juniper/contrail-kubernetes 10 | ADD cmd /go/src/github.com/Juniper/contrail-kubernetes/cmd 11 | ADD pkg /go/src/github.com/Juniper/contrail-kubernetes/pkg 12 | RUN GOPATH=$GOPATH:$GOPATH/src/github.com/openshift/origin/Godeps/_workspace go build github.com/Juniper/contrail-kubernetes/cmd/kube-network-manager 13 | RUN rm -rf src/github.com 14 | ENTRYPOINT ["/go/kube-network-manager"] 15 | -------------------------------------------------------------------------------- /cluster/zookeeper.manifest: -------------------------------------------------------------------------------- 1 | { 2 | "apiVersion": "v1", 3 | "kind": "Pod", 4 | "metadata": {"name":"zookeeper"}, 5 | "spec":{ 6 | "hostNetwork": true, 7 | "containers":[{ 8 | "name": "zookeeper", 9 | "image": "mesoscloud/zookeeper:3.4.6", 10 | "ports": [{ 11 | "name": "zookeeper", 12 | "containerPort": 2181, 13 | "hostPort": 2181 14 | }], 15 | "env": [{ 16 | "name": "MYID", 17 | "value": "1" 18 | }, { 19 | "name": "SERVERS", 20 | "value": "localhost" 21 | }], 22 | "volumeMounts": [{ 23 | "name": "logs", 24 | "mountPath": "/var/log/zookeeper", 25 | "readOnly": false 26 | }, { 27 | "name": "data", 28 | "mountPath": "/var/lib/zookeeper", 29 | "readOnly": false 30 | }] 31 | }], 32 | "volumes": [{ 33 | "name": "logs", 34 | "hostPath": {"path": "/var/log/zookeeper"} 35 | }, { 36 | "name": "data", 37 | "hostPath": {"path": "/var/lib/zookeeper"} 38 | }] 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /cluster/contrail-web.manifest: -------------------------------------------------------------------------------- 1 | { 2 | "apiVersion": "v1", 3 | "kind": "Pod", 4 | "metadata": {"name":"contrail-web"}, 5 | "spec":{ 6 | "hostNetwork": true, 7 | "containers":[{ 8 | "name": "contrail-web", 9 | "image": "opencontrail/web:2.20", 10 | "command": ["/usr/bin/contrail-webui-start.sh"], 11 | "securityContext": { 12 | "capabilities": { 13 | "add": ["NET_BIND_SERVICE"] 14 | } 15 | }, 16 | "ports": [{ 17 | "name": "web-http", 18 | "containerPort": 8070, 19 | "hostPort": 8070 20 | }, { 21 | "name": "web-https", 22 | "containerPort": 8143, 23 | "hostPort": 8143 24 | }, { 25 | "name": "web-jobs", 26 | "containerPort": 3000, 27 | "hostPort": 3000 28 | }], 29 | "volumeMounts": [ 30 | { 31 | "name": "logs", 32 | "mountPath": "/var/log/contrail", 33 | "readOnly": false 34 | }] 35 | }], 36 | "volumes": [{ 37 | "name": "logs", 38 | "hostPath": {"path": "/var/log/contrail"} 39 | }] 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /cluster/manifests.hash: -------------------------------------------------------------------------------- 1 | cassandra.manifest md5=2193a46160758f71c933c46c880125b5 2 | contrail-analytics-api.manifest md5=f1f675987c6ab3f3f7c041afb23aace7 3 | contrail-api.manifest md5=52107b35fed5324552042b8891a00c82 4 | contrail-collector.manifest md5=acc492998af9926333242402e6f63079 5 | contrail-control.manifest md5=7f5c4f4d03f6dc33a6f518a89f1427cf 6 | contrail-query-engine.manifest md5=39b8e6f06480958a20e29a576401c224 7 | contrail-schema.manifest md5=6ba2ef0ab20d423314052f0db9799237 8 | contrail-vrouter-agent.manifest md5=75706835d2b6b3ba1be0228e9cbb30e7 9 | contrail-web.manifest md5=2c773343f4b4f421d7d4d9c06f0850ad 10 | ifmap-server.manifest md5=f27327ed74c803a53809aad5e26cbc45 11 | kube-network-manager.manifest.sls md5=64fae6a73b03039b297aa996e6dae728 12 | provision_master.sh md5=a98338945e62591d7de35aa7615ef840 13 | provision_minion.sh md5=7c74a9f038ca7bc27ae0324e8b3f74ef 14 | rabbitmq.manifest md5=fe26b2f66c2adfd94c85c1971be50267 15 | redis.manifest md5=d5489945201a847f8bfeabdfccdc68ce 16 | zookeeper.manifest md5=c665836b3d5fe7b535d2c91a5efbb824 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # contrail-kubernetes 2 | 3 | [![Join the chat at https://gitter.im/Juniper/contrail-kubernetes](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/Juniper/contrail-kubernetes?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 4 | [![Build Status](https://travis-ci.org/Juniper/contrail-kubernetes.svg?branch=master)](https://travis-ci.org/Juniper/contrail-kubernetes) 5 | [![Coverage Status](https://coveralls.io/repos/Juniper/contrail-kubernetes/badge.svg?branch=master&service=github)](https://coveralls.io/github/Juniper/contrail-kubernetes?branch=master) 6 | 7 | OpenContrail Kubernetes integration 8 | 9 | The daemon kube-network-manager uses the kubernetes controller framework to watch k8s api objects. It isolates pods in virtual-networks (according to the label['name']) and connects pods with services (according to the label['uses']). 10 | 11 | Build: 12 | ``` 13 | GOPATH=$GOPATH:${GOROOT}/src/k8s.io/kubernetes/Godeps/_workspace 14 | 15 | go build github.com/Juniper/contrail-kubernetes/cmd/kube-network-manager 16 | ``` 17 | -------------------------------------------------------------------------------- /scripts/opencontrail-kubelet/setup.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2015 Juniper Networks, Inc. 3 | # 4 | 5 | import setuptools 6 | 7 | 8 | def requirements(filename): 9 | with open(filename) as f: 10 | lines = f.read().splitlines() 11 | return lines 12 | 13 | setuptools.setup( 14 | name='opencontrail-kubelet', 15 | version='0.3.5', 16 | packages=setuptools.find_packages(), 17 | 18 | # metadata 19 | author="OpenContrail", 20 | author_email="dev@lists.opencontrail.org", 21 | license="Apache Software License", 22 | url="http://www.opencontrail.org/", 23 | description="OpenContrail kubelet plugin", 24 | long_description="Kubernetes kubelet plugin for OpenContrail", 25 | 26 | install_requires=requirements('requirements.txt'), 27 | 28 | test_suite='opencontrail_kubelet.tests', 29 | tests_require=requirements('test-requirements.txt'), 30 | 31 | entry_points = { 32 | 'console_scripts': [ 33 | 'opencontrail-kubelet-plugin = opencontrail_kubelet.plugin:main', 34 | ], 35 | }, 36 | ) 37 | -------------------------------------------------------------------------------- /cmd/kube-network-manager/manager.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015 Juniper Networks, Inc. All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package main 18 | 19 | import ( 20 | goflag "flag" 21 | 22 | flag "github.com/spf13/pflag" 23 | 24 | "github.com/Juniper/contrail-kubernetes/cmd/kube-network-manager/app" 25 | ) 26 | 27 | func main() { 28 | m := app.NewNetworkManager() 29 | flag.CommandLine.AddGoFlagSet(goflag.CommandLine) 30 | m.AddFlags(flag.CommandLine) 31 | flag.Parse() 32 | m.Run(flag.CommandLine.Args()) 33 | } 34 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | env: 4 | - secure: "ACCvHWBVKddlUM5+fpTPsIM14At90jTXkUdhqDWJ8HKQvdQA33nBz1s6fq684S+/d1o1pPyhOTyqC3JVmMeOI8Dd07i+5ujAd/e5P7Zd3Bw1N/ZMO+rJylx73t+aYPJRog7sz4BAUPJS2Kn1/VZY4q6iS4Vx90kmgLNeTgkwwHo=" 5 | 6 | install: 7 | - mkdir -p ${GOPATH}/src/github.com/Juniper 8 | - (cd ${GOPATH}/src/github.com/Juniper && git clone https://github.com/Juniper/contrail-go-api) 9 | - wget https://github.com/Juniper/contrail-go-api/releases/download/1.0.0/contrail-go-api-generated-types-r2.20.tar.gz 10 | - tar -zxvf contrail-go-api-generated-types-r2.20.tar.gz -C ${GOPATH}/src 11 | - mkdir -p ${GOPATH}/src/k8s.io 12 | - (cd ${GOPATH}/src/k8s.io && git clone https://github.com/kubernetes/kubernetes.git) 13 | - go get golang.org/x/tools/cmd/cover 14 | - go get github.com/axw/gocov/gocov 15 | - go get github.com/mattn/goveralls 16 | - GOPATH=${GOPATH}:${GOPATH}/src/k8s.io/kubernetes/Godeps/_workspace 17 | 18 | script: 19 | - gocov test -v ./... > coverage.json 20 | - ${HOME}/gopath/bin/goveralls -gocovdata=coverage.json -service=travis-ci -repotoken=${COVERALLS_TOKEN} 21 | -------------------------------------------------------------------------------- /cluster/contrail-control.manifest: -------------------------------------------------------------------------------- 1 | { 2 | "apiVersion": "v1", 3 | "kind": "Pod", 4 | "metadata": {"name":"contrail-control"}, 5 | "spec":{ 6 | "hostNetwork": true, 7 | "containers":[{ 8 | "name": "contrail-control", 9 | "image": "opencontrail/control:2.20", 10 | "command": ["/usr/bin/contrail-control"], 11 | "securityContext": { 12 | "capabilities": { 13 | "add": ["NET_BIND_SERVICE"] 14 | } 15 | }, 16 | "ports": [{ 17 | "name": "bgp", 18 | "containerPort": 179, 19 | "hostPort": 179 20 | }, { 21 | "name": "xmpp", 22 | "containerPort": 5269, 23 | "hostPort": 5269 24 | }, { 25 | "name": "control-ui", 26 | "containerPort": 8083, 27 | "hostPort": 8083 28 | }], 29 | "volumeMounts": [ 30 | { 31 | "name": "config", 32 | "mountPath": "/etc/contrail" 33 | }, 34 | { 35 | "name": "logs", 36 | "mountPath": "/var/log/contrail", 37 | "readOnly": false 38 | }] 39 | }], 40 | "volumes": [{ 41 | "name": "config", 42 | "hostPath": {"path": "/etc/contrail"} 43 | },{ 44 | "name": "logs", 45 | "hostPath": {"path": "/var/log/contrail"} 46 | }] 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /cluster/contrail-vrouter-agent.manifest: -------------------------------------------------------------------------------- 1 | { 2 | "apiVersion": "v1", 3 | "kind": "Pod", 4 | "metadata": {"name":"contrail-vrouter-agent"}, 5 | "spec":{ 6 | "hostNetwork": true, 7 | "containers":[{ 8 | "name": "contrail-vrouter-agent", 9 | "image": "opencontrail/vrouter-agent:2.20", 10 | "command": ["/usr/bin/contrail-vrouter-agent"], 11 | "securityContext": { 12 | "privileged": true 13 | }, 14 | "ports": [{ 15 | "name": "vrouter-api", 16 | "containerPort": 9090, 17 | "hostPort": 9090 18 | }, { 19 | "name": "ipc", 20 | "containerPort": 9091, 21 | "hostPort":9091 22 | }, { 23 | "name": "control-ui", 24 | "containerPort": 8085, 25 | "hostPort": 8085 26 | }], 27 | "volumeMounts": [ 28 | { 29 | "name": "config", 30 | "mountPath": "/etc/contrail" 31 | }, 32 | { 33 | "name": "logs", 34 | "mountPath": "/var/log/contrail", 35 | "readOnly": false 36 | } 37 | ] 38 | }], 39 | "volumes": [{ 40 | "name": "config", 41 | "hostPath": {"path": "/etc/contrail"} 42 | },{ 43 | "name": "logs", 44 | "hostPath": {"path": "/var/log/contrail"} 45 | } 46 | ] 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /cluster/contrail-collector.manifest: -------------------------------------------------------------------------------- 1 | { 2 | "apiVersion": "v1", 3 | "kind": "Pod", 4 | "metadata": {"name":"contrail-collector"}, 5 | "spec":{ 6 | "hostNetwork": true, 7 | "containers":[{ 8 | "name": "contrail-collector", 9 | "image": "opencontrail/analytics:2.20", 10 | "command": ["/usr/bin/contrail-collector"], 11 | "ports": [{ 12 | "name": "collector-ui", 13 | "containerPort": 8086, 14 | "hostPort": 8086 15 | }], 16 | "volumeMounts": [ 17 | { 18 | "name": "config", 19 | "mountPath": "/etc/contrail" 20 | }, 21 | { 22 | "name": "logs", 23 | "mountPath": "/var/log/contrail", 24 | "readOnly": false 25 | }] 26 | }], 27 | "volumes": [ 28 | { 29 | "name": "config", 30 | "hostPath": {"path": "/etc/contrail"} 31 | }, 32 | { 33 | "name": "logs", 34 | "hostPath": {"path": "/var/log/contrail"} 35 | } 36 | ] 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /cluster/contrail-analytics-api.manifest: -------------------------------------------------------------------------------- 1 | { 2 | "apiVersion": "v1", 3 | "kind": "Pod", 4 | "metadata": {"name":"contrail-analytics-api"}, 5 | "spec":{ 6 | "hostNetwork": true, 7 | "containers":[{ 8 | "name": "contrail-analytics-api", 9 | "image": "opencontrail/analytics:2.20", 10 | "command": ["/usr/bin/contrail-analytics-api"], 11 | "ports": [{ 12 | "name": "analytics-ui", 13 | "containerPort": 8081, 14 | "hostPort": 8081 15 | }], 16 | "volumeMounts": [ 17 | { 18 | "name": "config", 19 | "mountPath": "/etc/contrail" 20 | }, 21 | { 22 | "name": "logs", 23 | "mountPath": "/var/log/contrail", 24 | "readOnly": false 25 | }] 26 | }], 27 | "volumes": [ 28 | { 29 | "name": "config", 30 | "hostPath": {"path": "/etc/contrail"} 31 | }, 32 | { 33 | "name": "logs", 34 | "hostPath": {"path": "/var/log/contrail"} 35 | } 36 | ] 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /cluster/contrail-query-engine.manifest: -------------------------------------------------------------------------------- 1 | { 2 | "apiVersion": "v1", 3 | "kind": "Pod", 4 | "metadata": {"name":"contrail-query-engine"}, 5 | "spec":{ 6 | "hostNetwork": true, 7 | "containers":[{ 8 | "name": "contrail-query-engine", 9 | "image": "opencontrail/analytics:2.20", 10 | "command": ["/usr/bin/contrail-query-engine"], 11 | "ports": [{ 12 | "name": "query-engine-ui", 13 | "containerPort": 8091, 14 | "hostPort": 8091 15 | }], 16 | "volumeMounts": [ 17 | { 18 | "name": "config", 19 | "mountPath": "/etc/contrail" 20 | }, 21 | { 22 | "name": "logs", 23 | "mountPath": "/var/log/contrail", 24 | "readOnly": false 25 | }] 26 | }], 27 | "volumes": [ 28 | { 29 | "name": "config", 30 | "hostPath": {"path": "/etc/contrail"} 31 | }, 32 | { 33 | "name": "logs", 34 | "hostPath": {"path": "/var/log/contrail"} 35 | } 36 | ] 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /scripts/opencontrail-install/util.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'socket' 4 | require 'ipaddr' 5 | require 'pp' 6 | 7 | def sh(cmd, ignore_exit_code = false, retry_count = 1, delay = 1, bg = false) 8 | puts cmd 9 | if bg then 10 | # Run command in background 11 | Process.detach(spawn(cmd)) 12 | return 13 | end 14 | 15 | r = "" 16 | retry_count.times { |i| 17 | r = `#{cmd}`.chomp 18 | puts r 19 | break if $?.to_i == 0 20 | exit -1 if !ignore_exit_code and i == retry_count - 1 21 | sleep delay 22 | puts "#{i}/#{retry_count}: Retry: #{cmd}" if i != retry_count - 1 23 | } 24 | return r 25 | end 26 | 27 | def error(msg); puts msg; exit -1 end 28 | 29 | # Return interface IP address, mask and gateway information 30 | def get_intf_ip(intf) 31 | prefix = sh("ip addr show dev #{intf}|\grep -w inet | " + 32 | "\grep -v dynamic | awk '{print $2}'") 33 | error("Cannot retrieve #{intf}'s IP address") if prefix !~ /(.*)\/(\d+)$/ 34 | ip = $1; prefix_len = $2 35 | mask = IPAddr.new(prefix).inspect.split("/")[1].chomp.chomp(">") 36 | gw = sh(%{netstat -rn |\grep "^0.0.0.0" | awk '{print $2}'}) 37 | 38 | return ip, mask, gw, prefix_len 39 | end 40 | 41 | def sh_container(container_id, cmd, ignore = false) 42 | pid = sh(%{docker inspect -f {{.State.Pid}} #{container_id}}) 43 | sh(%{echo #{cmd} | nsenter -n -t #{pid} sh}) 44 | end 45 | -------------------------------------------------------------------------------- /pkg/network/factory.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015 Juniper Networks, Inc. All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package network 18 | 19 | import ( 20 | client "k8s.io/kubernetes/pkg/client/unversioned" 21 | ) 22 | 23 | type Allocator func(*client.Client, []string) NetworkController 24 | 25 | var ( 26 | allocatorMap map[string]Allocator 27 | ) 28 | 29 | func Register(networkImpl string, allocFn Allocator) { 30 | if allocatorMap == nil { 31 | allocatorMap = make(map[string]Allocator, 0) 32 | } 33 | allocatorMap[networkImpl] = allocFn 34 | } 35 | 36 | // Placeholder class that constructs a NetworkController 37 | type NetworkFactory struct { 38 | } 39 | 40 | func NewNetworkFactory() *NetworkFactory { 41 | factory := new(NetworkFactory) 42 | return factory 43 | } 44 | 45 | func (f *NetworkFactory) Create(client *client.Client, args []string) NetworkController { 46 | // TODO(prm): read configuration in order to select plugin. 47 | if alloc, ok := allocatorMap["opencontrail"]; ok { 48 | return alloc(client, args) 49 | } 50 | return nil 51 | } 52 | -------------------------------------------------------------------------------- /pkg/network/cni/test/contrail-server.go: -------------------------------------------------------------------------------- 1 | // Copyright 2010 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "encoding/json" 9 | "fmt" 10 | "io" 11 | "net/http" 12 | "strconv" 13 | ) 14 | 15 | type vmResp struct { 16 | Vm string `json:"vm"` 17 | Ip string `json:"ip-address"` 18 | Plen int `json:"plen"` 19 | Gw string `json:"gateway"` 20 | Dns string `json:"dns-server"` 21 | Mac string `json:"mac-address"` 22 | } 23 | 24 | type statusResp struct { 25 | Status string `json:"status"` 26 | } 27 | 28 | var addr int = 3 29 | 30 | func vmServer(w http.ResponseWriter, req *http.Request) { 31 | switch req.Method { 32 | case "GET": 33 | fmt.Println("GET request") 34 | ip := "1.1.1." + strconv.Itoa(addr) 35 | addr += 1 36 | resp := vmResp{Vm: "VM", Ip: ip, Plen: 24, Gw: "1.1.1.1", 37 | Dns: "1.1.1.2", Mac: "00:00:00:00:00:01"} 38 | msg, _ := json.Marshal(resp) 39 | io.WriteString(w, string(msg)) 40 | return 41 | 42 | case "POST": 43 | fmt.Println("POST request") 44 | resp := statusResp{Status: "OK"} 45 | msg, _ := json.Marshal(resp) 46 | io.WriteString(w, string(msg)) 47 | return 48 | 49 | case "DELETE": 50 | fmt.Println("DELETE request") 51 | resp := statusResp{Status: "OK"} 52 | msg, _ := json.Marshal(resp) 53 | io.WriteString(w, string(msg)) 54 | return 55 | default: 56 | fmt.Println("Unkown command") 57 | } 58 | } 59 | 60 | func main() { 61 | http.HandleFunc("/port/", vmServer) 62 | http.HandleFunc("/port", vmServer) 63 | http.ListenAndServe(":9060", nil) 64 | } 65 | -------------------------------------------------------------------------------- /pkg/network/opencontrail/cni/test/contrail-server.go: -------------------------------------------------------------------------------- 1 | // Copyright 2010 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "encoding/json" 9 | "fmt" 10 | "io" 11 | "net/http" 12 | "strconv" 13 | ) 14 | 15 | type vmResp struct { 16 | Vm string `json:"vm"` 17 | Ip string `json:"ip-address"` 18 | Plen int `json:"plen"` 19 | Gw string `json:"gateway"` 20 | Dns string `json:"dns-server"` 21 | Mac string `json:"mac-address"` 22 | } 23 | 24 | type statusResp struct { 25 | Status string `json:"status"` 26 | } 27 | 28 | var addr int = 3 29 | 30 | func vmServer(w http.ResponseWriter, req *http.Request) { 31 | switch req.Method { 32 | case "GET": 33 | fmt.Println("GET request") 34 | ip := "1.1.1." + strconv.Itoa(addr) 35 | addr += 1 36 | resp := vmResp{Vm: "VM", Ip: ip, Plen: 24, Gw: "1.1.1.1", 37 | Dns: "1.1.1.2", Mac: "00:00:00:00:00:01"} 38 | msg, _ := json.Marshal(resp) 39 | io.WriteString(w, string(msg)) 40 | return 41 | 42 | case "POST": 43 | fmt.Println("POST request") 44 | resp := statusResp{Status: "OK"} 45 | msg, _ := json.Marshal(resp) 46 | io.WriteString(w, string(msg)) 47 | return 48 | 49 | case "DELETE": 50 | fmt.Println("DELETE request") 51 | resp := statusResp{Status: "OK"} 52 | msg, _ := json.Marshal(resp) 53 | io.WriteString(w, string(msg)) 54 | return 55 | default: 56 | fmt.Println("Unkown command") 57 | } 58 | } 59 | 60 | func main() { 61 | http.HandleFunc("/port/", vmServer) 62 | http.HandleFunc("/port", vmServer) 63 | http.ListenAndServe(":9060", nil) 64 | } 65 | -------------------------------------------------------------------------------- /pkg/network/opencontrail/mocks/Store.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | import "github.com/stretchr/testify/mock" 4 | 5 | type Store struct { 6 | mock.Mock 7 | } 8 | 9 | func (m *Store) Add(obj interface{}) error { 10 | ret := m.Called(obj) 11 | 12 | r0 := ret.Error(0) 13 | 14 | return r0 15 | } 16 | func (m *Store) Update(obj interface{}) error { 17 | ret := m.Called(obj) 18 | 19 | r0 := ret.Error(0) 20 | 21 | return r0 22 | } 23 | func (m *Store) Delete(obj interface{}) error { 24 | ret := m.Called(obj) 25 | 26 | r0 := ret.Error(0) 27 | 28 | return r0 29 | } 30 | func (m *Store) List() []interface{} { 31 | ret := m.Called() 32 | 33 | var r0 []interface{} 34 | if ret.Get(0) != nil { 35 | r0 = ret.Get(0).([]interface{}) 36 | } 37 | 38 | return r0 39 | } 40 | func (m *Store) ListKeys() []string { 41 | ret := m.Called() 42 | 43 | var r0 []string 44 | if ret.Get(0) != nil { 45 | r0 = ret.Get(0).([]string) 46 | } 47 | 48 | return r0 49 | } 50 | func (m *Store) Get(obj interface{}) (interface{}, bool, error) { 51 | ret := m.Called(obj) 52 | 53 | var r0 interface{} 54 | if ret.Get(0) != nil { 55 | r0 = ret.Get(0).(interface{}) 56 | } 57 | r1 := ret.Get(1).(bool) 58 | r2 := ret.Error(2) 59 | 60 | return r0, r1, r2 61 | } 62 | func (m *Store) GetByKey(key string) (interface{}, bool, error) { 63 | ret := m.Called(key) 64 | 65 | var r0 interface{} 66 | if ret.Get(0) != nil { 67 | r0 = ret.Get(0).(interface{}) 68 | } 69 | r1 := ret.Get(1).(bool) 70 | r2 := ret.Error(2) 71 | 72 | return r0, r1, r2 73 | } 74 | func (m *Store) Replace(_a0 []interface{}, _a1 string) error { 75 | ret := m.Called(_a0, _a1) 76 | 77 | r0 := ret.Error(0) 78 | 79 | return r0 80 | } 81 | -------------------------------------------------------------------------------- /pkg/network/controller.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015 Juniper Networks, Inc. All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package network 18 | 19 | import ( 20 | "io" 21 | 22 | "k8s.io/kubernetes/pkg/api" 23 | "k8s.io/kubernetes/pkg/client/cache" 24 | ) 25 | 26 | type NetworkController interface { 27 | Init(global *Config, configFile io.Reader) error 28 | 29 | SetNamespaceStore(store cache.Store) 30 | AddNamespace(obj *api.Namespace) 31 | UpdateNamespace(oldObj, newObj *api.Namespace) 32 | DeleteNamespace(obj *api.Namespace) 33 | 34 | SetPodStore(store cache.Indexer) 35 | AddPod(obj *api.Pod) 36 | UpdatePod(oldObj, newObj *api.Pod) 37 | DeletePod(obj *api.Pod) 38 | 39 | SetReplicationControllerStore(store cache.Store) 40 | AddReplicationController(obj *api.ReplicationController) 41 | UpdateReplicationController(oldObj, newObj *api.ReplicationController) 42 | DeleteReplicationController(obj *api.ReplicationController) 43 | 44 | SetServiceStore(store cache.Store) 45 | AddService(obj *api.Service) 46 | UpdateService(oldObj, newObj *api.Service) 47 | DeleteService(obj *api.Service) 48 | 49 | Run(shutdown chan struct{}) 50 | } 51 | -------------------------------------------------------------------------------- /cluster/kube-network-manager.manifest.sls: -------------------------------------------------------------------------------- 1 | {% set params = "" -%} 2 | 3 | {% if pillar['service_private_ip_range'] is defined -%} 4 | {% set private_net = "--private_net=" + pillar['service_private_ip_range'] -%} 5 | {% else -%} 6 | {% set private_net = "--private_net=10.10.0.0/16" -%} 7 | {% endif -%} 8 | 9 | {% if pillar['service_cluster_ip_range'] is defined -%} 10 | {% set portal_net = "--portal_net=" + pillar['service_cluster_ip_range'] -%} 11 | {% else -%} 12 | {% set portal_net = "--portal_net=10.0.0.0/16" -%} 13 | {% endif -%} 14 | 15 | {% if pillar['opencontrail_public_subnet'] is defined -%} 16 | {% set public_net = "--public_net=" + pillar['opencontrail_public_subnet'] -%} 17 | {% else -%} 18 | {% set public_net = "--public_net=10.1.0.0/16" -%} 19 | {% endif -%} 20 | 21 | { 22 | "apiVersion": "v1", 23 | "kind": "Pod", 24 | "metadata": {"name":"kube-network-manager"}, 25 | "spec":{ 26 | "hostNetwork": true, 27 | "containers":[{ 28 | "name": "kube-network-manager", 29 | "image": "opencontrail/kube-network-manager", 30 | "command": ["/go/kube-network-manager", "--", "{{ private_net }}", "{{ portal_net }}", "{{ public_net }}"], 31 | "volumeMounts": [{ 32 | "name": "config", 33 | "mountPath": "/etc/kubernetes" 34 | }, 35 | { 36 | "name": "logs", 37 | "mountPath": "/var/log/contrail", 38 | "readOnly": false 39 | }] 40 | }], 41 | "volumes": [{ 42 | "name": "config", 43 | "hostPath": {"path": "/etc/contrail"} 44 | }, 45 | { 46 | "name": "logs", 47 | "hostPath": {"path": "/var/log/contrail"} 48 | }] 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /scripts/opencontrail-kubelet/opencontrail_kubelet/vrouter_api.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2015 Juniper Networks, Inc. 3 | # 4 | 5 | import json 6 | import logging 7 | import requests 8 | 9 | 10 | VROUTER_AGENT_PORT = 9091 11 | 12 | 13 | class ContrailVRouterApi(object): 14 | def __init__(self): 15 | pass 16 | 17 | def add_port(self, instanceId, nicId, sysIfName, macAddress, **kwargs): 18 | data = { 19 | "id": nicId, 20 | "instance-id": instanceId, 21 | "system-name": sysIfName, 22 | "mac-address": macAddress, 23 | "vn-id": "00000000-0000-0000-0000-000000000001", 24 | "vm-project-id": "00000000-0000-0000-0000-000000000001", 25 | "ip-address": "0.0.0.0", 26 | "ip6-address": "0::0", 27 | "rx-vlan-id": 0, 28 | "tx-vlan-id": 0, 29 | "type": 0 30 | } 31 | 32 | if 'display_name' in kwargs: 33 | data['display-name'] = kwargs['display_name'] 34 | if 'port_type' in kwargs: 35 | if kwargs['port_type'] == "NovaVMPort": 36 | data['type'] = 0 37 | if kwargs['port_type'] == "NameSpacePort": 38 | data['type'] = 1 39 | 40 | json_data = json.dumps(data) 41 | 42 | url = "http://localhost:%d/port" % (VROUTER_AGENT_PORT) 43 | headers = {'content-type': 'application/json'} 44 | r = requests.post(url, data=json_data, headers=headers) 45 | if r.status_code != requests.codes.ok: 46 | logging.error("%s: %s", url, r.text) 47 | 48 | def delete_port(self, nicId): 49 | url = "http://localhost:%d/port/%s" % (VROUTER_AGENT_PORT, nicId) 50 | headers = {'content-type': 'application/json'} 51 | r = requests.delete(url, data=None, headers=headers) 52 | if r.status_code != requests.codes.ok: 53 | logging.error("%s: %s", url, r.headers['status']) 54 | -------------------------------------------------------------------------------- /pkg/network/config_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015 Juniper Networks, Inc. All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package network 18 | 19 | import ( 20 | "bytes" 21 | "testing" 22 | "time" 23 | ) 24 | 25 | func TestMaster(t *testing.T) { 26 | configData := ` 27 | [DEFAULT] 28 | master = https://master:443 29 | 30 | [opencontrail] 31 | option = value 32 | ` 33 | buffer := bytes.NewBufferString(configData) 34 | var config Config 35 | err := ReadConfiguration(buffer, &config) 36 | if err != nil { 37 | t.Error(err) 38 | } 39 | if config.KubeUrl != "https://master:443" { 40 | t.Errorf("expected https://master:443, got %s", config.KubeUrl) 41 | } 42 | } 43 | 44 | func TestDuration(t *testing.T) { 45 | configData := ` 46 | [DEFAULT] 47 | resync-interval = 10 48 | ` 49 | buffer := bytes.NewBufferString(configData) 50 | var config Config 51 | err := ReadConfiguration(buffer, &config) 52 | if err != nil { 53 | t.Error(err) 54 | } 55 | if config.ResyncPeriod != time.Duration(10)*time.Second { 56 | t.Errorf("expected 10s, got %s", config.ResyncPeriod.String()) 57 | } 58 | } 59 | 60 | func TestBadSubnet(t *testing.T) { 61 | configData := ` 62 | [DEFAULT] 63 | service-cluster-ip-range = 10 64 | ` 65 | buffer := bytes.NewBufferString(configData) 66 | var config Config 67 | err := ReadConfiguration(buffer, &config) 68 | if err == nil { 69 | t.Errorf("expected error") 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /pkg/network/opencontrail/address_allocator_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015 Juniper Networks, Inc. All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package opencontrail 18 | 19 | import ( 20 | "fmt" 21 | "testing" 22 | 23 | "github.com/golang/glog" 24 | "github.com/pborman/uuid" 25 | "github.com/stretchr/testify/assert" 26 | 27 | "github.com/Juniper/contrail-go-api" 28 | contrail_mocks "github.com/Juniper/contrail-go-api/mocks" 29 | "github.com/Juniper/contrail-go-api/types" 30 | ) 31 | 32 | type IpInterceptor struct { 33 | count int 34 | } 35 | 36 | func (i *IpInterceptor) Put(ptr contrail.IObject) { 37 | ip := ptr.(*types.InstanceIp) 38 | i.count += 1 39 | ip.SetInstanceIpAddress(fmt.Sprintf("10.254.%d.%d", i.count/256, i.count&0xff)) 40 | } 41 | 42 | func (i *IpInterceptor) Get(ptr contrail.IObject) { 43 | } 44 | 45 | func TestAllocator(t *testing.T) { 46 | client := new(contrail_mocks.ApiClient) 47 | client.Init() 48 | client.AddInterceptor("instance-ip", &IpInterceptor{}) 49 | 50 | allocator := NewAddressAllocator(client, NewConfig()) 51 | 52 | id := uuid.New() 53 | addr, err := allocator.LocateIpAddress(id) 54 | assert.NoError(t, err) 55 | assert.Equal(t, "10.254.0.1", addr) 56 | 57 | ipObj, err := types.InstanceIpByName(client, id) 58 | assert.NoError(t, err) 59 | glog.Infof(ipObj.GetInstanceIpAddress()) 60 | 61 | allocator.ReleaseIpAddress(id) 62 | _, err = types.InstanceIpByName(client, id) 63 | assert.Error(t, err) 64 | } 65 | -------------------------------------------------------------------------------- /pkg/network/opencontrail/mocks/KubeServiceInterface.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | import ( 4 | "github.com/stretchr/testify/mock" 5 | 6 | "k8s.io/kubernetes/pkg/api" 7 | "k8s.io/kubernetes/pkg/client/restclient" 8 | "k8s.io/kubernetes/pkg/watch" 9 | ) 10 | 11 | type KubeServiceInterface struct { 12 | mock.Mock 13 | } 14 | 15 | func (m *KubeServiceInterface) List(opts api.ListOptions) (*api.ServiceList, error) { 16 | ret := m.Called(opts) 17 | 18 | var r0 *api.ServiceList 19 | if ret.Get(0) != nil { 20 | r0 = ret.Get(0).(*api.ServiceList) 21 | } 22 | r1 := ret.Error(1) 23 | 24 | return r0, r1 25 | } 26 | func (m *KubeServiceInterface) Get(name string) (*api.Service, error) { 27 | ret := m.Called(name) 28 | 29 | var r0 *api.Service 30 | if ret.Get(0) != nil { 31 | r0 = ret.Get(0).(*api.Service) 32 | } 33 | r1 := ret.Error(1) 34 | 35 | return r0, r1 36 | } 37 | func (m *KubeServiceInterface) Create(srv *api.Service) (*api.Service, error) { 38 | ret := m.Called(srv) 39 | 40 | var r0 *api.Service 41 | if ret.Get(0) != nil { 42 | r0 = ret.Get(0).(*api.Service) 43 | } 44 | r1 := ret.Error(1) 45 | 46 | return r0, r1 47 | } 48 | func (m *KubeServiceInterface) Update(srv *api.Service) (*api.Service, error) { 49 | ret := m.Called(srv) 50 | 51 | var r0 *api.Service 52 | if ret.Get(0) != nil { 53 | r0 = ret.Get(0).(*api.Service) 54 | } 55 | r1 := ret.Error(1) 56 | 57 | return r0, r1 58 | } 59 | func (m *KubeServiceInterface) Delete(name string) error { 60 | ret := m.Called(name) 61 | 62 | r0 := ret.Error(0) 63 | 64 | return r0 65 | } 66 | func (m *KubeServiceInterface) Watch(opts api.ListOptions) (watch.Interface, error) { 67 | ret := m.Called(opts) 68 | 69 | r0 := ret.Get(0).(watch.Interface) 70 | r1 := ret.Error(1) 71 | 72 | return r0, r1 73 | } 74 | 75 | func (m *KubeServiceInterface) ProxyGet(scheme, name, port, path string, params map[string]string) restclient.ResponseWrapper { 76 | ret := m.Called(scheme, name, port, path, params) 77 | 78 | r0 := ret.Get(0).(restclient.ResponseWrapper) 79 | return r0 80 | } 81 | -------------------------------------------------------------------------------- /pkg/network/opencontrail/util_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015 Juniper Networks, Inc. All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package opencontrail 18 | 19 | import ( 20 | "reflect" 21 | "strings" 22 | "testing" 23 | 24 | "github.com/stretchr/testify/assert" 25 | 26 | "k8s.io/kubernetes/pkg/api" 27 | ) 28 | 29 | func TestServiceIdList(t *testing.T) { 30 | config := NewConfig() 31 | config.NamespaceServices = nil 32 | pod := &api.Pod{ 33 | ObjectMeta: api.ObjectMeta{ 34 | Name: "test-xz1", 35 | Namespace: "testns", 36 | Labels: map[string]string{ 37 | config.NetworkTag: "testpod", 38 | config.NetworkAccessTag: "service1", 39 | }, 40 | }, 41 | } 42 | 43 | list := MakeServiceIdList() 44 | 45 | // ServiceIdList is a slice; it must be passed as a pointer in order to be 46 | // modified. 47 | buildPodServiceList(pod, config, &list) 48 | 49 | if len(list) != 1 { 50 | t.Errorf("expected list length 1, got %d", len(list)) 51 | } 52 | names := make([]string, 0) 53 | for _, v := range list { 54 | names = append(names, v.Service) 55 | } 56 | if !reflect.DeepEqual(names, []string{"service1"}) { 57 | t.Errorf("expected [\"service1\"], got %+v", names) 58 | } 59 | } 60 | 61 | func TestEscapedNames(t *testing.T) { 62 | values := []string{ 63 | "a_b:c_d:x", 64 | "a\\_b:c_d:x", 65 | "foo:bar:baz", 66 | } 67 | for _, v := range values { 68 | escapedName := escapeFQN(strings.Split(v, ":")) 69 | result := splitEscapedString(strings.Join(escapedName, ":")) 70 | assert.Equal(t, v, strings.Join(result, ":")) 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /pkg/network/opencontrail/mocks/KubeNamespaceInterface.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | import ( 4 | "github.com/stretchr/testify/mock" 5 | 6 | "k8s.io/kubernetes/pkg/api" 7 | "k8s.io/kubernetes/pkg/watch" 8 | ) 9 | 10 | type KubeNamespaceInterface struct { 11 | mock.Mock 12 | } 13 | 14 | func (m *KubeNamespaceInterface) Create(item *api.Namespace) (*api.Namespace, error) { 15 | ret := m.Called(item) 16 | 17 | var r0 *api.Namespace 18 | if ret.Get(0) != nil { 19 | r0 = ret.Get(0).(*api.Namespace) 20 | } 21 | r1 := ret.Error(1) 22 | 23 | return r0, r1 24 | } 25 | func (m *KubeNamespaceInterface) Get(name string) (*api.Namespace, error) { 26 | ret := m.Called(name) 27 | 28 | var r0 *api.Namespace 29 | if ret.Get(0) != nil { 30 | r0 = ret.Get(0).(*api.Namespace) 31 | } 32 | r1 := ret.Error(1) 33 | 34 | return r0, r1 35 | } 36 | func (m *KubeNamespaceInterface) List(opts api.ListOptions) (*api.NamespaceList, error) { 37 | ret := m.Called(opts) 38 | 39 | var r0 *api.NamespaceList 40 | if ret.Get(0) != nil { 41 | r0 = ret.Get(0).(*api.NamespaceList) 42 | } 43 | r1 := ret.Error(1) 44 | 45 | return r0, r1 46 | } 47 | func (m *KubeNamespaceInterface) Delete(name string) error { 48 | ret := m.Called(name) 49 | 50 | r0 := ret.Error(0) 51 | 52 | return r0 53 | } 54 | func (m *KubeNamespaceInterface) Update(item *api.Namespace) (*api.Namespace, error) { 55 | ret := m.Called(item) 56 | 57 | var r0 *api.Namespace 58 | if ret.Get(0) != nil { 59 | r0 = ret.Get(0).(*api.Namespace) 60 | } 61 | r1 := ret.Error(1) 62 | 63 | return r0, r1 64 | } 65 | func (m *KubeNamespaceInterface) Watch(opts api.ListOptions) (watch.Interface, error) { 66 | ret := m.Called(opts) 67 | 68 | r0 := ret.Get(0).(watch.Interface) 69 | r1 := ret.Error(1) 70 | 71 | return r0, r1 72 | } 73 | func (m *KubeNamespaceInterface) Finalize(item *api.Namespace) (*api.Namespace, error) { 74 | ret := m.Called(item) 75 | 76 | var r0 *api.Namespace 77 | if ret.Get(0) != nil { 78 | r0 = ret.Get(0).(*api.Namespace) 79 | } 80 | r1 := ret.Error(1) 81 | 82 | return r0, r1 83 | } 84 | func (m *KubeNamespaceInterface) Status(item *api.Namespace) (*api.Namespace, error) { 85 | ret := m.Called(item) 86 | 87 | var r0 *api.Namespace 88 | if ret.Get(0) != nil { 89 | r0 = ret.Get(0).(*api.Namespace) 90 | } 91 | r1 := ret.Error(1) 92 | 93 | return r0, r1 94 | } 95 | -------------------------------------------------------------------------------- /pkg/network/opencontrail/mocks/KubePodInterface.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | import "github.com/stretchr/testify/mock" 4 | 5 | import "k8s.io/kubernetes/pkg/api" 6 | import "k8s.io/kubernetes/pkg/watch" 7 | import "k8s.io/kubernetes/pkg/client/restclient" 8 | 9 | type KubePodInterface struct { 10 | mock.Mock 11 | } 12 | 13 | func (m *KubePodInterface) List(opts api.ListOptions) (*api.PodList, error) { 14 | ret := m.Called(opts) 15 | 16 | var r0 *api.PodList 17 | if ret.Get(0) != nil { 18 | r0 = ret.Get(0).(*api.PodList) 19 | } 20 | r1 := ret.Error(1) 21 | 22 | return r0, r1 23 | } 24 | func (m *KubePodInterface) Get(name string) (*api.Pod, error) { 25 | ret := m.Called(name) 26 | 27 | var r0 *api.Pod 28 | if ret.Get(0) != nil { 29 | r0 = ret.Get(0).(*api.Pod) 30 | } 31 | r1 := ret.Error(1) 32 | 33 | return r0, r1 34 | } 35 | func (m *KubePodInterface) Delete(name string, options *api.DeleteOptions) error { 36 | ret := m.Called(name, options) 37 | 38 | r0 := ret.Error(0) 39 | 40 | return r0 41 | } 42 | func (m *KubePodInterface) Create(pod *api.Pod) (*api.Pod, error) { 43 | ret := m.Called(pod) 44 | 45 | var r0 *api.Pod 46 | if ret.Get(0) != nil { 47 | r0 = ret.Get(0).(*api.Pod) 48 | } 49 | r1 := ret.Error(1) 50 | 51 | return r0, r1 52 | } 53 | func (m *KubePodInterface) Update(pod *api.Pod) (*api.Pod, error) { 54 | ret := m.Called(pod) 55 | 56 | var r0 *api.Pod 57 | if ret.Get(0) != nil { 58 | r0 = ret.Get(0).(*api.Pod) 59 | } 60 | r1 := ret.Error(1) 61 | 62 | return r0, r1 63 | } 64 | func (m *KubePodInterface) Watch(opts api.ListOptions) (watch.Interface, error) { 65 | ret := m.Called(opts) 66 | 67 | r0 := ret.Get(0).(watch.Interface) 68 | r1 := ret.Error(1) 69 | 70 | return r0, r1 71 | } 72 | func (m *KubePodInterface) Bind(binding *api.Binding) error { 73 | ret := m.Called(binding) 74 | 75 | r0 := ret.Error(0) 76 | 77 | return r0 78 | } 79 | func (m *KubePodInterface) UpdateStatus(pod *api.Pod) (*api.Pod, error) { 80 | ret := m.Called(pod) 81 | 82 | var r0 *api.Pod 83 | if ret.Get(0) != nil { 84 | r0 = ret.Get(0).(*api.Pod) 85 | } 86 | r1 := ret.Error(1) 87 | 88 | return r0, r1 89 | } 90 | 91 | func (m *KubePodInterface) GetLogs(name string, opts *api.PodLogOptions) *restclient.Request { 92 | ret := m.Called(name, opts) 93 | 94 | r0 := ret.Get(0).(*restclient.Request) 95 | return r0 96 | } 97 | -------------------------------------------------------------------------------- /pkg/network/config.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015 Juniper Networks, Inc. All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package network 18 | 19 | import ( 20 | "bufio" 21 | "bytes" 22 | "io" 23 | "net" 24 | "regexp" 25 | "strings" 26 | "time" 27 | 28 | "gopkg.in/gcfg.v1" 29 | ) 30 | 31 | type Config struct { 32 | KubeUrl string `gcfg:"master"` 33 | KubeConfig string `gcfg:"kubeconfig"` 34 | ResyncPeriod time.Duration `gcfg:"resync-interval"` 35 | ClusterIpRange string `gcfg:"service-cluster-ip-range"` 36 | } 37 | 38 | type configWrapper struct { 39 | Default Config 40 | } 41 | 42 | func readSection(reader io.Reader, section string) *bytes.Buffer { 43 | buffer := new(bytes.Buffer) 44 | scanner := bufio.NewScanner(reader) 45 | re := regexp.MustCompile(`\[(\w+)\]`) 46 | var record bool 47 | for scanner.Scan() { 48 | line := scanner.Text() 49 | if match := re.FindStringSubmatch(line); match != nil { 50 | if record { 51 | break 52 | } 53 | if strings.EqualFold(section, match[1]) { 54 | record = true 55 | } 56 | } 57 | if record { 58 | buffer.WriteString(line) 59 | buffer.WriteByte('\n') 60 | } 61 | } 62 | return buffer 63 | } 64 | 65 | func ReadConfiguration(reader io.Reader, config *Config) error { 66 | wrapper := configWrapper{Default: *config} 67 | wrapper.Default.ResyncPeriod = 0 68 | 69 | buffer := readSection(reader, "default") 70 | err := gcfg.ReadInto(&wrapper, bytes.NewReader(buffer.Bytes())) 71 | if err != nil { 72 | return err 73 | } 74 | 75 | if wrapper.Default.ResyncPeriod != 0 { 76 | wrapper.Default.ResyncPeriod = wrapper.Default.ResyncPeriod * time.Second 77 | } else { 78 | wrapper.Default.ResyncPeriod = config.ResyncPeriod 79 | } 80 | 81 | if clusterIp := wrapper.Default.ClusterIpRange; clusterIp != "" { 82 | if _, _, err := net.ParseCIDR(clusterIp); err != nil { 83 | return err 84 | } 85 | } 86 | 87 | *config = wrapper.Default 88 | return nil 89 | } 90 | -------------------------------------------------------------------------------- /pkg/network/opencontrail/namespace.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015 Juniper Networks, Inc. All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package opencontrail 18 | 19 | import ( 20 | "strings" 21 | 22 | "github.com/golang/glog" 23 | 24 | kubeclient "k8s.io/kubernetes/pkg/client/unversioned" 25 | 26 | "github.com/Juniper/contrail-go-api" 27 | "github.com/Juniper/contrail-go-api/types" 28 | ) 29 | 30 | type NamespaceManager struct { 31 | client contrail.ApiClient 32 | config *Config 33 | kube kubeclient.Interface 34 | } 35 | 36 | func NewNamespaceManager(client contrail.ApiClient, config *Config, kube kubeclient.Interface) *NamespaceManager { 37 | manager := new(NamespaceManager) 38 | manager.client = client 39 | manager.config = config 40 | manager.kube = kube 41 | 42 | // create default namespace 43 | namespace, err := kube.Namespaces().Get(DefaultServiceProjectName) 44 | if err != nil { 45 | glog.Errorf("Get namespace %s: %v", DefaultServiceProjectName, err) 46 | return manager 47 | } 48 | project := manager.LocateNamespace(namespace.Name, string(namespace.ObjectMeta.UID)) 49 | 50 | if project == nil { 51 | glog.Fatalf("Cannot create project %, is Contrail running?", DefaultServiceProjectName) 52 | } 53 | return manager 54 | } 55 | 56 | func (m *NamespaceManager) LookupNamespace(name string) *types.Project { 57 | fqn := []string{m.config.DefaultDomain, name} 58 | 59 | obj, err := m.client.FindByName("project", strings.Join(fqn, ":")) 60 | if err != nil { 61 | return nil 62 | } 63 | return obj.(*types.Project) 64 | } 65 | 66 | func (m *NamespaceManager) LocateNamespace(name, uid string) *types.Project { 67 | fqn := []string{m.config.DefaultDomain, name} 68 | 69 | obj, err := m.client.FindByName("project", strings.Join(fqn, ":")) 70 | if err == nil { 71 | return obj.(*types.Project) 72 | } 73 | project := new(types.Project) 74 | project.SetFQName("domain", fqn) 75 | project.SetUuid(uid) 76 | err = m.client.Create(project) 77 | if err != nil { 78 | glog.Errorf("Create project %s: %v", name, err) 79 | return nil 80 | } 81 | return project 82 | } 83 | 84 | func (m *NamespaceManager) DeleteNamespace(name string) error { 85 | fqn := []string{m.config.DefaultDomain, name} 86 | 87 | obj, err := m.client.FindByName("project", strings.Join(fqn, ":")) 88 | if err != nil { 89 | return err 90 | } 91 | 92 | m.client.Delete(obj) 93 | return nil 94 | } 95 | -------------------------------------------------------------------------------- /pkg/network/opencontrail/util.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015 Juniper Networks, Inc. All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package opencontrail 18 | 19 | import ( 20 | "strconv" 21 | "strings" 22 | ) 23 | 24 | type ServiceId struct { 25 | Namespace string 26 | Service string 27 | } 28 | 29 | type ServiceIdList []ServiceId 30 | 31 | func (s *ServiceIdList) Contains(namespace, service string) bool { 32 | for _, entry := range *s { 33 | if entry.Namespace == namespace && entry.Service == service { 34 | return true 35 | } 36 | } 37 | return false 38 | } 39 | func (s *ServiceIdList) Add(namespace, service string) { 40 | if s.Contains(namespace, service) { 41 | return 42 | } 43 | *s = append(*s, ServiceId{namespace, service}) 44 | } 45 | 46 | func MakeServiceIdList() ServiceIdList { 47 | return make(ServiceIdList, 0) 48 | } 49 | 50 | // serviceIdFromName splits a prevalidated string in the form namespace/service-name. 51 | func serviceIdFromName(name string) (string, string) { 52 | tuple := strings.Split(name, "/") 53 | return tuple[0], tuple[1] 54 | } 55 | 56 | func IsClusterService(c *Config, namespace, name string) bool { 57 | for _, svc := range c.ClusterServices { 58 | svc_ns, svc_name := serviceIdFromName(svc) 59 | if svc_ns == namespace && svc_name == name { 60 | return true 61 | } 62 | } 63 | return false 64 | } 65 | 66 | func AppendConst(slice []string, element string) []string { 67 | newSlice := make([]string, len(slice), len(slice)+1) 68 | copy(newSlice, slice) 69 | return append(newSlice, element) 70 | } 71 | 72 | func PrefixToAddressLen(subnet string) (string, int) { 73 | prefix := strings.Split(subnet, "/") 74 | address := prefix[0] 75 | prefixlen, _ := strconv.Atoi(prefix[1]) 76 | return address, prefixlen 77 | } 78 | 79 | func escapeFQN(input []string) []string { 80 | result := make([]string, 0, len(input)) 81 | for _, piece := range input { 82 | result = append(result, strings.Replace(piece, "_", "\\_", -1)) 83 | } 84 | return result 85 | } 86 | 87 | func splitEscapedString(name string) []string { 88 | splitIndices := make([]int, 0) 89 | last := 0 90 | for { 91 | ix := strings.Index(name[last:], "_") 92 | if ix < 0 { 93 | break 94 | } 95 | abs := last + ix 96 | last = abs + 1 97 | if name[abs-1] == '\\' { 98 | continue 99 | } 100 | splitIndices = append(splitIndices, abs) 101 | } 102 | 103 | result := make([]string, 0) 104 | prev := 0 105 | for _, value := range splitIndices { 106 | piece := strings.Replace(name[prev:value], "\\_", "_", -1) 107 | result = append(result, piece) 108 | prev = value + 1 109 | } 110 | piece := strings.Replace(name[prev:], "\\_", "_", -1) 111 | result = append(result, piece) 112 | return result 113 | } 114 | -------------------------------------------------------------------------------- /pkg/network/opencontrail/service_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015 Juniper Networks, Inc. All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package opencontrail 18 | 19 | import ( 20 | "strings" 21 | "testing" 22 | 23 | "github.com/stretchr/testify/assert" 24 | "github.com/stretchr/testify/require" 25 | 26 | contrail_mocks "github.com/Juniper/contrail-go-api/mocks" 27 | "github.com/Juniper/contrail-go-api/types" 28 | ) 29 | 30 | func TestServiceDeleteConnections(t *testing.T) { 31 | client := new(contrail_mocks.ApiClient) 32 | client.Init() 33 | 34 | config := NewConfig() 35 | networkMgr := NewNetworkManager(client, config) 36 | serviceMgr := NewServiceManager(client, config, networkMgr) 37 | 38 | netnsProject := new(types.Project) 39 | netnsProject.SetFQName("", []string{"default-domain", "testns"}) 40 | client.Create(netnsProject) 41 | 42 | globalProject := new(types.Project) 43 | globalProject.SetFQName("", []string{"default-domain", "global"}) 44 | client.Create(globalProject) 45 | 46 | network, err := networkMgr.LocateNetwork("testns", "client", config.PrivateSubnet) 47 | 48 | services := []string{ 49 | "testns/s1", 50 | "testns/s2", 51 | "global/s3", 52 | "global/s4", 53 | } 54 | 55 | for _, svc := range services { 56 | parts := strings.Split(svc, "/") 57 | namespace := parts[0] 58 | svcName := parts[1] 59 | err := serviceMgr.Create(namespace, svcName) 60 | if err != nil { 61 | t.Error(err) 62 | continue 63 | } 64 | err = serviceMgr.Connect(network, namespace, svcName) 65 | if err != nil { 66 | t.Error(err) 67 | } 68 | } 69 | 70 | serviceList := MakeServiceIdList() 71 | serviceList.Add("testns", "s1") 72 | serviceList.Add("global", "s3") 73 | 74 | purgeList := make([]string, 0) 75 | policyRefs, err := network.GetNetworkPolicyRefs() 76 | require.NoError(t, err) 77 | for _, ref := range policyRefs { 78 | require.Len(t, ref.To, 3) 79 | svcName, err := serviceNameFromPolicyName(ref.To[2]) 80 | require.NoError(t, err) 81 | if serviceList.Contains(ref.To[1], svcName) { 82 | continue 83 | } 84 | purgeList = append(purgeList, ref.Uuid) 85 | } 86 | 87 | err = serviceMgr.DeleteConnections(network, purgeList) 88 | if err != nil { 89 | t.Fatal(err) 90 | } 91 | 92 | refs, err := network.GetNetworkPolicyRefs() 93 | if err != nil { 94 | t.Fatal(err) 95 | } 96 | if len(refs) != len(serviceList) { 97 | t.Errorf("expected %d policy refs, got %d", len(serviceList), len(refs)) 98 | } 99 | actual := make([]string, 0) 100 | for _, ref := range refs { 101 | svc, err := serviceNameFromPolicyName(ref.To[2]) 102 | if err != nil { 103 | continue 104 | } 105 | actual = append(actual, ref.To[1]+"/"+svc) 106 | } 107 | assert.EqualValues(t, []string{"testns/s1", "global/s3"}, actual) 108 | } 109 | -------------------------------------------------------------------------------- /scripts/opencontrail-install/ifup-vhost: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | source /etc/contrail/opencontrail-rc 3 | 4 | if [ -e /lib/lsb/init-functions ]; then 5 | . /lib/lsb/init-functions 6 | fi 7 | 8 | if [ -f /etc/sysconfig/network-scripts ]; then 9 | . /etc/sysconfig/network-scripts/network-functions 10 | fi 11 | 12 | [ -f ../network ] && . ../network 13 | 14 | LOGFILE=/var/log/vhost.log 15 | 16 | timestamp() { 17 | date 18 | } 19 | 20 | log_error_msg() { 21 | msg=$1 22 | echo "$(timestamp): ERROR: $msg" >> $LOGFILE 23 | } 24 | 25 | log_warn_msg() { 26 | msg=$1 27 | echo "$(timestamp): WARNING: $msg" >> $LOGFILE 28 | } 29 | 30 | log_info_msg() { 31 | msg=$1 32 | echo "$(timestamp): INFO: $msg" >> $LOGFILE 33 | } 34 | 35 | function pre-checks() 36 | { 37 | vrouter=$(cat /proc/modules |grep vrouter | awk '{print $1}') 38 | if [ "$vrouter" != "vrouter" ]; then 39 | log_info_msg "vrouter not found. Initializing now.." 40 | modprobe vrouter 41 | if [ $? != 0 ]; then 42 | log_info_msg "vrouter kernel module probe - Error" 43 | exit 44 | fi 45 | fi 46 | 47 | if [ "$IFACE" != "vhost0" ]; then 48 | log_info_msg "Event not for vhost0" 49 | exit 50 | fi 51 | } 52 | 53 | function get_hwaddr () 54 | { 55 | if [ -f /sys/class/net/${1}/address ]; then 56 | awk '{ print toupper($0) }' < /sys/class/net/${1}/address 57 | elif [ -d "/sys/class/net/${1}" ]; then 58 | LC_ALL= LANG= ip -o link show ${1} 2>/dev/null | \ 59 | awk '{ print toupper(gensub(/.*link\/[^ ]* ([[:alnum:]:]*).*/, 60 | "\\1", 1)); }' 61 | fi 62 | } 63 | 64 | function pkt_setup () { 65 | for f in /sys/class/net/$1/queues/rx-* 66 | do 67 | q="$(echo $f | cut -d '-' -f2)" 68 | r=$(($q%32)) 69 | s=$(($q/32)) 70 | ((mask=1<<$r)) 71 | str=(`printf "%x" $mask`) 72 | if [ $s -gt 0 ]; then 73 | for ((i=0; i < $s; i++)) 74 | do 75 | str+=,00000000 76 | done 77 | fi 78 | echo $str > $f/rps_cpus 79 | done 80 | } 81 | 82 | function pkt_bitmap_setup() 83 | { 84 | if [ -f /sys/class/net/pkt1/queues/rx-0/rps_cpus ]; then 85 | pkt_setup pkt1 86 | fi 87 | if [ -f /sys/class/net/pkt2/queues/rx-0/rps_cpus ]; then 88 | pkt_setup pkt2 89 | fi 90 | if [ -f /sys/class/net/pkt3/queues/rx-0/rps_cpus ]; then 91 | pkt_setup pkt3 92 | fi 93 | } 94 | 95 | function vif_create() 96 | { 97 | vif="/usr/bin/vif" 98 | if [ ! -L /sys/class/net/${IFACE} ]; then 99 | log_info_msg "Creating vhost interface" 100 | $vif --create ${IFACE} --mac $(get_hwaddr ${OPENCONTRAIL_VROUTER_INTF}) 101 | $vif --add ${OPENCONTRAIL_VROUTER_INTF} --mac $(get_hwaddr ${OPENCONTRAIL_VROUTER_INTF}) --vrf 0 --vhost-phys --type physical 102 | $vif --add ${IFACE} --mac $(get_hwaddr ${OPENCONTRAIL_VROUTER_INTF}) --vrf 0 --type vhost --xconnect ${OPENCONTRAIL_VROUTER_INTF} 103 | ip link set dev ${IFACE} up 104 | fi 105 | log_info_msg "vhost0 interface successfully instantiated" 106 | } 107 | 108 | function def_reroute() 109 | { 110 | # If default route is on OPENCONTRAIL_VROUTER_INTF move it to vhost0 111 | def=$(ip route | grep $OPENCONTRAIL_VROUTER_INTF | grep -o default) 112 | if [ "$def" == "default" ]; then 113 | defgw=$(ip route | grep $OPENCONTRAIL_VROUTER_INTF | awk 'NR==1{print $3}') 114 | `route add default gw $defgw dev $VHOST` 115 | `route del default dev $OPENCONTRAIL_VROUTER_INTF` 116 | fi 117 | } 118 | 119 | function main() 120 | { 121 | pre-checks 122 | pkt_bitmap_setup 123 | vif_create 124 | def_reroute 125 | } 126 | 127 | main 128 | -------------------------------------------------------------------------------- /pkg/network/opencontrail/mocks/NetworkManager.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | import ( 4 | "github.com/stretchr/testify/mock" 5 | 6 | "github.com/Juniper/contrail-go-api/types" 7 | ) 8 | 9 | type NetworkManager struct { 10 | mock.Mock 11 | } 12 | 13 | func (m *NetworkManager) LocateFloatingIpPool(network *types.VirtualNetwork) (*types.FloatingIpPool, error) { 14 | ret := m.Called(network) 15 | 16 | var r0 *types.FloatingIpPool 17 | if ret.Get(0) != nil { 18 | r0 = ret.Get(0).(*types.FloatingIpPool) 19 | } 20 | r1 := ret.Error(1) 21 | 22 | return r0, r1 23 | } 24 | func (m *NetworkManager) LookupFloatingIpPool(network *types.VirtualNetwork) (*types.FloatingIpPool, error) { 25 | ret := m.Called(network) 26 | 27 | var r0 *types.FloatingIpPool 28 | if ret.Get(0) != nil { 29 | r0 = ret.Get(0).(*types.FloatingIpPool) 30 | } 31 | r1 := ret.Error(1) 32 | 33 | return r0, r1 34 | } 35 | func (m *NetworkManager) DeleteFloatingIpPool(network *types.VirtualNetwork, cascade bool) error { 36 | ret := m.Called(network, cascade) 37 | 38 | r0 := ret.Error(0) 39 | 40 | return r0 41 | } 42 | func (m *NetworkManager) LookupNetwork(projectName string, networkName string) (*types.VirtualNetwork, error) { 43 | ret := m.Called(projectName, networkName) 44 | 45 | var r0 *types.VirtualNetwork 46 | if ret.Get(0) != nil { 47 | r0 = ret.Get(0).(*types.VirtualNetwork) 48 | } 49 | r1 := ret.Error(1) 50 | 51 | return r0, r1 52 | } 53 | func (m *NetworkManager) LocateNetwork(project string, name string, subnet string) (*types.VirtualNetwork, error) { 54 | ret := m.Called(project, name, subnet) 55 | 56 | var r0 *types.VirtualNetwork 57 | if ret.Get(0) != nil { 58 | r0 = ret.Get(0).(*types.VirtualNetwork) 59 | } 60 | r1 := ret.Error(1) 61 | 62 | return r0, r1 63 | } 64 | func (m *NetworkManager) DeleteNetwork(_a0 *types.VirtualNetwork) error { 65 | ret := m.Called(_a0) 66 | 67 | r0 := ret.Error(0) 68 | 69 | return r0 70 | } 71 | func (m *NetworkManager) ReleaseNetworkIfEmpty(namespace string, name string) (bool, error) { 72 | ret := m.Called(namespace, name) 73 | 74 | r0 := ret.Get(0).(bool) 75 | r1 := ret.Error(1) 76 | 77 | return r0, r1 78 | } 79 | func (m *NetworkManager) LocateFloatingIp(network *types.VirtualNetwork, resourceName string, address string) (*types.FloatingIp, error) { 80 | ret := m.Called(network, resourceName, address) 81 | 82 | var r0 *types.FloatingIp 83 | if ret.Get(0) != nil { 84 | r0 = ret.Get(0).(*types.FloatingIp) 85 | } 86 | r1 := ret.Error(1) 87 | 88 | return r0, r1 89 | } 90 | func (m *NetworkManager) DeleteFloatingIp(network *types.VirtualNetwork, resourceName string) error { 91 | ret := m.Called(network, resourceName) 92 | 93 | r0 := ret.Error(0) 94 | 95 | return r0 96 | } 97 | func (m *NetworkManager) GetPublicNetwork() *types.VirtualNetwork { 98 | ret := m.Called() 99 | 100 | var r0 *types.VirtualNetwork 101 | if ret.Get(0) != nil { 102 | r0 = ret.Get(0).(*types.VirtualNetwork) 103 | } 104 | 105 | return r0 106 | } 107 | func (m *NetworkManager) GetGatewayAddress(network *types.VirtualNetwork) (string, error) { 108 | ret := m.Called(network) 109 | 110 | r0 := ret.Get(0).(string) 111 | r1 := ret.Error(1) 112 | 113 | return r0, r1 114 | } 115 | 116 | func (m *NetworkManager) Connect(network *types.VirtualNetwork, networkFQN string) error { 117 | ret := m.Called(network, networkFQN) 118 | r0 := ret.Error(0) 119 | return r0 120 | } 121 | func (m *NetworkManager) Disconnect(networkFQN []string, targetCDN string) error { 122 | ret := m.Called(networkFQN, targetCDN) 123 | r0 := ret.Error(0) 124 | return r0 125 | } 126 | 127 | func (m *NetworkManager) DeleteConnections(network *types.VirtualNetwork, policies map[string]string) error { 128 | ret := m.Called(network, policies) 129 | r0 := ret.Error(0) 130 | return r0 131 | } 132 | -------------------------------------------------------------------------------- /pkg/network/opencontrail/address_allocator.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015 Juniper Networks, Inc. All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package opencontrail 18 | 19 | import ( 20 | "strings" 21 | 22 | "github.com/golang/glog" 23 | 24 | "github.com/Juniper/contrail-go-api" 25 | "github.com/Juniper/contrail-go-api/config" 26 | "github.com/Juniper/contrail-go-api/types" 27 | ) 28 | 29 | type AddressAllocator interface { 30 | LocateIpAddress(uid string) (string, error) 31 | ReleaseIpAddress(uid string) 32 | } 33 | 34 | // Allocate an unique address for each Pod. 35 | type AddressAllocatorImpl struct { 36 | client contrail.ApiClient 37 | network *types.VirtualNetwork 38 | privateSubnet string 39 | } 40 | 41 | const ( 42 | AddressAllocationNetwork = "default-domain:default:addr-alloc" 43 | ) 44 | 45 | func NewAddressAllocator(client contrail.ApiClient, config *Config) AddressAllocator { 46 | allocator := new(AddressAllocatorImpl) 47 | allocator.client = client 48 | allocator.privateSubnet = config.PrivateSubnet 49 | allocator.initializeAllocator() 50 | return allocator 51 | } 52 | 53 | func (a *AddressAllocatorImpl) initializeAllocator() { 54 | obj, err := a.client.FindByName("virtual-network", AddressAllocationNetwork) 55 | if err == nil { 56 | a.network = obj.(*types.VirtualNetwork) 57 | return 58 | } 59 | 60 | fqn := strings.Split(AddressAllocationNetwork, ":") 61 | parent := strings.Join(fqn[0:len(fqn)-1], ":") 62 | projectId, err := a.client.UuidByName("project", parent) 63 | if err != nil { 64 | glog.Fatalf("%s: %v", parent, err) 65 | } 66 | netId, err := config.CreateNetworkWithSubnet( 67 | a.client, projectId, fqn[len(fqn)-1], a.privateSubnet) 68 | if err != nil { 69 | glog.Fatalf("%s: %v", parent, err) 70 | } 71 | glog.Infof("Created network %s", AddressAllocationNetwork) 72 | obj, err = a.client.FindByUuid("virtual-network", netId) 73 | if err != nil { 74 | glog.Fatalf("Get virtual-network %s: %v", netId, err) 75 | } 76 | a.network = obj.(*types.VirtualNetwork) 77 | } 78 | 79 | func (a *AddressAllocatorImpl) allocateIpAddress(uid string) (contrail.IObject, error) { 80 | ipObj := new(types.InstanceIp) 81 | ipObj.SetName(uid) 82 | ipObj.AddVirtualNetwork(a.network) 83 | err := a.client.Create(ipObj) 84 | if err != nil { 85 | glog.Errorf("Create InstanceIp %s: %v", uid, err) 86 | return nil, err 87 | } 88 | obj, err := a.client.FindByUuid("instance-ip", ipObj.GetUuid()) 89 | if err != nil { 90 | glog.Errorf("Get InstanceIp %s: %v", uid, err) 91 | return nil, err 92 | } 93 | return obj, err 94 | } 95 | 96 | func (a *AddressAllocatorImpl) LocateIpAddress(uid string) (string, error) { 97 | obj, err := a.client.FindByName("instance-ip", uid) 98 | if err != nil { 99 | obj, err = a.allocateIpAddress(uid) 100 | if err != nil { 101 | return "", err 102 | } 103 | } 104 | 105 | ipObj := obj.(*types.InstanceIp) 106 | return ipObj.GetInstanceIpAddress(), nil 107 | } 108 | 109 | func (a *AddressAllocatorImpl) ReleaseIpAddress(uid string) { 110 | objid, err := a.client.UuidByName("instance-ip", uid) 111 | if err != nil { 112 | glog.V(1).Infof("IP address for %s: %v", uid, err) 113 | return 114 | } 115 | err = a.client.DeleteByUuid("instance-ip", objid) 116 | if err != nil { 117 | glog.Warningf("Delete instance-ip: %v", err) 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /pkg/network/opencontrail/virtual_router.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016 Juniper Networks, Inc. All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package opencontrail 18 | 19 | import ( 20 | "github.com/golang/glog" 21 | 22 | "k8s.io/kubernetes/pkg/api" 23 | 24 | "github.com/Juniper/contrail-go-api" 25 | "github.com/Juniper/contrail-go-api/types" 26 | ) 27 | 28 | type virtualRouterMap map[string]*types.VirtualRouter 29 | 30 | type VirtualRouterManager struct { 31 | client contrail.ApiClient 32 | config *Config 33 | vrMap virtualRouterMap 34 | } 35 | 36 | // Setup 37 | func NewVirtualRouterManager(client contrail.ApiClient, config *Config) (*VirtualRouterManager, error) { 38 | vrMgr := new(VirtualRouterManager) 39 | vrMgr.vrMap = make(virtualRouterMap, 0) 40 | vrMgr.client = client 41 | vrMgr.config = config 42 | 43 | objects, err := client.ListDetail("virtual-router", nil) 44 | if err != nil { 45 | glog.Error(err) 46 | return nil, err 47 | } 48 | 49 | for _, object := range objects { 50 | vr := object.(*types.VirtualRouter) 51 | vrMgr.vrMap[vr.GetVirtualRouterIpAddress()] = vr 52 | } 53 | return vrMgr, nil 54 | } 55 | 56 | // Add Pod(VM) reference to VirtualRouter in Contrail Config 57 | func (vrMgr *VirtualRouterManager) addPodRefToVirtualRouter( 58 | pod *api.Pod, instance *types.VirtualMachine) bool { 59 | 60 | if pod.Status.HostIP == "" { 61 | glog.Infof("HostIP is empty during pod(%s) add/update", pod.Name) 62 | return true 63 | } 64 | 65 | // Given HostIP, find virtual-router. 66 | var vr *types.VirtualRouter 67 | vr = vrMgr.vrMap[pod.Status.HostIP] 68 | if vr == nil { 69 | glog.Errorf("Pod(%s) added to non-exisitng Virtual-router(%s) node", pod.Name, pod.Status.HostIP) 70 | return false 71 | } 72 | 73 | // Update reference to virtual-machine 74 | err := vr.ObjectBase.ClientPtr.UpdateReference( 75 | &contrail.ReferenceUpdateMsg{ 76 | vr.GetType(), 77 | vr.GetUuid(), "virtual-machine", instance.GetUuid(), instance.GetFQName(), 78 | "ADD", 79 | nil, 80 | }) 81 | if err != nil { 82 | glog.Errorf("Failed to add pod(%s) to vrouter(%s): %v", pod.Name, pod.Status.HostIP, err) 83 | return false 84 | } 85 | 86 | glog.Infof("pod(%s) added to vRouter(%s)", pod.Name, pod.Status.HostIP) 87 | return true 88 | } 89 | 90 | // Remove Pod(VM) reference from VirtualRouter in Contrail Config 91 | func (vrMgr *VirtualRouterManager) removePodRefFromVirtualRouter( 92 | pod *api.Pod, instance *types.VirtualMachine) bool { 93 | 94 | if pod.Status.HostIP == "" { 95 | glog.Warningf("HostIP is empty during pod(%s) delete", pod.Name) 96 | return false; 97 | } 98 | 99 | // Given HostIP, find virtual-router. 100 | var vr *types.VirtualRouter 101 | vr = vrMgr.vrMap[pod.Status.HostIP] 102 | if vr == nil { 103 | glog.Errorf("Pod(%s) removed from non-exisitng Virtual-router(%s) node", pod.Name, pod.Status.HostIP) 104 | return false 105 | } 106 | 107 | // Update reference to virtual-machine 108 | err := vr.ObjectBase.ClientPtr.UpdateReference( 109 | &contrail.ReferenceUpdateMsg{ 110 | vr.GetType(), 111 | vr.GetUuid(), "virtual-machine", instance.GetUuid(), instance.GetFQName(), 112 | "DELETE", 113 | nil, 114 | }) 115 | if err != nil { 116 | glog.Errorf("Failed to remove pod(%s) from vrouter(%s): %v", pod.Name, pod.Status.HostIP, err) 117 | return false 118 | } 119 | 120 | return true 121 | } 122 | -------------------------------------------------------------------------------- /pkg/network/opencontrail/config_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015 Juniper Networks, Inc. All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package opencontrail 18 | 19 | import ( 20 | "bytes" 21 | "regexp" 22 | "strings" 23 | "testing" 24 | 25 | "github.com/stretchr/testify/assert" 26 | ) 27 | 28 | func TestParse(t *testing.T) { 29 | assert := assert.New(t) 30 | config := NewConfig() 31 | assert.Equal("localhost", config.ApiAddress) 32 | config.Parse([]string{"--portal_net=172.12.0.0/16"}) 33 | assert.Equal("172.12.0.0/16", config.ServiceSubnet) 34 | assert.Equal("localhost", config.ApiAddress) 35 | } 36 | 37 | func TestConfigFile(t *testing.T) { 38 | content := ` 39 | [DEFAULT] 40 | service-cluster-ip-range = 10.253.0.0/16 41 | 42 | [opencontrail] 43 | api-server = master 44 | network-label = k8-app 45 | public-ip-range = 192.168.0.0/24 46 | ` 47 | buffer := bytes.NewBufferString(content) 48 | config := NewConfig() 49 | err := config.ReadConfiguration(nil, buffer) 50 | if err != nil { 51 | t.Fatal(err) 52 | } 53 | if config.ApiAddress != "master" { 54 | t.Errorf("expected master, got %s", config.ApiAddress) 55 | } 56 | if config.NetworkTag != "k8-app" { 57 | t.Errorf("expected k8-app, got %s", config.NetworkTag) 58 | } 59 | if config.PublicSubnet != "192.168.0.0/24" { 60 | t.Errorf("expected 192.168.0.0/24, got %s", config.PublicSubnet) 61 | } 62 | } 63 | 64 | func TestConfigClusterServices(t *testing.T) { 65 | content := ` 66 | [opencontrail] 67 | cluster-service = kube-system/dns 68 | cluster-service = kube-system/monitoring 69 | ` 70 | buffer := bytes.NewBufferString(content) 71 | config := NewConfig() 72 | err := config.ReadConfiguration(nil, buffer) 73 | if err != nil { 74 | t.Fatal(err) 75 | } 76 | 77 | if len(config.ClusterServices) != 2 { 78 | t.Errorf("expected 2 entries in cluster-services list, got %d", len(config.ClusterServices)) 79 | } 80 | values := []string{"dns", "monitoring"} 81 | for i, v := range values { 82 | fqn := strings.Split(config.ClusterServices[i], "/") 83 | if fqn[len(fqn)-1] != v { 84 | t.Errorf("expected %s, got %s", v, fqn[len(fqn)-1]) 85 | } 86 | } 87 | } 88 | 89 | func TestConfigGlobalNetworks(t *testing.T) { 90 | content := ` 91 | [opencontrail] 92 | global-network = default-domain:default-project:Public 93 | global-network = default-domain:default:logging 94 | global-connect-include = "project-.*" 95 | global-connect-exclude = "kube-system/.*" 96 | ` 97 | buffer := bytes.NewBufferString(content) 98 | config := NewConfig() 99 | err := config.ReadConfiguration(nil, buffer) 100 | if err != nil { 101 | t.Fatal(err) 102 | } 103 | 104 | if len(config.GlobalNetworks) != 2 { 105 | t.Errorf("expected 2 entries in global-network list, got %d", len(config.GlobalNetworks)) 106 | } 107 | 108 | re, err := regexp.Compile(config.GlobalConnectInclude) 109 | if err != nil { 110 | t.Errorf("global-connect-include: %+v", err) 111 | } 112 | value := "project-a/default" 113 | if !re.Match([]byte(value)) { 114 | t.Errorf("expected regexp match on %s, failed", value) 115 | } 116 | 117 | re, err = regexp.Compile(config.GlobalConnectExclude) 118 | if err != nil { 119 | t.Errorf("global-connect-exclude: %+v", err) 120 | } 121 | value = "kube-system/monitoring" 122 | if !re.Match([]byte(value)) { 123 | t.Errorf("expected regexp match on %s, failed", value) 124 | } 125 | } 126 | 127 | func TestGlobalNetworkNameCheck(t *testing.T) { 128 | illegalNames := []string{ 129 | "domain:project", 130 | "domain::name", 131 | "::", 132 | } 133 | for _, v := range illegalNames { 134 | assert.Error(t, validateColonSeparatedNetworkName(v)) 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /scripts/opencontrail-install/ubuntu1404/install.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | @version = "2.20" # master 4 | 5 | @common_packages = [ 6 | "curl", 7 | "gdebi-core", 8 | "git", 9 | "gcc", 10 | "python-lxml", 11 | "python-setuptools", 12 | "software-properties-common", 13 | "sshpass", 14 | "strace", 15 | "tcpdump", 16 | "unzip", 17 | "vim", 18 | "wget", 19 | ] 20 | 21 | # Download and extract contrail software which are missing in ppa 22 | def download_contrail_software 23 | sh("wget -qO - https://github.com/rombie/opencontrail-packages/raw/R#{@version}/ubuntu1404/contrail.tar.xz | tar Jx", false, 5) 24 | end 25 | 26 | # Install from /cs-shared/builder/cache/centoslinux70/icehouse 27 | def install_thirdparty_software_controller 28 | sh("apt-get -y install #{@common_packages.join(" ")}") 29 | end 30 | 31 | # Install contrail controller software 32 | def install_contrail_software_controller 33 | sh("wget -q -O - https://launchpad.net/~opencontrail/+archive/ubuntu/ppa/+files/nodejs_0.8.15-1contrail1_amd64.deb > nodejs_0.8.15-1contrail1_amd64.deb") 34 | sh("gdebi -n nodejs_0.8.15-1contrail1_amd64.deb") 35 | 36 | sh("curl -sL http://debian.datastax.com/debian/repo_key|sudo apt-key add -") 37 | sh(%{sh -c 'echo "deb http://debian.datastax.com/community/ stable main" >> /etc/apt/sources.list'}) 38 | sh("add-apt-repository -y ppa:opencontrail/ppa") 39 | sh("add-apt-repository -y ppa:anantha-l/opencontrail-#{@version}") 40 | sh("apt-get -y --allow-unauthenticated update") 41 | sh("apt-get -y install --allow-unauthenticated openjdk-7-jre-headless=7u51-2.4.6-1ubuntu4") # openjdk-7-jre 42 | sh("apt-get -y --allow-unauthenticated install cassandra", true) 43 | 44 | # In certain instances such as aws, extra storage disk is at a different 45 | # mount point 46 | if @opt.cassandra_db_path != "/var/lib/cassandra" 47 | old_cassandra_dir = "/var/lib/cassandra/".gsub(/\//, '\/') 48 | new_cassandra_dir = "#{@opt.cassandra_db_path}/" 49 | sh("mkdir -p #{new_cassandra_dir}") 50 | sh("chown -R cassandra.cassandra #{new_cassandra_dir}") 51 | new_cassandra_dir.gsub!(/\//, '\/') 52 | sh(%{sed -i 's/#{old_cassandra_dir}/#{new_cassandra_dir}/' /etc/cassandra/cassandra.yaml}) 53 | end 54 | sh(%{sed -i 's/start_rpc: false/start_rpc: true/' /etc/cassandra/cassandra.yaml}) 55 | sh("service cassandra restart") 56 | 57 | sh("apt-get -y --allow-unauthenticated install contrail-analytics contrail-config contrail-control contrail-web-controller contrail-dns contrail-utils zookeeperd rabbitmq-server ifmap-server") 58 | sh("gdebi -n contrail-setup_*.deb") 59 | 60 | # Update time-zone 61 | sh("echo 'America/Los_Angeles' > /etc/timezone") 62 | sh("dpkg-reconfigure -f noninteractive tzdata") 63 | end 64 | 65 | def create_vhost_interface(ip, mask, gw) 66 | intf = "/etc/network/interfaces" 67 | `\grep vhost0 #{intf} 2>&1 > /dev/null` 68 | return if $?.to_i == 0 69 | 70 | ifcfg = < /proc/sys/vm/drop_caches") 89 | sh("add-apt-repository -y ppa:opencontrail/ppa") 90 | sh("add-apt-repository -y ppa:anantha-l/opencontrail-#{@version}") 91 | sh("apt-get -y --allow-unauthenticated update") 92 | sh("apt-get -y --allow-unauthenticated install contrail-vrouter-agent contrail-vrouter-utils contrail-utils python-contrail-vrouter-api") 93 | 94 | # Install contrail-vrouter-init and contrail-setup packages also 95 | sh("gdebi -n contrail-vrouter-init_*.deb") 96 | sh("gdebi -n contrail-setup_*.deb") 97 | 98 | # Update time-zone 99 | sh("echo 'America/Los_Angeles' > /etc/timezone") 100 | sh("dpkg-reconfigure -f noninteractive tzdata") 101 | end 102 | -------------------------------------------------------------------------------- /pkg/network/opencontrail/opencontrail.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015 Juniper Networks, Inc. All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package opencontrail 18 | 19 | import ( 20 | "io" 21 | "time" 22 | 23 | "github.com/golang/glog" 24 | 25 | "k8s.io/kubernetes/pkg/client/cache" 26 | kubeclient "k8s.io/kubernetes/pkg/client/unversioned" 27 | 28 | "github.com/Juniper/contrail-go-api" 29 | "github.com/Juniper/contrail-kubernetes/pkg/network" 30 | ) 31 | 32 | const ( 33 | MetadataAnnotationTag = "opencontrail.org/pod-state" 34 | 35 | servicePolicyPrefix = "__svc_" 36 | networkPolicyPrefix = "__net_" 37 | 38 | DefaultPodNetworkName = "cluster-network" 39 | DefaultServiceDomainName = "default-domain" 40 | DefaultServiceProjectName = "default" 41 | ClusterNetworkName = "cluster-network" 42 | ClusterServiceNetworkName = DefaultServiceDomainName + ":" + DefaultServiceProjectName + 43 | ":" + ClusterNetworkName 44 | ) 45 | 46 | // InstanceMetadata contains the information required in the kubernetes minion to 47 | // connect the Pod interface to the network. 48 | type InstanceMetadata struct { 49 | // Interface uuid 50 | Uuid string `json:"uuid"` 51 | 52 | // The OpenContrail vrouter verifies the source-mac of the virtual interface. 53 | MacAddress string `json:"macAddress"` 54 | 55 | // Private IP address 56 | IpAddress string `json:"ipAddress"` 57 | 58 | // Default gateway (VRouter address) 59 | Gateway string `json:"gateway"` 60 | } 61 | 62 | // makeSyncController creates a controller that uses a non-buffered (synchronous channel) 63 | // This is used in unit tests to avoid timing problems. 64 | func makeSyncController(kube kubeclient.Interface, config *Config) *Controller { 65 | controller := new(Controller) 66 | controller.eventChannel = make(chan notification) 67 | controller.kube = kube 68 | controller.config = config 69 | return controller 70 | } 71 | 72 | func NewController(kube *kubeclient.Client, args []string) network.NetworkController { 73 | controller := new(Controller) 74 | controller.eventChannel = make(chan notification, 32) 75 | controller.kube = kube 76 | controller.config = NewConfig() 77 | controller.config.Parse(args) 78 | return controller 79 | } 80 | 81 | func (c *Controller) initComponents(client contrail.ApiClient) { 82 | c.client = client 83 | c.namespaceMgr = NewNamespaceManager(client, c.config, c.kube) 84 | c.allocator = NewAddressAllocator(client, c.config) 85 | c.instanceMgr = NewInstanceManager(client, c.config, c.allocator) 86 | c.networkMgr = NewNetworkManager(client, c.config) 87 | c.serviceMgr = NewServiceManager(client, c.config, c.networkMgr, c.kube) 88 | 89 | virtualRouterMgr, err := NewVirtualRouterManager(client, c.config) 90 | if err != nil { 91 | glog.Errorf("NewVirtualRouterManager failed: %v", err) 92 | return 93 | } 94 | c.virtualRouterMgr = virtualRouterMgr 95 | } 96 | 97 | func (c *Controller) Init(global *network.Config, reader io.Reader) error { 98 | err := c.config.ReadConfiguration(global, reader) 99 | if err != nil { 100 | glog.Error(err) 101 | } 102 | 103 | glog.Infof("Starting opencontrail plugin") 104 | glog.Infof("Private Subnet: %s", c.config.PrivateSubnet) 105 | glog.Infof("Services Subnet: %s", c.config.ServiceSubnet) 106 | glog.Infof("Public Subnet: %s", c.config.PublicSubnet) 107 | 108 | client := contrail.NewClient(c.config.ApiAddress, c.config.ApiPort) 109 | c.initComponents(client) 110 | c.consistencyPeriod = time.Duration(1) * time.Minute 111 | 112 | return nil 113 | } 114 | 115 | func (c *Controller) SetNamespaceStore(store cache.Store) { 116 | // c.NamespaceStore = store 117 | } 118 | 119 | func (c *Controller) SetPodStore(store cache.Indexer) { 120 | c.podStore = store 121 | } 122 | 123 | func (c *Controller) SetReplicationControllerStore(store cache.Store) { 124 | // c.RCStore = store 125 | } 126 | 127 | func (c *Controller) SetServiceStore(store cache.Store) { 128 | c.serviceStore = store 129 | } 130 | 131 | func init() { 132 | network.Register("opencontrail", NewController) 133 | } 134 | -------------------------------------------------------------------------------- /pkg/network/opencontrail/mocks/KubeClient.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015 Juniper Networks, Inc. All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package mocks 18 | 19 | import ( 20 | "github.com/emicklei/go-restful/swagger" 21 | 22 | "k8s.io/kubernetes/pkg/api" 23 | "k8s.io/kubernetes/pkg/api/unversioned" 24 | kubeclient "k8s.io/kubernetes/pkg/client/unversioned" 25 | "k8s.io/kubernetes/pkg/client/typed/discovery" 26 | "k8s.io/kubernetes/pkg/version" 27 | ) 28 | 29 | type KubeClient struct { 30 | podMocks map[string]*KubePodInterface 31 | serviceMocks map[string]*KubeServiceInterface 32 | NamespaceInterface *KubeNamespaceInterface 33 | } 34 | 35 | func NewKubeClient() *KubeClient { 36 | client := new(KubeClient) 37 | client.podMocks = make(map[string]*KubePodInterface, 0) 38 | client.serviceMocks = make(map[string]*KubeServiceInterface, 0) 39 | client.NamespaceInterface = new(KubeNamespaceInterface) 40 | return client 41 | } 42 | 43 | func (m *KubeClient) Pods(namespace string) kubeclient.PodInterface { 44 | pods, ok := m.podMocks[namespace] 45 | if !ok { 46 | pods = new(KubePodInterface) 47 | m.podMocks[namespace] = pods 48 | } 49 | return pods 50 | } 51 | func (m *KubeClient) ReplicationControllers(namespace string) kubeclient.ReplicationControllerInterface { 52 | return nil 53 | } 54 | func (m *KubeClient) Services(namespace string) kubeclient.ServiceInterface { 55 | services, ok := m.serviceMocks[namespace] 56 | if !ok { 57 | services = new(KubeServiceInterface) 58 | m.serviceMocks[namespace] = services 59 | } 60 | return services 61 | } 62 | 63 | func (m *KubeClient) Endpoints(namespace string) kubeclient.EndpointsInterface { 64 | return nil 65 | } 66 | 67 | func (m *KubeClient) Events(namespace string) kubeclient.EventInterface { 68 | return nil 69 | } 70 | func (c *KubeClient) Nodes() kubeclient.NodeInterface { 71 | return nil 72 | } 73 | func (c *KubeClient) LimitRanges(namespace string) kubeclient.LimitRangeInterface { 74 | return nil 75 | } 76 | 77 | func (c *KubeClient) ResourceQuotas(namespace string) kubeclient.ResourceQuotaInterface { 78 | return nil 79 | } 80 | 81 | func (c *KubeClient) Secrets(namespace string) kubeclient.SecretsInterface { 82 | return nil 83 | } 84 | 85 | func (c *KubeClient) Namespaces() kubeclient.NamespaceInterface { 86 | return c.NamespaceInterface 87 | } 88 | 89 | func (c *KubeClient) PersistentVolumes() kubeclient.PersistentVolumeInterface { 90 | return nil 91 | } 92 | 93 | func (c *KubeClient) PersistentVolumeClaims(namespace string) kubeclient.PersistentVolumeClaimInterface { 94 | return nil 95 | } 96 | func (c *KubeClient) ServerVersion() (*version.Info, error) { 97 | return nil, nil 98 | } 99 | func (c *KubeClient) ServerAPIVersions() (*unversioned.APIVersions, error) { 100 | return nil, nil 101 | } 102 | func (c *KubeClient) ComponentStatuses() kubeclient.ComponentStatusInterface { 103 | return nil 104 | } 105 | func (c *KubeClient) ConfigMaps(namespace string) kubeclient.ConfigMapsInterface { 106 | return nil 107 | } 108 | func (c *KubeClient) PodTemplates(namespace string) kubeclient.PodTemplateInterface { 109 | return nil 110 | } 111 | func (c *KubeClient) ServiceAccounts(namespace string) kubeclient.ServiceAccountsInterface { 112 | return nil 113 | } 114 | 115 | func (c *KubeClient) ValidateComponents() (*api.ComponentStatusList, error) { 116 | return nil, nil 117 | } 118 | 119 | func (c *KubeClient) SwaggerSchema(version string) (*swagger.ApiDeclaration, error) { 120 | return nil, nil 121 | } 122 | 123 | func (c *KubeClient) Extensions() kubeclient.ExtensionsInterface { 124 | return nil 125 | } 126 | 127 | func (c *KubeClient) Autoscaling() kubeclient.AutoscalingInterface { 128 | return nil 129 | } 130 | 131 | func (c *KubeClient) Batch() kubeclient.BatchInterface { 132 | return nil 133 | } 134 | 135 | func (c *KubeClient) Discovery() discovery.DiscoveryInterface { 136 | return nil 137 | } 138 | 139 | func (m *KubeServiceInterface) UpdateStatus(srv *api.Service) (*api.Service, error) { 140 | ret := m.Called(srv) 141 | var r0 *api.Service 142 | if ret.Get(0) != nil { 143 | r0 = ret.Get(0).(*api.Service) 144 | } 145 | r1 := ret.Error(1) 146 | return r0, r1 147 | } 148 | -------------------------------------------------------------------------------- /pkg/network/opencontrail/policy.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016 Juniper Networks, Inc. All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package opencontrail 18 | 19 | import ( 20 | "strings" 21 | 22 | "github.com/golang/glog" 23 | 24 | "github.com/Juniper/contrail-go-api" 25 | "github.com/Juniper/contrail-go-api/types" 26 | ) 27 | 28 | func locatePolicy(client contrail.ApiClient, policyName[] string) (*types.NetworkPolicy, error) { 29 | var policy *types.NetworkPolicy = nil 30 | obj, err := client.FindByName("network-policy", strings.Join(policyName, ":")) 31 | if err != nil { 32 | policy = new(types.NetworkPolicy) 33 | policy.SetFQName("project", policyName) 34 | err = client.Create(policy) 35 | if err != nil { 36 | glog.Errorf("Create policy %s: %v", strings.Join(policyName, ":"), err) 37 | return nil, err 38 | } 39 | } else { 40 | policy = obj.(*types.NetworkPolicy) 41 | } 42 | return policy, nil 43 | } 44 | 45 | func policyLocateRule(client contrail.ApiClient, policy *types.NetworkPolicy, lhs, rhs *types.VirtualNetwork) error { 46 | return policyLocateRuleByFQN(client, policy, lhs.GetFQName(), rhs.GetFQName()) 47 | } 48 | 49 | func policyLocateRuleByFQN(client contrail.ApiClient, policy *types.NetworkPolicy, lhsFQN, rhsFQN []string) error { 50 | lhsName := strings.Join(lhsFQN, ":") 51 | rhsName := strings.Join(rhsFQN, ":") 52 | entries := policy.GetNetworkPolicyEntries() 53 | for _, rule := range entries.PolicyRule { 54 | if rule.SrcAddresses[0].VirtualNetwork == lhsName && 55 | rule.DstAddresses[0].VirtualNetwork == rhsName { 56 | return nil 57 | } 58 | } 59 | rule := new(types.PolicyRuleType) 60 | rule.Protocol = "any" 61 | rule.Direction = "<>" 62 | rule.SrcAddresses = []types.AddressType{types.AddressType{ 63 | VirtualNetwork: lhsName, 64 | }} 65 | rule.DstAddresses = []types.AddressType{types.AddressType{ 66 | VirtualNetwork: rhsName, 67 | }} 68 | rule.SrcPorts = []types.PortType{types.PortType{StartPort: -1, EndPort: -1}} 69 | rule.DstPorts = []types.PortType{types.PortType{StartPort: -1, EndPort: -1}} 70 | rule.ActionList = &types.ActionListType{ 71 | SimpleAction: "pass", 72 | } 73 | 74 | entries.AddPolicyRule(rule) 75 | policy.SetNetworkPolicyEntries(&entries) 76 | err := client.Update(policy) 77 | if err != nil { 78 | glog.Errorf("policy-rule: %v", err) 79 | return err 80 | } 81 | return nil 82 | } 83 | 84 | func removeRulesIndex(rules []types.PolicyRuleType, index int) []types.PolicyRuleType { 85 | rules[index], rules = rules[len(rules)-1], rules[:len(rules)-1] 86 | return rules 87 | } 88 | 89 | func policyDeleteRule(client contrail.ApiClient, policy *types.NetworkPolicy, lhsName, rhsName string) error { 90 | entries := policy.GetNetworkPolicyEntries() 91 | var index int = -1 92 | for i, rule := range entries.PolicyRule { 93 | if rule.SrcAddresses[0].VirtualNetwork == lhsName && 94 | rule.DstAddresses[0].VirtualNetwork == rhsName { 95 | index = i 96 | break 97 | } 98 | } 99 | if index < 0 { 100 | return nil 101 | } 102 | entries.PolicyRule = removeRulesIndex(entries.PolicyRule, index) 103 | policy.SetNetworkPolicyEntries(&entries) 104 | err := client.Update(policy) 105 | if err != nil { 106 | glog.Errorf("policy-rule: %v", err) 107 | } 108 | return err 109 | } 110 | 111 | func policyAttach(client contrail.ApiClient, network *types.VirtualNetwork, policy *types.NetworkPolicy) error { 112 | refs, err := network.GetNetworkPolicyRefs() 113 | if err != nil { 114 | glog.Errorf("get network policy-refs %s: %v", network.GetName(), err) 115 | return err 116 | } 117 | for _, ref := range refs { 118 | if ref.Uuid == policy.GetUuid() { 119 | return nil 120 | } 121 | } 122 | network.AddNetworkPolicy(policy, 123 | types.VirtualNetworkPolicyType{ 124 | Sequence: &types.SequenceType{Major: 10, Minor: 0}, 125 | }) 126 | err = client.Update(network) 127 | if err != nil { 128 | glog.Errorf("Update network %s policies: %v", network.GetName(), err) 129 | return err 130 | } 131 | return nil 132 | } 133 | 134 | func policyDetach(client contrail.ApiClient, network *types.VirtualNetwork, policyName string) error { 135 | refs, err := network.GetNetworkPolicyRefs() 136 | if err != nil { 137 | glog.Errorf("get network policy-refs %s: %v", network.GetName(), err) 138 | return err 139 | } 140 | for _, ref := range refs { 141 | if strings.Join(ref.To, ":") == policyName { 142 | network.DeleteNetworkPolicy(ref.Uuid) 143 | err := client.Update(network) 144 | if err != nil { 145 | glog.Errorf("Update network %s policies: %v", network.GetName(), err) 146 | } 147 | return err 148 | } 149 | } 150 | return nil 151 | } 152 | -------------------------------------------------------------------------------- /pkg/network/opencontrail/network_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015 Juniper Networks, Inc. All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package opencontrail 18 | 19 | import ( 20 | "fmt" 21 | "strings" 22 | "testing" 23 | 24 | "github.com/pborman/uuid" 25 | "github.com/stretchr/testify/assert" 26 | "github.com/stretchr/testify/require" 27 | 28 | "github.com/Juniper/contrail-go-api" 29 | "github.com/Juniper/contrail-go-api/types" 30 | 31 | contrail_mocks "github.com/Juniper/contrail-go-api/mocks" 32 | ) 33 | 34 | type FloatingIpInterceptor struct { 35 | count int 36 | } 37 | 38 | func (i *FloatingIpInterceptor) Put(ptr contrail.IObject) { 39 | } 40 | 41 | func (i *FloatingIpInterceptor) Get(ptr contrail.IObject) { 42 | fip := ptr.(*types.FloatingIp) 43 | if fip.GetFloatingIpAddress() == "" { 44 | i.count += 1 45 | fip.SetFloatingIpAddress(fmt.Sprintf("100.64.%d.%d", i.count/256, i.count&0xff)) 46 | } 47 | } 48 | 49 | func TestNetworkLocate(t *testing.T) { 50 | client := new(contrail_mocks.ApiClient) 51 | client.Init() 52 | 53 | config := new(Config) 54 | config.PublicNetwork = "default-domain:default-project:Public" 55 | netman := NewNetworkManager(client, config) 56 | 57 | project := new(types.Project) 58 | project.SetUuid(uuid.New()) 59 | project.SetFQName("", []string{config.DefaultDomain, "p1"}) 60 | client.Create(project) 61 | 62 | network, err := netman.LocateNetwork("p1", "n1", "10.0.1.0/24") 63 | assert.NoError(t, err, "LocateNetwork") 64 | 65 | n2, err := netman.LocateNetwork("p1", "n1", "10.0.1.0/24") 66 | assert.NoError(t, err, "LocateNetwork -- exists") 67 | assert.Equal(t, network, n2) 68 | } 69 | 70 | // The Public network subnet configuration may change. 71 | func TestPublicNetworkSubnetChange(t *testing.T) { 72 | client := new(contrail_mocks.ApiClient) 73 | client.Init() 74 | 75 | config := new(Config) 76 | config.PublicNetwork = "default-domain:default-project:Public" 77 | config.PublicSubnet = "192.0.2.0/24" 78 | netman := NewNetworkManager(client, config) 79 | 80 | // No public IP addresses are assigned: expect the public network to 81 | // have a single subnet. 82 | config.PublicSubnet = "198.51.100.0/24" 83 | netman = NewNetworkManager(client, config) 84 | 85 | network := netman.GetPublicNetwork() 86 | refs, err := network.GetNetworkIpamRefs() 87 | require.NoError(t, err) 88 | assert.Len(t, refs, 1) 89 | if len(refs) > 0 { 90 | attr := refs[0].Attr.(types.VnSubnetsType) 91 | assert.Len(t, attr.IpamSubnets, 1) 92 | if len(attr.IpamSubnets) > 0 { 93 | prefix := fmt.Sprintf("%s/%d", 94 | attr.IpamSubnets[0].Subnet.IpPrefix, 95 | attr.IpamSubnets[0].Subnet.IpPrefixLen) 96 | assert.Equal(t, config.PublicSubnet, prefix) 97 | } 98 | } 99 | } 100 | 101 | // When the subnet is in use it can't be deleted. 102 | func TestPublicNetworkSubnetChangeWhenInUse(t *testing.T) { 103 | client := new(contrail_mocks.ApiClient) 104 | client.Init() 105 | 106 | config := new(Config) 107 | config.PublicNetwork = "default-domain:default-project:Public" 108 | config.PublicSubnet = "192.0.2.0/24" 109 | netman := NewNetworkManager(client, config) 110 | _, err := netman.LocateFloatingIp(netman.GetPublicNetwork(), "test", "192.0.2.1") 111 | require.NoError(t, err) 112 | 113 | config.PublicSubnet = "198.51.100.0/24" 114 | netman = NewNetworkManager(client, config) 115 | 116 | network := netman.GetPublicNetwork() 117 | refs, err := network.GetNetworkIpamRefs() 118 | require.NoError(t, err) 119 | assert.Len(t, refs, 1) 120 | if len(refs) > 0 { 121 | attr := refs[0].Attr.(types.VnSubnetsType) 122 | assert.Len(t, attr.IpamSubnets, 2) 123 | } 124 | netman.DeleteFloatingIp(network, "test") 125 | } 126 | 127 | func TestGlobalNetworkMatch(t *testing.T) { 128 | testCases := []struct { 129 | include string 130 | exclude string 131 | result bool 132 | }{ 133 | {".*", "", true}, 134 | {".*", "testns/.*", false}, 135 | {"(testns|foo)/.*", "", true}, 136 | {"foo/.*", "", false}, 137 | } 138 | 139 | for _, test := range testCases { 140 | config := new(Config) 141 | config.GlobalConnectInclude = test.include 142 | config.GlobalConnectExclude = test.exclude 143 | result := networkAccessGlobalNetworks(config, []string{"default-domain", "testns", "x"}) 144 | if test.result != result { 145 | t.Errorf("Include: '%s' Exclude: '%s'", test.include, test.exclude) 146 | } 147 | } 148 | 149 | config := new(Config) 150 | config.GlobalNetworks = []string{"default-domain:a:b", "default-domain:foo:bar"} 151 | assert.True(t, networkAccessGlobalNetworks(config, []string{"default-domain", "a", "x"})) 152 | assert.False(t, networkAccessGlobalNetworks(config, strings.Split(config.GlobalNetworks[1], ":"))) 153 | } 154 | -------------------------------------------------------------------------------- /pkg/network/opencontrail/cni/args/args.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016 Juniper Networks, Inc. All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | /* Common code to parse agruments for both Kubernetes and Mesos. */ 18 | package args 19 | 20 | import ( 21 | "../agent" 22 | "encoding/json" 23 | "fmt" 24 | "github.com/containernetworking/cni/pkg/skel" 25 | "github.com/containernetworking/cni/pkg/types" 26 | "net" 27 | "strings" 28 | ) 29 | 30 | /**************************************************************************** 31 | * Argument handling 32 | ****************************************************************************/ 33 | const modek8s = "kubernetes" 34 | const modeMesos = "mesos" 35 | const k8sDir = "/var/contrail/k8s" 36 | const mesosDir = "/var/contrail/mesos" 37 | const pollTimeout = "100ms" 38 | const pollRetries = 10 39 | const vrouterIp = "127.0.0.1" 40 | const vrouterPort = 9091 41 | const logDir = "/var/log/contrail/cni" 42 | const logLevel = "0" 43 | 44 | /* Example configuration for Contrail 45 | { 46 | "cniVersion": "0.2.0", 47 | "contrail" : { 48 | "vrouter" : { 49 | "ip" : "127.0.0.1", 50 | "port" : 9091, 51 | "timeout" : "2000ms" 52 | "retries" : 10 53 | }, 54 | "mode" : "k8s" 55 | "mtu" : 1500 56 | "dir" : "/var/contrail/vm", 57 | "log" : { 58 | "dir" : "/var/log/contrail/cni", 59 | "level" : "2" 60 | }, 61 | }, 62 | 63 | "name": "contrail", 64 | "type": "contrail" 65 | } 66 | */ 67 | 68 | // Logging arguments 69 | type LoggingArgs struct { 70 | Dir string `json:"dir"` 71 | Level string `json:"level"` 72 | } 73 | 74 | // Definitions to get VRouter related parameters from json data 75 | type VRouterArgs struct { 76 | Ip string `json:"ip"` 77 | Port int `json:"port"` 78 | PollTimeout string `json:"pollTimeout"` 79 | PollRetries int `json:"pollRetries"` 80 | } 81 | 82 | // Contrail specific arguments 83 | type ContrailArgs struct { 84 | VRouterArgs VRouterArgs `json:"vrouter"` 85 | Mode string `json:"mode"` 86 | Mtu int `json:"mtu"` 87 | Dir string `json:"dir"` 88 | Logging LoggingArgs `json:"log"` 89 | } 90 | 91 | // Kubernetes specific arguments 92 | type K8SArgs struct { 93 | NameSpace string 94 | PodName string 95 | } 96 | 97 | // Definition of json data in STDIN 98 | type CniArgs struct { 99 | ContrailArgs ContrailArgs `json:"contrail"` 100 | ContainerID string 101 | IfName string 102 | Netns string 103 | K8SArgs K8SArgs 104 | } 105 | 106 | /* 107 | * Get Kubernetes specific arguments 108 | * Format of arguments in case of kubernetes is 109 | * "IgnoreUnknown=1;K8S_POD_NAMESPACE=default;K8S_POD_NAME=hello-world-1-81nl8; 110 | * K8S_POD_INFRA_CONTAINER_ID=" 111 | */ 112 | func (k8sArgs *K8SArgs) getK8sArgs(args *skel.CmdArgs) { 113 | cniArgs := strings.Split(args.Args, ";") 114 | for _, v := range cniArgs { 115 | a := strings.Split(v, "=") 116 | if len(a) >= 2 { 117 | if a[0] == "K8S_POD_NAMESPACE" { 118 | k8sArgs.NameSpace = a[1] 119 | } 120 | if a[0] == "K8S_POD_NAME" { 121 | k8sArgs.PodName = a[1] 122 | } 123 | } 124 | } 125 | } 126 | 127 | // Fetch all parameters. Includes parameters from STDIN and Environemnt vars 128 | func Get(args *skel.CmdArgs) (*CniArgs, error) { 129 | // Set defaults 130 | vrouterArgs := VRouterArgs{Ip: vrouterIp, Port: vrouterPort, 131 | PollTimeout: pollTimeout, PollRetries: pollRetries} 132 | logArgs := LoggingArgs{Dir: logDir, Level: logLevel} 133 | contrailArgs := ContrailArgs{VRouterArgs: vrouterArgs, Dir: k8sDir, 134 | Logging: logArgs} 135 | cniArgs := &CniArgs{ContrailArgs: contrailArgs} 136 | 137 | // Parse json data 138 | if err := json.Unmarshal(args.StdinData, cniArgs); err != nil { 139 | msg := fmt.Sprintf("Invalid JSon string. Error %v : Message %s\n", 140 | err, args.StdinData) 141 | return nil, fmt.Errorf(msg) 142 | } 143 | 144 | cniArgs.ContainerID = args.ContainerID 145 | cniArgs.Netns = args.Netns 146 | cniArgs.IfName = args.IfName 147 | // Kubernetes specific parameters are passed in environment variable. 148 | // Get Kubernetes specific parameters from environment variable 149 | cniArgs.K8SArgs.getK8sArgs(args) 150 | return cniArgs, nil 151 | } 152 | 153 | // Convert cniArgs from VRouter format to CNI format 154 | func VRouterResultToCniResult(agent *agent.Result) *types.Result { 155 | mask := net.CIDRMask(agent.Plen, 32) 156 | ipv4 := types.IPConfig{IP: net.IPNet{IP: net.ParseIP(agent.Ip), 157 | Mask: mask}, Gateway: net.ParseIP(agent.Gw)} 158 | result := &types.Result{IP4: &ipv4} 159 | 160 | _, defaultNet, err := net.ParseCIDR("0.0.0.0/0") 161 | if err != nil { 162 | return nil 163 | } 164 | result.IP4.Routes = append(result.IP4.Routes, 165 | types.Route{Dst: *defaultNet, GW: result.IP4.Gateway}) 166 | 167 | result.DNS.Nameservers = append(result.DNS.Nameservers, agent.Dns) 168 | return result 169 | } 170 | -------------------------------------------------------------------------------- /cluster/provision_master.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -ex 4 | set -o pipefail 5 | 6 | LOG_FILE=/var/log/contrail/provision_master.log 7 | mkdir -p /var/log/contrail 8 | exec 1<&- # Close STDOUT file descriptor 9 | exec 2<&- # Close STDERR FD 10 | exec 1<>$LOG_FILE # Open STDOUT as $LOG_FILE file for read and write. 11 | exec 2>&1 # Redirect STDERR to STDOUT 12 | 13 | # contrail-kubernetes setup and provisioning script. For more info, please refer to 14 | # https://github.com/Juniper/contrail-kubernetes 15 | 16 | # Retry a command $RETRY times with $WAIT seconds delay in between 17 | function retry() { 18 | set +e 19 | 20 | CMD=$1 21 | echo $CMD 22 | shift 23 | 24 | if [ -z $RETRY ]; then 25 | RETRY=10 26 | fi 27 | if [ -z $WAIT ]; then 28 | WAIT=10 29 | fi 30 | 31 | COUNTER=0 32 | while [ $COUNTER -lt $RETRY ]; do 33 | $CMD $* 34 | if [ "$?" = "0" ]; then 35 | return 36 | fi 37 | let COUNTER=COUNTER+1 38 | echo Try again $COUNTER/$RETRY 39 | sleep $WAIT 40 | done 41 | 42 | set -e 43 | 44 | echo "Error EXIT: $CMD $* Failed with exit code $?" 45 | exit -1 46 | } 47 | 48 | # Run a command in kubernetes-master node 49 | function master() { 50 | bash -c "sudo $*" 51 | } 52 | 53 | # Verify that contrail infra components are up and listening 54 | function verify_contrail_listen_services() { 55 | RETRY=20 56 | WAIT=3 57 | retry master 'netstat -anp | grep LISTEN | grep -w 5672' # RabbitMQ 58 | retry master 'netstat -anp | grep LISTEN | grep -w 2181' # ZooKeeper 59 | retry master 'netstat -anp | grep LISTEN | grep -w 9160' # Cassandra 60 | retry master 'netstat -anp | grep LISTEN | grep -w 5269' # XMPP Server 61 | retry master 'netstat -anp | grep LISTEN | grep -w 8083' # Control-Node 62 | retry master 'netstat -anp | grep LISTEN | grep -w 8443' # IFMAP 63 | retry master 'netstat -anp | grep LISTEN | grep -w 8082' # API-Server 64 | retry master 'netstat -anp | grep LISTEN | grep -w 8087' # Schema 65 | retry master 'netstat -anp | grep LISTEN | grep -w 8086' # Collector 66 | retry master 'netstat -anp | grep LISTEN | grep -w 8081' # OpServer 67 | retry master 'netstat -anp | grep LISTEN | grep -w 8091' # query-engine 68 | retry master 'netstat -anp | grep LISTEN | grep -w 6379' # redis 69 | retry master 'netstat -anp | grep LISTEN | grep -w 8143' # WebUI 70 | retry master 'netstat -anp | grep LISTEN | grep -w 8070' # WebUI 71 | retry master 'netstat -anp | grep LISTEN | grep -w 3000' # WebUI 72 | # retry master 'netstat -anp | grep LISTEN | grep -w 5998' # discovery 73 | # retry master 'netstat -anp | grep LISTEN | grep -w 8094' # dns 74 | # retry master 'netstat -anp | grep LISTEN | grep -w 53' # named 75 | } 76 | 77 | # Provision controller 78 | function provision_controller() { 79 | cmd='docker ps | grep contrail-api | grep -v pause | awk "{print \"docker exec \" \$1 \" python /usr/share/contrail-utils/provision_control.py --router_asn 64512 --host_name `hostname` --host_ip `hostname --ip-address` --oper add --api_server_ip `hostname --ip-address` --api_server_port 8082\"}" | sudo sh' 80 | master $cmd 81 | } 82 | 83 | # Provision link local service 84 | function provision_linklocal() { 85 | cmd='docker ps | grep contrail-api | grep -v pause | awk "{print \"docker exec \" \$1 \" python /usr/share/contrail-utils/provision_linklocal.py --api_server_ip `hostname --ip-address` --api_server_port 8082 --linklocal_service_name kubernetes --linklocal_service_ip 10.0.0.1 --linklocal_service_port 8080 --ipfabric_service_ip `hostname --ip-` --ipfabric_service_port 8080 --oper add\"}" | sudo sh' 86 | master $cmd 87 | } 88 | 89 | # Provision vrouter encap order 90 | function provision_vrouter_encap() { 91 | cmd='docker ps | grep contrail-api | grep -v pause | awk "{print \"docker exec \" \$1 \" python /usr/share/contrail-utils/provision_encap.py --api_server_ip `hostname --ip-address` --api_server_port 8082 --encap_priority MPLSoUDP,MPLSoGRE,VXLAN --admin_user myuser --admin_password mypass --oper add\"}" | sudo sh' 92 | master $cmd 93 | } 94 | 95 | # Setup kube dns endpoints 96 | function setup_kube_dns_endpoints() { 97 | master kubectl --namespace=kube-system create -f /etc/kubernetes/addons/kube-ui/kube-ui-endpoint.yaml || true 98 | master kubectl --namespace=kube-system create -f /etc/kubernetes/addons/kube-ui/kube-ui-svc-address.yaml || true 99 | } 100 | 101 | # Setup contrail manifest files under kubernetes 102 | function setup_contrail_manifest_files() { 103 | cmd='wget -qO - https://raw.githubusercontent.com/juniper/contrail-kubernetes/master/cluster/manifests.hash | grep -v kube-network-manager | grep -v contrail-vrouter-agent | grep -v provision | awk "{print \"https://raw.githubusercontent.com/juniper/contrail-kubernetes/master/cluster/\"\$1}" | xargs -n1 sudo wget -q --directory-prefix=/etc/contrail/manifests --continue' 104 | master $cmd 105 | 106 | cmd='grep \"image\": /etc/contrail/manifests/* | cut -d "\"" -f 4 | sort -u | xargs -n1 sudo docker pull' 107 | RETRY=20 108 | WAIT=3 109 | retry master $cmd 110 | cmd='mv /etc/contrail/manifests/* /etc/kubernetes/manifests/' 111 | master $cmd 112 | } 113 | 114 | # Setup contrail-controller components 115 | function setup_contrail_master() { 116 | 117 | # Pull all contrail images and copy the manifest files 118 | setup_contrail_manifest_files 119 | 120 | # Wait for contrail-control to be ready. 121 | verify_contrail_listen_services 122 | 123 | # Provision controller 124 | provision_controller 125 | 126 | # Provision link-local service to connect to kube-api 127 | provision_linklocal 128 | 129 | # Provision vrouter encap order 130 | provision_vrouter_encap 131 | 132 | # Setip kube-dns 133 | setup_kube_dns_endpoints 134 | } 135 | 136 | setup_contrail_master 137 | -------------------------------------------------------------------------------- /pkg/network/opencontrail/controller_dispatch.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015 Juniper Networks, Inc. All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package opencontrail 18 | 19 | import ( 20 | "encoding/json" 21 | "reflect" 22 | 23 | "github.com/golang/glog" 24 | 25 | "k8s.io/kubernetes/pkg/api" 26 | kubetypes "k8s.io/kubernetes/pkg/kubelet/types" 27 | ) 28 | 29 | func EqualTags(m1, m2 map[string]string, tags []string) bool { 30 | if m1 == nil { 31 | return m2 == nil 32 | } 33 | for _, tag := range tags { 34 | if m1[tag] != m2[tag] { 35 | return false 36 | } 37 | } 38 | return true 39 | } 40 | 41 | // IgnorePod returns true if this pod should not be managed by OpenContrail. 42 | // Pods that use host networking on kubelet static pods fall into this category. 43 | func IgnorePod(pod *api.Pod) bool { 44 | context := pod.Spec.SecurityContext 45 | if context != nil && context.HostNetwork { 46 | return true 47 | } 48 | 49 | if _, ok := pod.Annotations[kubetypes.ConfigMirrorAnnotationKey]; ok { 50 | return true 51 | } 52 | return false 53 | } 54 | 55 | func (c *Controller) AddPod(pod *api.Pod) { 56 | if IgnorePod(pod) { 57 | return 58 | } 59 | c.eventChannel <- notification{evAddPod, pod} 60 | } 61 | 62 | func (c *Controller) podAnnotationsCheck(pod *api.Pod) bool { 63 | if pod.Annotations == nil { 64 | return false 65 | } 66 | data, ok := pod.Annotations[MetadataAnnotationTag] 67 | if !ok { 68 | return false 69 | } 70 | 71 | var state InstanceMetadata 72 | err := json.Unmarshal([]byte(data), &state) 73 | if err != nil { 74 | return false 75 | } 76 | 77 | nic := c.instanceMgr.LookupInterface(pod.Namespace, pod.Name) 78 | if nic == nil || nic.GetUuid() != state.Uuid { 79 | return false 80 | } 81 | return true 82 | } 83 | 84 | func (c *Controller) UpdatePod(oldPod, newPod *api.Pod) { 85 | if IgnorePod(newPod) { 86 | if !IgnorePod(oldPod) { 87 | c.eventChannel <- notification{evDeletePod, oldPod} 88 | } else { 89 | glog.V(3).Infof("Update pod %s: ignore", newPod.Name) 90 | } 91 | return 92 | } 93 | 94 | watchTags := []string{ 95 | c.config.NetworkTag, 96 | c.config.NetworkAccessTag, 97 | } 98 | update := false 99 | if !c.podAnnotationsCheck(newPod) { 100 | glog.V(3).Infof("Pod %s: opencontrail annotations not current", newPod.Name) 101 | update = true 102 | } else if !EqualTags(oldPod.Labels, newPod.Labels, watchTags) { 103 | glog.V(3).Infof("Pod %s: labels have changed", newPod.Name) 104 | update = true 105 | } else if !EqualTags(oldPod.Annotations, newPod.Annotations, []string{c.config.NetworkAccessTag}) { 106 | glog.V(3).Infof("Pod %s: annotations have changed", newPod.Name) 107 | update = true 108 | } 109 | if update { 110 | c.eventChannel <- notification{evUpdatePod, newPod} 111 | } 112 | } 113 | 114 | func (c *Controller) DeletePod(pod *api.Pod) { 115 | c.eventChannel <- notification{evDeletePod, pod} 116 | } 117 | 118 | func (c *Controller) AddService(service *api.Service) { 119 | if len(service.Spec.Selector) == 0 { 120 | return 121 | } 122 | 123 | c.eventChannel <- notification{evAddService, service} 124 | } 125 | 126 | func (c *Controller) UpdateService(oldObj, newObj *api.Service) { 127 | update := false 128 | 129 | nLabel, nExists := newObj.Labels[c.config.NetworkTag] 130 | oLabel, oExists := oldObj.Labels[c.config.NetworkTag] 131 | 132 | if nExists != oExists || (nExists && nLabel != oLabel) { 133 | glog.V(3).Infof("Service %s: network-tag changed", newObj.Name) 134 | c.eventChannel <- notification{evDeleteService, oldObj} 135 | update = true 136 | } else if !reflect.DeepEqual(newObj.Spec.Selector, oldObj.Spec.Selector) { 137 | glog.V(3).Infof("Service %s: selector changed", newObj.Name) 138 | update = true 139 | } else if len(newObj.Spec.Selector) == 0 { 140 | return 141 | } 142 | 143 | if newObj.Spec.ClusterIP != oldObj.Spec.ClusterIP { 144 | glog.V(3).Infof("Service %s: clusterIP changed", newObj.Name) 145 | update = true 146 | } 147 | if !reflect.DeepEqual(newObj.Spec.ExternalIPs, oldObj.Spec.ExternalIPs) { 148 | glog.V(3).Infof("Service %s: ExternalIPs changed", newObj.Name) 149 | update = true 150 | } 151 | if newObj.Spec.Type != oldObj.Spec.Type { 152 | update = true 153 | glog.V(3).Infof("Service %s: Type changed", newObj.Name) 154 | } else if newObj.Spec.Type == api.ServiceTypeLoadBalancer && len(newObj.Status.LoadBalancer.Ingress) == 0 { 155 | update = true 156 | glog.V(3).Infof("Service %s: no load balancer status", newObj.Name) 157 | } 158 | if update { 159 | c.eventChannel <- notification{evUpdateService, newObj} 160 | } 161 | } 162 | 163 | func (c *Controller) DeleteService(service *api.Service) { 164 | c.eventChannel <- notification{evDeleteService, service} 165 | } 166 | 167 | func (c *Controller) AddNamespace(namespace *api.Namespace) { 168 | c.eventChannel <- notification{evAddNamespace, namespace} 169 | } 170 | 171 | func (c *Controller) UpdateNamespace(oldObj, newObj *api.Namespace) { 172 | } 173 | 174 | func (c *Controller) DeleteNamespace(namespace *api.Namespace) { 175 | c.eventChannel <- notification{evDeleteNamespace, namespace} 176 | } 177 | 178 | func (c *Controller) AddReplicationController(rc *api.ReplicationController) { 179 | } 180 | 181 | func (c *Controller) UpdateReplicationController(oldObj, newObj *api.ReplicationController) { 182 | } 183 | 184 | func (c *Controller) DeleteReplicationController(rc *api.ReplicationController) { 185 | } 186 | -------------------------------------------------------------------------------- /pkg/network/cni/args/args.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016 Juniper Networks, Inc. All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | /* Common code to parse agruments for both Kubernetes and Mesos. */ 18 | package args 19 | 20 | import ( 21 | "../agent" 22 | "context" 23 | "encoding/json" 24 | "fmt" 25 | "github.com/containernetworking/cni/pkg/skel" 26 | "github.com/containernetworking/cni/pkg/types" 27 | "github.com/docker/docker/client" 28 | "net" 29 | "os" 30 | "strings" 31 | ) 32 | 33 | /**************************************************************************** 34 | * Argument handling 35 | ****************************************************************************/ 36 | const modek8s = "kubernetes" 37 | const modeMesos = "mesos" 38 | const k8sDir = "/var/contrail/k8s" 39 | const mesosDir = "/var/contrail/mesos" 40 | const pollTimeout = "100ms" 41 | const pollRetries = 10 42 | const vrouterIp = "127.0.0.1" 43 | const vrouterPort = 9091 44 | const logDir = "/var/log/contrail/cni" 45 | const logLevel = "0" 46 | 47 | /* Example configuration for Contrail 48 | { 49 | "cniVersion": "0.2.0", 50 | "contrail" : { 51 | "vrouter" : { 52 | "ip" : "127.0.0.1", 53 | "port" : 9091, 54 | "timeout" : "2000ms" 55 | "retries" : 10 56 | }, 57 | "mode" : "k8s" 58 | "mtu" : 1500 59 | "dir" : "/var/contrail/vm", 60 | "log" : { 61 | "dir" : "/var/log/contrail/cni", 62 | "level" : "2" 63 | }, 64 | }, 65 | 66 | "name": "contrail", 67 | "type": "contrail" 68 | } 69 | */ 70 | 71 | // Logging arguments 72 | type LoggingArgs struct { 73 | Dir string `json:"dir"` 74 | Level string `json:"level"` 75 | } 76 | 77 | // Definitions to get VRouter related parameters from json data 78 | type VRouterArgs struct { 79 | Ip string `json:"ip"` 80 | Port int `json:"port"` 81 | PollTimeout string `json:"pollTimeout"` 82 | PollRetries int `json:"pollRetries"` 83 | } 84 | 85 | // Contrail specific arguments 86 | type ContrailArgs struct { 87 | VRouterArgs VRouterArgs `json:"vrouter"` 88 | Mode string `json:"mode"` 89 | Mtu int `json:"mtu"` 90 | Dir string `json:"dir"` 91 | Logging LoggingArgs `json:"log"` 92 | } 93 | 94 | // Kubernetes specific arguments 95 | type K8SArgs struct { 96 | PodUuid string 97 | PodName string 98 | NameSpace string 99 | } 100 | 101 | // Definition of json data in STDIN 102 | type CniArgs struct { 103 | ContrailArgs ContrailArgs `json:"contrail"` 104 | ContainerID string 105 | IfName string 106 | Netns string 107 | K8SArgs K8SArgs 108 | } 109 | 110 | /* 111 | * Get Kubernetes specific arguments 112 | * Format of arguments in case of kubernetes is 113 | * "IgnoreUnknown=1;K8S_POD_NAMESPACE=default;K8S_POD_NAME=hello-world-1-81nl8; 114 | * K8S_POD_INFRA_CONTAINER_ID=" 115 | */ 116 | func (k8sArgs *K8SArgs) getK8sArgs(args *skel.CmdArgs) { 117 | cniArgs := strings.Split(args.Args, ";") 118 | for _, v := range cniArgs { 119 | a := strings.Split(v, "=") 120 | if len(a) >= 2 { 121 | if a[0] == "K8S_POD_NAMESPACE" { 122 | k8sArgs.NameSpace = a[1] 123 | } 124 | if a[0] == "K8S_POD_NAME" { 125 | k8sArgs.PodName = a[1] 126 | } 127 | } 128 | } 129 | } 130 | 131 | // Get Pod-UUID from docker client 132 | func (k8sArgs *K8SArgs) getPodUuid() error { 133 | os.Setenv("DOCKER_API_VERSION", "1.22") 134 | cli, err := client.NewEnvClient() 135 | if err != nil { 136 | return err 137 | } 138 | 139 | container, err := cli.ContainerInspect(context.Background(), 140 | k8sArgs.PodName) 141 | if err != nil { 142 | return err 143 | } 144 | 145 | if container.Config == nil { 146 | return fmt.Errorf("Could not find UUID for POD %s", k8sArgs.PodName) 147 | } 148 | 149 | var ok bool 150 | k8sArgs.PodUuid, ok = container.Config.Labels["io.kubernetes.pod.uid"] 151 | if ok == false { 152 | return fmt.Errorf("Could not find UUID for POD %s", k8sArgs.PodName) 153 | } 154 | 155 | return nil 156 | } 157 | 158 | // Fetch all parameters. Includes parameters from STDIN and Environemnt vars 159 | func Get(args *skel.CmdArgs) (*CniArgs, error) { 160 | // Set defaults 161 | vrouterArgs := VRouterArgs{Ip: vrouterIp, Port: vrouterPort, 162 | PollTimeout: pollTimeout, PollRetries: pollRetries} 163 | logArgs := LoggingArgs{Dir: logDir, Level: logLevel} 164 | contrailArgs := ContrailArgs{VRouterArgs: vrouterArgs, Dir: k8sDir, 165 | Logging: logArgs} 166 | cniArgs := &CniArgs{ContrailArgs: contrailArgs} 167 | 168 | // Parse json data 169 | if err := json.Unmarshal(args.StdinData, cniArgs); err != nil { 170 | msg := fmt.Sprintf("Invalid JSon string. Error %v : Message %s\n", 171 | err, args.StdinData) 172 | return nil, fmt.Errorf(msg) 173 | } 174 | 175 | cniArgs.ContainerID = args.ContainerID 176 | cniArgs.Netns = args.Netns 177 | cniArgs.IfName = args.IfName 178 | // Kubernetes specific parameters are passed in environment variable. 179 | // Get Kubernetes specific parameters from environment variable 180 | cniArgs.K8SArgs.getK8sArgs(args) 181 | return cniArgs, nil 182 | } 183 | 184 | // Convert cniArgs from VRouter format to CNI format 185 | func VRouterResultToCniResult(agent *agent.Result) *types.Result { 186 | mask := net.CIDRMask(agent.Plen, 32) 187 | ipv4 := types.IPConfig{IP: net.IPNet{IP: net.ParseIP(agent.Ip), 188 | Mask: mask}, Gateway: net.ParseIP(agent.Gw)} 189 | result := &types.Result{IP4: &ipv4} 190 | 191 | _, defaultNet, err := net.ParseCIDR("0.0.0.0/0") 192 | if err != nil { 193 | return nil 194 | } 195 | result.IP4.Routes = append(result.IP4.Routes, 196 | types.Route{Dst: *defaultNet, GW: result.IP4.Gateway}) 197 | 198 | result.DNS.Nameservers = append(result.DNS.Nameservers, agent.Dns) 199 | return result 200 | } 201 | -------------------------------------------------------------------------------- /pkg/network/opencontrail/config.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015 Juniper Networks, Inc. All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package opencontrail 18 | 19 | import ( 20 | "fmt" 21 | "io" 22 | "net" 23 | "regexp" 24 | "strings" 25 | 26 | flag "github.com/spf13/pflag" 27 | 28 | "gopkg.in/gcfg.v1" 29 | 30 | "github.com/Juniper/contrail-kubernetes/pkg/network" 31 | ) 32 | 33 | const ( 34 | DefaultServiceSubnet = "10.254.0.0/16" 35 | ) 36 | 37 | type Config struct { 38 | // OpenContrail Default Domain 39 | DefaultDomain string `gcfg:"default-domain"` 40 | 41 | // OpenContrail api server address:port 42 | ApiAddress string `gcfg:"api-server"` 43 | ApiPort int `gcfg:"api-port"` 44 | 45 | // Project used for objects that are not namespace specific 46 | DefaultProject string `gcfg:"default"` 47 | // Network identifier for the external network 48 | PublicNetwork string `gcfg:"public-network"` 49 | // IP address range configured on the external network 50 | PublicSubnet string `gcfg:"public-ip-range"` 51 | // IP address range used to allocate Pod private IP addresses 52 | PrivateSubnet string `gcfg:"private-ip-range"` 53 | // IP address range used by the kube-apiserver to allocate ClusterIP addresses for services 54 | ServiceSubnet string `gcfg:"service-cluster-ip-range"` 55 | 56 | // Label used to create the network name used by pods and services 57 | NetworkTag string `gcfg:"network-label"` 58 | // Label used to connect pods with services 59 | NetworkAccessTag string `gcfg:"service-label"` 60 | 61 | // Networks connected to all pod networks 62 | GlobalNetworks []string `gcfg:"global-network"` 63 | // Connect networks that match this pattern to the global network list. 64 | GlobalConnectInclude string `gcfg:"global-connect-include"` 65 | // Exclude networks that match the following pattern from connecting to the global network list. 66 | GlobalConnectExclude string `gcfg:"global-connect-exclude"` 67 | 68 | // Cluster-wide service networks 69 | ClusterServices []string `gcfg:"cluster-service"` 70 | 71 | // Tenant-specific service networks that are automatically attached to tenant networks 72 | NamespaceServices []string `gcfg:"namespace-service"` 73 | } 74 | 75 | func NewConfig() *Config { 76 | config := &Config{ 77 | DefaultDomain: "default-domain", 78 | ApiAddress: "localhost", 79 | ApiPort: 8082, 80 | DefaultProject: "default-domain:default", 81 | PublicNetwork: "default-domain:default:Public", 82 | PrivateSubnet: "10.0.0.0/16", 83 | ServiceSubnet: DefaultServiceSubnet, 84 | NetworkTag: "opencontrail.org/name", 85 | NetworkAccessTag: "opencontrail.org/services", 86 | GlobalConnectInclude: ".*", 87 | } 88 | return config 89 | } 90 | 91 | // DEPRECATED 92 | func (c *Config) Parse(args []string) { 93 | fs := flag.NewFlagSet("opencontrail", flag.ExitOnError) 94 | fs.StringVar(&c.ApiAddress, "contrail_api", c.ApiAddress, 95 | "Hostname or address for the OpenContrail API server.") 96 | fs.IntVar(&c.ApiPort, "contrail_port", 8082, 97 | "OpenContrail API port.") 98 | fs.StringVar(&c.PublicNetwork, "public_name", c.PublicNetwork, 99 | "External network name.") 100 | fs.StringVar(&c.PublicSubnet, "public_net", c.PublicSubnet, 101 | "External network subnet prefix used when provisioning the cluster.") 102 | fs.StringVar(&c.PrivateSubnet, "private_net", c.PrivateSubnet, 103 | "Address range to use for private IP addresses.") 104 | fs.StringVar(&c.ServiceSubnet, "portal_net", c.ServiceSubnet, 105 | "Address range to use for services.") 106 | fs.StringVar(&c.NetworkTag, "network_label", c.NetworkTag, 107 | "Label used to specify the network used by the resource (pod or service).") 108 | fs.StringVar(&c.NetworkAccessTag, "access_label", c.NetworkAccessTag, 109 | "Label used to determine what services this resource (pod/rc) accesses.") 110 | fs.Parse(args) 111 | } 112 | 113 | type configWrapper struct { 114 | Default network.Config 115 | OpenContrail Config 116 | } 117 | 118 | func validateQualifiedNetworkName(name string) error { 119 | serviceName := strings.Split(name, "/") 120 | if len(serviceName) != 2 { 121 | return fmt.Errorf("Expected 'namespace/service', got \"%s\"", name) 122 | } 123 | return nil 124 | } 125 | 126 | func validateColonSeparatedNetworkName(name string) error { 127 | networkName := strings.Split(name, ":") 128 | if len(networkName) != 3 { 129 | return fmt.Errorf("Expected 'domain:project:network', got \"%s\"", name) 130 | } 131 | for _, v := range networkName { 132 | if v == "" { 133 | return fmt.Errorf("Empty element in fully qualified network name") 134 | } 135 | } 136 | return nil 137 | } 138 | 139 | func validateNamespaceService(name string) error { 140 | if strings.Contains(name, "/") { 141 | return fmt.Errorf("Namespace-specific service \"%s\": cannot contain namespace separator (\"/\")", name) 142 | } 143 | return nil 144 | } 145 | 146 | func (c *Config) Validate() error { 147 | if _, _, err := net.ParseCIDR(c.PrivateSubnet); err != nil { 148 | return err 149 | } 150 | if c.PublicSubnet != "" { 151 | if _, _, err := net.ParseCIDR(c.PublicSubnet); err != nil { 152 | return err 153 | } 154 | } 155 | if _, _, err := net.ParseCIDR(c.ServiceSubnet); err != nil { 156 | return err 157 | } 158 | 159 | for _, svc := range c.ClusterServices { 160 | err := validateQualifiedNetworkName(svc) 161 | if err != nil { 162 | return err 163 | } 164 | } 165 | 166 | if c.GlobalConnectInclude != "" { 167 | if _, err := regexp.Compile(c.GlobalConnectInclude); err != nil { 168 | return err 169 | } 170 | } 171 | 172 | if c.GlobalConnectExclude != "" { 173 | if _, err := regexp.Compile(c.GlobalConnectExclude); err != nil { 174 | return err 175 | } 176 | } 177 | 178 | for _, net := range c.GlobalNetworks { 179 | err := validateColonSeparatedNetworkName(net) 180 | if err != nil { 181 | return err 182 | } 183 | } 184 | for _, svc := range c.NamespaceServices { 185 | err := validateNamespaceService(svc) 186 | if err != nil { 187 | return err 188 | } 189 | } 190 | 191 | return nil 192 | } 193 | 194 | func (c *Config) ReadConfiguration(global *network.Config, reader io.Reader) error { 195 | if global != nil && c.ServiceSubnet == DefaultServiceSubnet { 196 | c.ServiceSubnet = global.ClusterIpRange 197 | } 198 | 199 | if reader == nil { 200 | return nil 201 | } 202 | 203 | wrapper := configWrapper{OpenContrail: *c} 204 | if err := gcfg.ReadInto(&wrapper, reader); err != nil { 205 | return err 206 | } 207 | if err := wrapper.OpenContrail.Validate(); err != nil { 208 | return err 209 | } 210 | *c = wrapper.OpenContrail 211 | return nil 212 | } 213 | -------------------------------------------------------------------------------- /scripts/opencontrail-kubelet/opencontrail_kubelet/lxc_manager.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2015 Juniper Networks, Inc. 3 | # 4 | 5 | import logging 6 | import os 7 | import re 8 | from shell import Shell 9 | 10 | 11 | def ifindex2name(ifindex): 12 | for ifname in os.listdir('/sys/class/net'): 13 | content = open( 14 | os.path.join('/sys/class/net', ifname, 'ifindex'), 'r').readlines() 15 | index = int('\n'.join(content).strip()) 16 | if index == ifindex: 17 | return ifname 18 | return None 19 | 20 | 21 | class LxcManager(object): 22 | def __init__(self): 23 | pass 24 | 25 | def _interface_generate_unique_name(self): 26 | output = Shell.run('ip link list') 27 | ids = {} 28 | 29 | for line in output.split('\n'): 30 | m = re.match(r'[\d]+: instance([\d]+)', line) 31 | if m: 32 | ids[m.group(1)] = True 33 | 34 | for i in range(256): 35 | if str(i) in ids: 36 | continue 37 | return 'instance%d' % i 38 | return None 39 | 40 | # Find the peer interface (in the host) for a given nsname 41 | # The two ends of the interface link peerings have adjacent ifindex 42 | def interface_find_peer_name(self, nsname): 43 | ifname_instance = Shell.run( 44 | 'ip netns exec %s ip link show eth0 | ' 45 | 'awk \'/eth0/{split($2, array, \":\"); print array[1];}\'' % 46 | nsname) 47 | logging.debug('instance interface %s' % ifname_instance) 48 | 49 | # Get ifindex of ifname_instance 50 | ns_ifindex = Shell.run('ip netns exec %s ethtool -S eth0 | ' 51 | 'grep peer_ifindex | awk \'{print $2}\'' 52 | % (nsname)) 53 | 54 | ifname = ifindex2name(int(ns_ifindex)) 55 | return ifname 56 | 57 | # Find the name of the docker bridge. By default, it is docker0 58 | def get_docker_bridge(self): 59 | bridge = "docker0" 60 | 61 | # Check if docker is running on a different bridge than default docker0 62 | br = Shell.run("ps -efww|grep -w docker|grep bridge|grep -v grep", 63 | True) 64 | m = re.search(r'--bridge\W+(.*?)\W', br) 65 | if m and len(m.group(1)) > 0: 66 | bridge = m.group(1) 67 | return bridge 68 | 69 | # Move the interface out of the docker bridge and attach it to contrail 70 | # Return the moved interface name 71 | def move_interface(self, nsname, pid, mac): 72 | ifname_master = self.interface_find_peer_name(nsname) 73 | 74 | # Remove the interface from the bridge 75 | try: 76 | Shell.run('brctl delif %s %s' % 77 | (self.get_docker_bridge(), ifname_master)) 78 | except Exception as ex: 79 | logging.error(ex) 80 | 81 | if mac: 82 | # Set interface mac 83 | Shell.run('ip netns exec %s ifconfig eth0 hw ether %s' % 84 | (nsname, mac)) 85 | # remove any IP address already assigned 86 | Shell.run('ip netns exec %s ip addr flush dev %s' % 87 | (nsname, 'eth0')) 88 | return ifname_master 89 | 90 | def create_interface(self, nsname, ifname_instance, mac): 91 | ifname_master = self._interface_generate_unique_name() 92 | Shell.run('ip link add %s type veth peer name %s' % 93 | (ifname_instance, ifname_master)) 94 | if mac: 95 | Shell.run('ifconfig %s hw ether %s' % (ifname_instance, mac)) 96 | 97 | Shell.run('ip link set %s netns %s' % (ifname_instance, nsname)) 98 | Shell.run('ip link set %s up' % ifname_master) 99 | return ifname_master 100 | 101 | def _interface_list_contains(self, output, iface): 102 | for line in output.split('\n'): 103 | m = re.match(r'[\d]+: ' + iface + ':', line) 104 | if m: 105 | return True 106 | return False 107 | 108 | def _get_master_ifname(self, daemon, ifname_instance): 109 | output = Shell.run('ip netns exec ns-%s ethtool -S %s' % 110 | (daemon, ifname_instance)) 111 | m = re.search(r'peer_ifindex: (\d+)', output) 112 | ifindex = m.group(1) 113 | output = Shell.run('ip link list') 114 | expr = '^' + ifindex + ': (\w+): ' 115 | regex = re.compile(expr, re.MULTILINE) 116 | m = regex.search(output) 117 | return m.group(1) 118 | 119 | def interface_update(self, daemon, vmi, ifname_instance): 120 | """ 121 | 1. Make sure that the interface exists in the name space. 122 | 2. Update the mac address. 123 | """ 124 | output = Shell.run('ip netns exec ns-%s ip link list' % daemon) 125 | if not self._interface_list_contains(output, ifname_instance): 126 | ifname_master = self.create_interface('ns-%s' % daemon, 127 | ifname_instance) 128 | else: 129 | ifname_master = self._get_master_ifname(daemon, ifname_instance) 130 | 131 | mac = vmi.virtual_machine_interface_mac_addresses.mac_address[0] 132 | Shell.run('ip netns exec ns-%s ifconfig %s hw ether %s' % 133 | (daemon, ifname_instance, mac)) 134 | return ifname_master 135 | 136 | def interface_config(self, daemon, ifname_guest, advertise_default=True, 137 | ip_prefix=None): 138 | """ 139 | Once the interface is operational, configure the IP addresses. 140 | For a bi-directional interface we use dhclient. 141 | """ 142 | if advertise_default: 143 | Shell.run('ip netns exec ns-%s dhclient %s' % 144 | (daemon, ifname_guest)) 145 | else: 146 | Shell.run('ip netns exec ns-%s ip addr add %s/%d dev %s' % 147 | (daemon, ip_prefix[0], ip_prefix[1], ifname_guest)) 148 | Shell.run('ip netns exec ns-%s ip link set %s up' % 149 | (daemon, ifname_guest)) 150 | # disable reverse path filtering 151 | Shell.run('ip netns exec ns-%s sh -c ' + 152 | '"echo 2 >/proc/sys/net/ipv4/conf/%s/rp_filter"' % 153 | (daemon, ifname_guest)) 154 | 155 | def clear_interfaces(self, nsname): 156 | output = "" 157 | try: 158 | output = Shell.run('ip netns exec %s ip link list' % nsname) 159 | except Exception as ex: 160 | logging.error(ex) 161 | 162 | for line in output.split('\n'): 163 | m = re.match(r'^[\d]+: ([\w]+):', line) 164 | if m: 165 | ifname = m.group(1) 166 | if ifname == 'lo': 167 | continue 168 | Shell.run('ip netns exec %s ip link delete %s' % 169 | (nsname, ifname)) 170 | 171 | def namespace_init(self, daemon): 172 | output = Shell.run('ip netns list') 173 | for line in output.split(): 174 | if line == 'ns-' + daemon: 175 | return False 176 | Shell.run('ip netns add ns-%s' % daemon) 177 | return True 178 | 179 | def namespace_delete(self, daemon): 180 | Shell.run('ip netns delete ns-%s' % daemon) 181 | -------------------------------------------------------------------------------- /pkg/network/opencontrail/instance.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015 Juniper Networks, Inc. All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package opencontrail 18 | 19 | import ( 20 | "strings" 21 | 22 | "github.com/golang/glog" 23 | 24 | "github.com/Juniper/contrail-go-api" 25 | "github.com/Juniper/contrail-go-api/types" 26 | ) 27 | 28 | type InstanceManager struct { 29 | client contrail.ApiClient 30 | config *Config 31 | allocator AddressAllocator 32 | } 33 | 34 | func NewInstanceManager(client contrail.ApiClient, config *Config, allocator AddressAllocator) *InstanceManager { 35 | manager := new(InstanceManager) 36 | manager.client = client 37 | manager.config = config 38 | manager.allocator = allocator 39 | return manager 40 | } 41 | 42 | func instanceFQName(domain, tenant, podName string) []string { 43 | fqn := []string{domain, tenant, podName} 44 | return fqn 45 | } 46 | 47 | func (m *InstanceManager) LocateInstance(tenant, podName, uid string) *types.VirtualMachine { 48 | obj, err := m.client.FindByUuid("virtual-machine", string(uid)) 49 | if err == nil { 50 | return obj.(*types.VirtualMachine) 51 | } 52 | 53 | instance := new(types.VirtualMachine) 54 | instance.SetName(podName) 55 | instance.SetUuid(uid) 56 | err = m.client.Create(instance) 57 | if err != nil { 58 | glog.Errorf("Create %s: %v", podName, err) 59 | return nil 60 | } 61 | return instance 62 | } 63 | 64 | func (m *InstanceManager) DeleteInstance(uid string) error { 65 | err := m.client.DeleteByUuid("virtual-machine", uid) 66 | return err 67 | } 68 | 69 | func interfaceFQName(defaultDomain, tenant, podName string) []string { 70 | fqn := []string{defaultDomain, tenant, podName} 71 | return fqn 72 | } 73 | 74 | func (m *InstanceManager) LookupInterface(tenant, podName string) *types.VirtualMachineInterface { 75 | fqn := interfaceFQName(m.config.DefaultDomain, tenant, podName) 76 | obj, err := m.client.FindByName("virtual-machine-interface", strings.Join(fqn, ":")) 77 | if err != nil { 78 | glog.Infof("Get vmi %s: %v", podName, err) 79 | return nil 80 | } 81 | return obj.(*types.VirtualMachineInterface) 82 | } 83 | 84 | func (m *InstanceManager) LocateInterface(project string, 85 | network *types.VirtualNetwork, instance *types.VirtualMachine) *types.VirtualMachineInterface { 86 | fqn := interfaceFQName(m.config.DefaultDomain, project, instance.GetName()) 87 | 88 | obj, err := m.client.FindByName( 89 | "virtual-machine-interface", strings.Join(fqn, ":")) 90 | 91 | if err == nil { 92 | nic := obj.(*types.VirtualMachineInterface) 93 | // TODO(prm): ensure network is as expected, else update. 94 | return nic 95 | } 96 | 97 | nic := new(types.VirtualMachineInterface) 98 | nic.SetFQName("project", fqn) 99 | nic.AddVirtualMachine(instance) 100 | if network != nil { 101 | nic.AddVirtualNetwork(network) 102 | } 103 | err = m.client.Create(nic) 104 | if err != nil { 105 | glog.Errorf("Create interface %s: %v", instance.GetName(), err) 106 | return nil 107 | } 108 | obj, err = m.client.FindByUuid(nic.GetType(), nic.GetUuid()) 109 | if err != nil { 110 | glog.Errorf("Get vmi %s: %v", nic.GetUuid(), err) 111 | return nil 112 | } 113 | return obj.(*types.VirtualMachineInterface) 114 | } 115 | 116 | func (m *InstanceManager) ReleaseInterface(project, podName string) { 117 | fqn := interfaceFQName(m.config.DefaultDomain, project, podName) 118 | obj, err := m.client.FindByName("virtual-machine-interface", strings.Join(fqn, ":")) 119 | if err != nil { 120 | glog.Errorf("Get vmi %s: %v", strings.Join(fqn, ":"), err) 121 | return 122 | } 123 | vmi := obj.(*types.VirtualMachineInterface) 124 | refs, err := vmi.GetFloatingIpBackRefs() 125 | if err == nil { 126 | for _, ref := range refs { 127 | fip, err := types.FloatingIpByUuid(m.client, ref.Uuid) 128 | if err != nil { 129 | glog.Errorf("Get floating-ip %s: %v", ref.Uuid, err) 130 | continue 131 | } 132 | fip.DeleteVirtualMachineInterface(vmi.GetUuid()) 133 | err = m.client.Update(fip) 134 | if err != nil { 135 | glog.Errorf("Remove floating-ip reference %s: %v", ref.Uuid, err) 136 | } 137 | } 138 | } else { 139 | glog.Errorf("Get %s floating-ip back refs: %v", podName, err) 140 | } 141 | err = m.client.Delete(obj) 142 | if err != nil { 143 | glog.Errorf("Delete vmi %s: %v", obj.GetUuid(), err) 144 | } 145 | } 146 | 147 | func makeInstanceIpName(tenant, nicName string) string { 148 | return tenant + "_" + nicName 149 | } 150 | 151 | func (m *InstanceManager) LocateInstanceIp( 152 | network *types.VirtualNetwork, instanceUID string, nic *types.VirtualMachineInterface) *types.InstanceIp { 153 | vmiFqname := nic.GetFQName()[len(nic.GetFQName())-2] 154 | name := makeInstanceIpName(vmiFqname, nic.GetName()) 155 | obj, err := m.client.FindByName("instance-ip", name) 156 | if err == nil { 157 | // TODO(prm): ensure that attributes are as expected 158 | return obj.(*types.InstanceIp) 159 | } 160 | 161 | address, err := m.allocator.LocateIpAddress(instanceUID) 162 | if err != nil { 163 | return nil 164 | } 165 | 166 | // Create InstanceIp 167 | ipObj := new(types.InstanceIp) 168 | ipObj.SetName(name) 169 | ipObj.AddVirtualNetwork(network) 170 | ipObj.AddVirtualMachineInterface(nic) 171 | ipObj.SetInstanceIpAddress(address) 172 | ipObj.SetInstanceIpMode("active-active") 173 | err = m.client.Create(ipObj) 174 | if err != nil { 175 | glog.Errorf("Create instance-ip %s for %s: %v", address, nic.GetName(), err) 176 | return nil 177 | } 178 | obj, err = m.client.FindByUuid(ipObj.GetType(), ipObj.GetUuid()) 179 | if err != nil { 180 | glog.Errorf("Get instance-ip %s for %s: %v", address, ipObj.GetUuid(), err) 181 | return nil 182 | } 183 | return ipObj 184 | } 185 | 186 | func (m *InstanceManager) ReleaseInstanceIp(tenant, nicName, instanceUID string) { 187 | name := makeInstanceIpName(tenant, nicName) 188 | uid, err := m.client.UuidByName("instance-ip", name) 189 | if err != nil { 190 | glog.Errorf("Get instance-ip %s: %v", name, err) 191 | return 192 | } 193 | err = m.client.DeleteByUuid("instance-ip", uid) 194 | if err != nil { 195 | glog.Errorf("Delete instance-ip %s: %v", uid, err) 196 | } 197 | 198 | m.allocator.ReleaseIpAddress(instanceUID) 199 | } 200 | 201 | func (m *InstanceManager) AttachFloatingIp( 202 | podName, projectName string, floatingIp *types.FloatingIp) { 203 | 204 | fqn := []string{m.config.DefaultDomain, projectName, podName} 205 | obj, err := m.client.FindByName( 206 | "virtual-machine-interface", strings.Join(fqn, ":")) 207 | if err != nil { 208 | // this is not an error, the pod may have not been created 209 | // the floating IP will be created when pod create notif is received 210 | return 211 | } 212 | 213 | vmi := obj.(*types.VirtualMachineInterface) 214 | 215 | refs, err := floatingIp.GetVirtualMachineInterfaceRefs() 216 | if err != nil { 217 | glog.Errorf("GET floating-ip %s: %v", floatingIp.GetUuid(), err) 218 | return 219 | } 220 | for _, ref := range refs { 221 | if ref.Uuid == vmi.GetUuid() { 222 | return 223 | } 224 | } 225 | 226 | floatingIp.AddVirtualMachineInterface(vmi) 227 | err = m.client.Update(floatingIp) 228 | if err != nil { 229 | glog.Errorf("Update floating-ip %s: %v", podName, err) 230 | } 231 | } 232 | -------------------------------------------------------------------------------- /cmd/kube-network-manager/app/server.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015 Juniper Networks, Inc. All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package app 18 | 19 | import ( 20 | "io" 21 | "os" 22 | "time" 23 | 24 | "github.com/golang/glog" 25 | flag "github.com/spf13/pflag" 26 | 27 | "k8s.io/kubernetes/pkg/api" 28 | "k8s.io/kubernetes/pkg/client/cache" 29 | client "k8s.io/kubernetes/pkg/client/unversioned" 30 | "k8s.io/kubernetes/pkg/client/unversioned/clientcmd" 31 | "k8s.io/kubernetes/pkg/controller/framework" 32 | "k8s.io/kubernetes/pkg/fields" 33 | 34 | "github.com/Juniper/contrail-kubernetes/pkg/network" 35 | ) 36 | 37 | const ( 38 | ResyncTimeDefault = time.Duration(5) * time.Minute 39 | ClusterIpRangeDefault = "10.254.0.0/16" 40 | ) 41 | 42 | type NetworkManager struct { 43 | ConfigFile string 44 | config network.Config 45 | 46 | Client *client.Client 47 | Controller network.NetworkController 48 | 49 | PodStore cache.Indexer 50 | PodInformer *framework.Controller 51 | 52 | NamespaceStore cache.Store 53 | NamespaceInformer *framework.Controller 54 | 55 | RCStore cache.Store 56 | RCInformer *framework.Controller 57 | 58 | ServiceStore cache.Store 59 | ServiceInformer *framework.Controller 60 | 61 | Shutdown chan struct{} 62 | } 63 | 64 | func NewNetworkManager() *NetworkManager { 65 | manager := new(NetworkManager) 66 | manager.config = network.Config{ 67 | ResyncPeriod: ResyncTimeDefault, 68 | ClusterIpRange: ClusterIpRangeDefault, 69 | } 70 | manager.Shutdown = make(chan struct{}) 71 | return manager 72 | } 73 | 74 | func (m *NetworkManager) AddFlags(fs *flag.FlagSet) { 75 | fs.StringVar(&m.ConfigFile, "config-file", "/etc/kubernetes/network.conf", 76 | "Network manager configuration") 77 | // DEPRECATED 78 | fs.StringVar(&m.config.KubeUrl, "master", m.config.KubeUrl, 79 | "Kubernetes API endpoint") 80 | } 81 | 82 | func (m *NetworkManager) parseConfig() io.ReadCloser { 83 | if m.ConfigFile == "" { 84 | return nil 85 | } 86 | file, err := os.Open(m.ConfigFile) 87 | if err != nil { 88 | glog.Warning(err) 89 | return nil 90 | } 91 | 92 | err = network.ReadConfiguration(file, &m.config) 93 | if err != nil { 94 | glog.Error(err) 95 | } 96 | 97 | _, err = file.Seek(0, 0) 98 | if err != nil { 99 | glog.Error(err) 100 | } 101 | 102 | return file 103 | } 104 | 105 | func (m *NetworkManager) init(args []string) { 106 | configFile := m.parseConfig() 107 | defer func() { 108 | if configFile != nil { 109 | configFile.Close() 110 | } 111 | }() 112 | 113 | loadingRules := clientcmd.NewDefaultClientConfigLoadingRules() 114 | if m.config.KubeConfig != "" { 115 | loadingRules.ExplicitPath = m.config.KubeConfig 116 | } 117 | configOverrides := &clientcmd.ConfigOverrides{} 118 | if m.config.KubeUrl != "" { 119 | configOverrides.ClusterInfo.Server = m.config.KubeUrl 120 | } 121 | kubeConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, configOverrides) 122 | config, err := kubeConfig.ClientConfig() 123 | if err != nil { 124 | glog.Fatal(err) 125 | } 126 | m.Client, err = client.New(config) 127 | if err != nil { 128 | glog.Fatalf("Invalid API configuratin: %v", err) 129 | } 130 | 131 | m.Controller = network.NewNetworkFactory().Create(m.Client, args) 132 | m.Controller.Init(&m.config, configFile) 133 | } 134 | 135 | func (m *NetworkManager) start(args []string) { 136 | m.init(args) 137 | 138 | m.PodStore, m.PodInformer = framework.NewIndexerInformer( 139 | cache.NewListWatchFromClient( 140 | m.Client, 141 | string(api.ResourcePods), 142 | api.NamespaceAll, 143 | fields.Everything(), 144 | ), 145 | &api.Pod{}, 146 | m.config.ResyncPeriod, 147 | framework.ResourceEventHandlerFuncs{ 148 | AddFunc: func(obj interface{}) { 149 | m.Controller.AddPod(obj.(*api.Pod)) 150 | }, 151 | UpdateFunc: func(oldObj, newObj interface{}) { 152 | m.Controller.UpdatePod( 153 | oldObj.(*api.Pod), newObj.(*api.Pod)) 154 | }, 155 | DeleteFunc: func(obj interface{}) { 156 | if pod, ok := obj.(*api.Pod); ok { 157 | m.Controller.DeletePod(pod) 158 | } 159 | }, 160 | }, 161 | cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, 162 | ) 163 | 164 | m.NamespaceStore, m.NamespaceInformer = framework.NewInformer( 165 | cache.NewListWatchFromClient( 166 | m.Client, 167 | "namespaces", 168 | api.NamespaceAll, 169 | fields.Everything(), 170 | ), 171 | &api.Namespace{}, 172 | m.config.ResyncPeriod, 173 | framework.ResourceEventHandlerFuncs{ 174 | AddFunc: func(obj interface{}) { 175 | m.Controller.AddNamespace( 176 | obj.(*api.Namespace)) 177 | }, 178 | UpdateFunc: func(oldObj, newObj interface{}) { 179 | m.Controller.UpdateNamespace( 180 | oldObj.(*api.Namespace), 181 | newObj.(*api.Namespace)) 182 | }, 183 | DeleteFunc: func(obj interface{}) { 184 | if namespace, ok := obj.(*api.Namespace); ok { 185 | m.Controller.DeleteNamespace(namespace) 186 | } 187 | }, 188 | }, 189 | ) 190 | 191 | m.RCStore, m.RCInformer = framework.NewInformer( 192 | cache.NewListWatchFromClient( 193 | m.Client, 194 | string(api.ResourceReplicationControllers), 195 | api.NamespaceAll, 196 | fields.Everything(), 197 | ), 198 | &api.ReplicationController{}, 199 | m.config.ResyncPeriod, 200 | framework.ResourceEventHandlerFuncs{ 201 | AddFunc: func(obj interface{}) { 202 | m.Controller.AddReplicationController( 203 | obj.(*api.ReplicationController)) 204 | }, 205 | UpdateFunc: func(oldObj, newObj interface{}) { 206 | m.Controller.UpdateReplicationController( 207 | oldObj.(*api.ReplicationController), 208 | newObj.(*api.ReplicationController)) 209 | }, 210 | DeleteFunc: func(obj interface{}) { 211 | if rc, ok := obj.(*api.ReplicationController); ok { 212 | m.Controller.DeleteReplicationController(rc) 213 | } 214 | }, 215 | }, 216 | ) 217 | 218 | m.ServiceStore, m.ServiceInformer = framework.NewInformer( 219 | cache.NewListWatchFromClient( 220 | m.Client, 221 | string(api.ResourceServices), 222 | api.NamespaceAll, 223 | fields.Everything(), 224 | ), 225 | &api.Service{}, 226 | m.config.ResyncPeriod, 227 | framework.ResourceEventHandlerFuncs{ 228 | AddFunc: func(obj interface{}) { 229 | m.Controller.AddService( 230 | obj.(*api.Service)) 231 | }, 232 | UpdateFunc: func(oldObj, newObj interface{}) { 233 | m.Controller.UpdateService( 234 | oldObj.(*api.Service), 235 | newObj.(*api.Service)) 236 | }, 237 | DeleteFunc: func(obj interface{}) { 238 | if service, ok := obj.(*api.Service); ok { 239 | m.Controller.DeleteService(service) 240 | } 241 | }, 242 | }, 243 | ) 244 | 245 | m.Controller.SetPodStore(m.PodStore) 246 | m.Controller.SetNamespaceStore(m.NamespaceStore) 247 | m.Controller.SetReplicationControllerStore(m.RCStore) 248 | m.Controller.SetServiceStore(m.ServiceStore) 249 | } 250 | 251 | func (m *NetworkManager) Run(args []string) error { 252 | m.start(args) 253 | go m.PodInformer.Run(m.Shutdown) 254 | go m.NamespaceInformer.Run(m.Shutdown) 255 | go m.RCInformer.Run(m.Shutdown) 256 | go m.ServiceInformer.Run(m.Shutdown) 257 | go m.Controller.Run(m.Shutdown) 258 | select {} 259 | } 260 | -------------------------------------------------------------------------------- /scripts/opencontrail-kubelet/opencontrail_kubelet/plugin.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2015 Juniper Networks, Inc. 3 | # 4 | 5 | import argparse 6 | import distutils.spawn 7 | import json 8 | import logging 9 | import os 10 | import requests 11 | import sys 12 | import time 13 | import xml.etree.ElementTree as ElementTree 14 | 15 | from vrouter_api import ContrailVRouterApi 16 | from lxc_manager import LxcManager 17 | from shell import Shell 18 | 19 | opt_net_mode = "bridge" 20 | 21 | 22 | def docker_get_pid(docker_id): 23 | pid_str = Shell.run('docker inspect -f \'{{.State.Pid}}\' %s' % docker_id) 24 | return int(pid_str) 25 | 26 | 27 | def getDockerPod(docker_id): 28 | name = Shell.run('docker inspect -f \'{{.Name}}\' %s' % docker_id) 29 | 30 | # Name 31 | # See: pkg/kubelet/dockertools/docker.go:ParseDockerName 32 | # name_namespace_uid 33 | fields = name.rstrip().split('_') 34 | 35 | podName = fields[2] 36 | uid = fields[4] 37 | return uid, podName 38 | 39 | 40 | def getPodInfo(namespace, podName): 41 | r = requests.get('http://localhost:10255/pods') 42 | if r.status_code != requests.codes.ok: 43 | logging.error("%s: %s", 'http://localhost:10255/pods', r.text) 44 | return None 45 | 46 | podItems = json.loads(r.text) 47 | 48 | for pod in podItems["items"]: 49 | if 'metadata' not in pod: 50 | continue 51 | meta = pod['metadata'] 52 | if meta['namespace'] == namespace and meta['name'] == podName: 53 | logging.debug('pod %s %s', podName, pod['status']) 54 | return pod 55 | 56 | logging.error('%s not present in kubelet cache' % podName) 57 | return None 58 | 59 | 60 | def init(): 61 | """ Ensure that the following tools are available on the PATH """ 62 | executables = ['ethtool', 'brctl'] 63 | for prog in executables: 64 | if distutils.spawn.find_executable(prog) is None: 65 | logging.error('%s not in PATH' % prog) 66 | sys.exit(1) 67 | 68 | 69 | def setup(pod_namespace, pod_name, docker_id, retry=True): 70 | """ 71 | project: pod_namespace 72 | network: pod_name 73 | netns: docker_id{12} 74 | """ 75 | 76 | # Kubelet::createPodInfraContainer ensures that State.Pid is set 77 | pid = docker_get_pid(docker_id) 78 | if pid == 0: 79 | raise Exception('Unable to read State.Pid') 80 | 81 | short_id = docker_id[0:12] 82 | 83 | if not os.path.exists('/var/run/netns'): 84 | os.mkdir('/var/run/netns') 85 | 86 | Shell.run('ln -sf /proc/%d/ns/net /var/run/netns/%s' % (pid, short_id)) 87 | 88 | manager = LxcManager() 89 | 90 | if opt_net_mode == 'none': 91 | instance_ifname = 'veth0' 92 | else: 93 | instance_ifname = 'eth0' 94 | 95 | uid, podName = getDockerPod(docker_id) 96 | podInfo = None 97 | podState = None 98 | for i in range(0, 30): 99 | podInfo = getPodInfo(pod_namespace, podName) 100 | if podInfo is None: 101 | return False 102 | if 'hostNetwork' in podInfo['spec'] and \ 103 | podInfo['spec']['hostNetwork']: 104 | return True 105 | if 'annotations' in podInfo["metadata"] and \ 106 | 'opencontrail.org/pod-state' in podInfo["metadata"]["annotations"]: 107 | podState = json.loads( 108 | podInfo["metadata"]["annotations"] 109 | ["opencontrail.org/pod-state"]) 110 | break 111 | if not retry: 112 | return False 113 | time.sleep(1) 114 | 115 | # The lxc_manager uses the mac_address to setup the container interface. 116 | # Additionally the ip-address, prefixlen and gateway are also used. 117 | if podState is None: 118 | logging.error('No annotations in pod %s', podInfo["metadata"]["name"]) 119 | return False 120 | 121 | nic_uuid = podState["uuid"] 122 | mac_address = podState["macAddress"] 123 | if opt_net_mode == 'none': 124 | ifname = manager.create_interface(short_id, instance_ifname, 125 | mac_address) 126 | else: 127 | ifname = manager.move_interface(short_id, pid, mac_address) 128 | 129 | api = ContrailVRouterApi() 130 | api.add_port(uid, nic_uuid, ifname, mac_address, 131 | port_type='NovaVMPort', 132 | display_name=podName, 133 | hostname=podName+'.'+pod_namespace) 134 | 135 | ip_address = podState["ipAddress"] 136 | gateway = podState["gateway"] 137 | Shell.run('ip netns exec %s ip addr add %s/32 peer %s dev %s' % 138 | (short_id, ip_address, gateway, instance_ifname)) 139 | Shell.run('ip netns exec %s ip route add default via %s' % 140 | (short_id, gateway)) 141 | Shell.run('ip netns exec %s ip link set %s up' % 142 | (short_id, instance_ifname)) 143 | return True 144 | 145 | 146 | def vrouter_interface_by_name(vmName): 147 | r = requests.get('http://localhost:8085/Snh_ItfReq') 148 | root = ElementTree.fromstring(r.text) 149 | for interface in root.iter('ItfSandeshData'): 150 | vm = interface.find('vm_name') 151 | if vm is not None and vm.text == vmName: 152 | vmi = interface.find('uuid') 153 | return vmi.text, interface 154 | return None, None 155 | 156 | 157 | def teardown(pod_namespace, pod_name, docker_id): 158 | manager = LxcManager() 159 | short_id = docker_id[0:12] 160 | 161 | api = ContrailVRouterApi() 162 | 163 | uid, podName = getDockerPod(docker_id) 164 | vmi, _ = vrouter_interface_by_name(podName) 165 | if vmi is not None: 166 | api.delete_port(vmi) 167 | 168 | manager.clear_interfaces(short_id) 169 | Shell.run('ip netns delete %s' % short_id) 170 | 171 | 172 | class PodNetworkStatus(object): 173 | def __init__(self): 174 | self.kind = 'PodNetworkStatus' 175 | self.apiVersion = 'v1beta1' 176 | self.ip = None 177 | 178 | 179 | def podHasLivenessProbe(podInfo): 180 | for container in podInfo['spec']['containers']: 181 | if 'livenessProbe' in container: 182 | return True 183 | return False 184 | 185 | 186 | def status(pod_namespace, pod_name, docker_id): 187 | status = PodNetworkStatus() 188 | uid, podName = getDockerPod(docker_id) 189 | vmi, data = vrouter_interface_by_name(podName) 190 | if vmi is None: 191 | setup(pod_namespace, pod_name, docker_id, retry=False) 192 | return 193 | 194 | podInfo = getPodInfo(pod_namespace, podName) 195 | if podInfo and podHasLivenessProbe(podInfo): 196 | localaddr = data.find('mdata_ip_addr') 197 | else: 198 | localaddr = data.find('ip_addr') 199 | 200 | if localaddr is not None: 201 | status.ip = localaddr.text 202 | print json.dumps(status.__dict__) 203 | 204 | 205 | def main(): 206 | logging.basicConfig(filename='/var/log/contrail/kubelet-driver.log', 207 | level=logging.INFO) 208 | logging.debug(' '.join(sys.argv)) 209 | parser = argparse.ArgumentParser() 210 | subparsers = parser.add_subparsers(title="action", dest='action') 211 | 212 | subparsers.add_parser('init') 213 | 214 | cmd_parser = argparse.ArgumentParser(add_help=False) 215 | cmd_parser.add_argument('pod_namespace') 216 | cmd_parser.add_argument('pod_name') 217 | cmd_parser.add_argument('docker_id') 218 | 219 | subparsers.add_parser('setup', parents=[cmd_parser]) 220 | subparsers.add_parser('teardown', parents=[cmd_parser]) 221 | subparsers.add_parser('status', parents=[cmd_parser]) 222 | 223 | args = parser.parse_args() 224 | 225 | try: 226 | if args.action == 'init': 227 | init() 228 | elif args.action == 'setup': 229 | setup(args.pod_namespace, args.pod_name, args.docker_id) 230 | elif args.action == 'teardown': 231 | teardown(args.pod_namespace, args.pod_name, args.docker_id) 232 | elif args.action == 'status': 233 | status(args.pod_namespace, args.pod_name, args.docker_id) 234 | except Exception as ex: 235 | logging.error(ex) 236 | sys.exit(1) 237 | 238 | if __name__ == '__main__': 239 | main() 240 | -------------------------------------------------------------------------------- /pkg/network/opencontrail/random_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015 Juniper Networks, Inc. All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package opencontrail 18 | 19 | import ( 20 | "math/rand" 21 | "strings" 22 | "testing" 23 | 24 | "github.com/golang/glog" 25 | "github.com/pborman/uuid" 26 | "github.com/stretchr/testify/assert" 27 | 28 | "k8s.io/kubernetes/pkg/api" 29 | kubetypes "k8s.io/kubernetes/pkg/types" 30 | ) 31 | 32 | type tierConfig struct { 33 | Name string 34 | MaxInstances int 35 | Connections []string 36 | Service api.ServiceType 37 | } 38 | 39 | var ( 40 | testProjects = []string{"cluster", "appA", "appB"} 41 | testTiers = map[string][]tierConfig{ 42 | testProjects[0]: []tierConfig{ 43 | tierConfig{Name: "default", MaxInstances: 2}, 44 | tierConfig{Name: "log", MaxInstances: 2, Service: api.ServiceTypeClusterIP}, 45 | }, 46 | testProjects[1]: []tierConfig{ 47 | tierConfig{Name: "web", MaxInstances: 5, Connections: []string{"cache", "db"}, Service: api.ServiceTypeLoadBalancer}, 48 | tierConfig{Name: "cache", MaxInstances: 3, Service: api.ServiceTypeClusterIP}, 49 | tierConfig{Name: "db", MaxInstances: 2, Service: api.ServiceTypeClusterIP}, 50 | }, 51 | testProjects[2]: []tierConfig{ 52 | tierConfig{Name: "web", MaxInstances: 1, Connections: []string{"monitor", "db", "aux"}, Service: api.ServiceTypeLoadBalancer}, 53 | tierConfig{Name: "monitor", MaxInstances: 1, Service: api.ServiceTypeClusterIP}, 54 | tierConfig{Name: "db", MaxInstances: 2, Connections: []string{"monitor"}, Service: api.ServiceTypeClusterIP}, 55 | tierConfig{Name: "aux", MaxInstances: 3, Connections: []string{"monitor"}, Service: api.ServiceTypeClusterIP}, 56 | }, 57 | } 58 | ) 59 | 60 | func appendUnique(array []string, element string) []string { 61 | for _, v := range array { 62 | if v == element { 63 | return array 64 | } 65 | } 66 | return append(array, element) 67 | } 68 | 69 | func stringSliceRemove(array []string, element string) []string { 70 | for i, v := range array { 71 | if v == element { 72 | return append(array[:i], array[i+1:]...) 73 | } 74 | } 75 | return array 76 | } 77 | 78 | type transform interface { 79 | init(env *TestFramework) bool 80 | exec(env *TestFramework) 81 | } 82 | 83 | func selectTier() (string, *tierConfig) { 84 | coin := rand.Int() 85 | // select project 86 | projectNum := coin % len(testProjects) 87 | projectName := testProjects[projectNum] 88 | coin = coin - projectNum 89 | 90 | // select tier 91 | tierList := testTiers[projectName] 92 | tierNum := coin % len(tierList) 93 | return projectName, &tierList[tierNum] 94 | } 95 | 96 | type addPodTransform struct { 97 | pod *api.Pod 98 | } 99 | 100 | func (t *addPodTransform) init(env *TestFramework) bool { 101 | projectName, config := selectTier() 102 | 103 | data := env.GetGroupState(projectName, config.Name) 104 | if len(data.Pods) == config.MaxInstances { 105 | return false 106 | } 107 | objectID := uuid.New() 108 | pod := api.Pod{ 109 | ObjectMeta: api.ObjectMeta{ 110 | UID: kubetypes.UID(objectID), 111 | Name: config.Name + "-" + objectID[0:8], 112 | Namespace: projectName, 113 | Labels: map[string]string{ 114 | "Name": config.Name, 115 | env.config.NetworkTag: config.Name, 116 | }, 117 | }, 118 | } 119 | if config.Connections != nil { 120 | pod.ObjectMeta.Labels[env.config.NetworkAccessTag] = config.Connections[rand.Intn(len(config.Connections))] 121 | } 122 | t.pod = &pod 123 | return true 124 | } 125 | 126 | func (t *addPodTransform) exec(env *TestFramework) { 127 | glog.Infof("add Pod %s/%s", t.pod.Namespace, t.pod.Name) 128 | env.AddPod(t.pod) 129 | } 130 | 131 | type deletePodTransform struct { 132 | pod *api.Pod 133 | } 134 | 135 | func (t *deletePodTransform) init(env *TestFramework) bool { 136 | projectName, config := selectTier() 137 | 138 | data := env.GetGroupState(projectName, config.Name) 139 | if len(data.Pods) == 0 { 140 | return false 141 | } 142 | t.pod = data.Pods[rand.Intn(len(data.Pods))] 143 | return true 144 | } 145 | 146 | func (t *deletePodTransform) exec(env *TestFramework) { 147 | glog.Infof("delete Pod %s/%s", t.pod.Namespace, t.pod.Name) 148 | env.DeletePod(t.pod) 149 | } 150 | 151 | type addServiceTransform struct { 152 | service *api.Service 153 | } 154 | 155 | func (t *addServiceTransform) init(env *TestFramework) bool { 156 | projectName, config := selectTier() 157 | 158 | if config.Service == "" { 159 | return false 160 | } 161 | state := env.GetGroupState(projectName, config.Name) 162 | if len(state.Services) > 0 { 163 | return false 164 | } 165 | service := &api.Service{ 166 | ObjectMeta: api.ObjectMeta{ 167 | Name: config.Name, 168 | Namespace: projectName, 169 | Labels: map[string]string{ 170 | env.config.NetworkTag: config.Name, 171 | }, 172 | }, 173 | Spec: api.ServiceSpec{ 174 | Selector: map[string]string{ 175 | "Name": config.Name, 176 | }, 177 | ClusterIP: env.AllocateClusterIP(config.Name), 178 | Type: config.Service, 179 | }, 180 | } 181 | t.service = service 182 | return true 183 | } 184 | 185 | func (t *addServiceTransform) exec(env *TestFramework) { 186 | glog.Infof("add service %s/%s", t.service.Namespace, t.service.Name) 187 | env.AddService(t.service, t.service.Name) 188 | } 189 | 190 | type deleteServiceTransform struct { 191 | service *api.Service 192 | } 193 | 194 | func (t *deleteServiceTransform) init(env *TestFramework) bool { 195 | projectName, config := selectTier() 196 | state := env.GetGroupState(projectName, config.Name) 197 | if len(state.Services) == 0 { 198 | return false 199 | } 200 | t.service = state.Services[0] 201 | return true 202 | } 203 | 204 | func (t *deleteServiceTransform) exec(env *TestFramework) { 205 | glog.Infof("delete service %s/%s", t.service.Namespace, t.service.Name) 206 | env.DeleteService(t.service, t.service.Name) 207 | } 208 | 209 | type updateGlobalNetworkConfig struct { 210 | } 211 | 212 | func (t *updateGlobalNetworkConfig) init(env *TestFramework) bool { 213 | globalNetworkName := strings.Join([]string{env.config.DefaultDomain, "cluster", "default"}, ":") 214 | coin := rand.Intn(8) 215 | switch coin { 216 | case 0: 217 | // Add global service 218 | env.config.ClusterServices = appendUnique(env.config.ClusterServices, "cluster/log") 219 | break 220 | case 1: 221 | // Delete global service 222 | env.config.ClusterServices = stringSliceRemove(env.config.ClusterServices, "cluster/log") 223 | break 224 | case 2: 225 | // Add global network 226 | env.config.GlobalNetworks = appendUnique(env.config.GlobalNetworks, globalNetworkName) 227 | break 228 | case 3: 229 | // Delete global network 230 | env.config.GlobalNetworks = stringSliceRemove(env.config.GlobalNetworks, globalNetworkName) 231 | break 232 | default: 233 | return false 234 | } 235 | return true 236 | } 237 | 238 | func (t *updateGlobalNetworkConfig) exec(env *TestFramework) { 239 | glog.Infof("update global network config") 240 | } 241 | 242 | func TestRandom(t *testing.T) { 243 | transforms := []transform{ 244 | &addPodTransform{}, 245 | &deletePodTransform{}, 246 | &addServiceTransform{}, 247 | &deleteServiceTransform{}, 248 | } 249 | 250 | env := new(TestFramework) 251 | env.SetUp("192.0.2.0/24") 252 | env.Start() 253 | 254 | for i := 0; i < 128; i++ { 255 | tf := transforms[rand.Int()%len(transforms)] 256 | doExec := tf.init(env) 257 | if !doExec { 258 | continue 259 | } 260 | tf.exec(env) 261 | env.SyncBarrier() 262 | check := env.checker.Check() 263 | if !check { 264 | env.checker.Check() 265 | assert.True(t, env.checker.Check(), "iteration %d", i) 266 | } 267 | } 268 | 269 | env.Shutdown() 270 | } 271 | 272 | // TODO(prm): repeat test without public network configuration. 273 | -------------------------------------------------------------------------------- /scripts/opencontrail-install/fedora21/install.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | @branch = "3.0" # master 4 | @tag = "4100" 5 | @pkg_tag = "#{@branch}-#{@tag}" 6 | 7 | @common_packages = [ 8 | "createrepo", 9 | "docker", 10 | "git", 11 | "sshpass", 12 | "strace", 13 | "tcpdump", 14 | "unzip", 15 | "vim", 16 | ] 17 | 18 | @controller_thirdparty_packages = [ 19 | @common_packages, 20 | "#{@ws}/thirdparty/authbind-2.1.1-0.x86_64.rpm", 21 | "#{@ws}/thirdparty/librdkafka1-0.8.5-2.0contrail0.el7.centos.x86_64.rpm", 22 | "#{@ws}/thirdparty/librdkafka-devel-0.8.5-2.0contrail0.el7.centos.x86_64.rpm", 23 | "#{@ws}/thirdparty/cassandra12-1.2.11-1.noarch.rpm", 24 | "#{@ws}/thirdparty/kafka-2.9.2-0.8.2.0.0contrail0.el7.x86_64.rpm", 25 | "#{@ws}/thirdparty/python-pycassa-1.10.0-0contrail.el7.noarch.rpm", 26 | "#{@ws}/thirdparty/thrift-0.9.1-12.el7.x86_64.rpm", 27 | "#{@ws}/thirdparty/python-thrift-0.9.1-12.el7.x86_64.rpm", 28 | "#{@ws}/thirdparty/python-bitarray-0.8.0-0contrail.el7.x86_64.rpm", 29 | "#{@ws}/thirdparty/python-jsonpickle-0.3.1-2.1.el7.noarch.rpm", 30 | "#{@ws}/thirdparty/xmltodict-0.7.0-0contrail.el7.noarch.rpm", 31 | "#{@ws}/thirdparty/python-amqp-1.4.5-1.el7.noarch.rpm", 32 | "#{@ws}/thirdparty/python-geventhttpclient-1.0a-0contrail.el7.x86_64.rpm", 33 | "#{@ws}/thirdparty/consistent_hash-1.0-0contrail0.el7.noarch.rpm", 34 | "#{@ws}/thirdparty/python-kafka-python-0.9.2-0contrail0.el7.noarch.rpm", 35 | "#{@ws}/thirdparty/redis-py-0.1-2contrail.el7.noarch.rpm", 36 | "#{@ws}/thirdparty/ifmap-server-0.3.2-2contrail.el7.noarch.rpm", 37 | "#{@ws}/thirdparty/hc-httpcore-4.1-1.jpp6.noarch.rpm", 38 | "#{@ws}/thirdparty/zookeeper-3.4.3-1.el6.noarch.rpm", 39 | "#{@ws}/thirdparty/bigtop-utils-0.6.0+243-1.cdh4.7.0.p0.17.el6.noarch.rpm", 40 | "#{@ws}/thirdparty/python-keystone-2014.1.3-2.el7ost.noarch.rpm", 41 | "#{@ws}/thirdparty/python-psutil-1.2.1-1.el7.x86_64.rpm", 42 | "#{@ws}/thirdparty/java-1.7.0-openjdk-1.7.0.55-2.4.7.2.el7_0.x86_64.rpm", 43 | "#{@ws}/thirdparty/java-1.7.0-openjdk-headless-1.7.0.55-2.4.7.2.el7_0.x86_64.rpm", 44 | "#{@ws}/thirdparty/log4j-1.2.17-15.el7.noarch.rpm", 45 | 46 | "supervisor", 47 | "supervisord", 48 | "python-supervisor", 49 | "rabbitmq-server", 50 | "python-kazoo", 51 | "python-ncclient", 52 | ] 53 | 54 | @controller_contrail_packages = [ 55 | "#{@ws}/contrail/controller/build/package-build/RPMS/noarch/contrail-database-#{@pkg_tag}.fc21.noarch.rpm", 56 | "#{@ws}/contrail/controller/build/package-build/RPMS/x86_64/python-contrail-#{@pkg_tag}.fc21.x86_64.rpm", 57 | "#{@ws}/contrail/controller/build/package-build/RPMS/noarch/contrail-config-#{@pkg_tag}.fc21.noarch.rpm", 58 | "#{@ws}/contrail/controller/build/package-build/RPMS/x86_64/contrail-lib-#{@pkg_tag}.fc21.x86_64.rpm", 59 | "#{@ws}/contrail/controller/build/package-build/RPMS/x86_64/contrail-control-#{@pkg_tag}.fc21.x86_64.rpm", 60 | "#{@ws}/contrail/controller/build/package-build/RPMS/x86_64/contrail-analytics-#{@pkg_tag}.fc21.x86_64.rpm", 61 | "#{@ws}/contrail/controller/build/package-build/RPMS/x86_64/contrail-web-controller-#{@pkg_tag}.x86_64.rpm", 62 | "#{@ws}/contrail/controller/build/package-build/RPMS/x86_64/contrail-web-core-#{@pkg_tag}.x86_64.rpm", 63 | "#{@ws}/contrail/controller/build/package-build/RPMS/noarch/contrail-setup-#{@pkg_tag}.fc21.noarch.rpm", 64 | "#{@ws}/controller/build/package-build/RPMS/x86_64/contrail-nodemgr-#{@pkg_tag}.fc21.x86_64.rpm", 65 | "#{@ws}/controller/build/package-build/RPMS/x86_64/contrail-utils-#{@pkg_tag}.fc21.x86_64.rpm", 66 | "#{@ws}/controller/build/package-build/RPMS/x86_64/contrail-dns-#{@pkg_tag}.fc21.x86_64.rpm", 67 | "#{@ws}/controller/build/package-build/RPMS/noarch/contrail-openstack-control-#{@pkg_tag}.fc21.noarch.rpm", 68 | "#{@ws}/controller/build/package-build/RPMS/noarch/contrail-openstack-database-#{@pkg_tag}.fc21.noarch.rpm", 69 | "#{@ws}/controller/build/package-build/RPMS/noarch/contrail-openstack-webui-#{@pkg_tag}.fc21.noarch.rpm", 70 | ] 71 | 72 | @compute_thirdparty_packages = [ 73 | @common_packages, 74 | "#{@ws}/thirdparty/xmltodict-0.7.0-0contrail.el7.noarch.rpm", 75 | "#{@ws}/thirdparty/consistent_hash-1.0-0contrail0.el7.noarch.rpm", 76 | "#{@ws}/thirdparty/python-pycassa-1.10.0-0contrail.el7.noarch.rpm ", 77 | ] 78 | 79 | @compute_contrail_packages = [ 80 | "#{@ws}/contrail/controller/build/package-build/RPMS/x86_64/python-contrail-#{@pkg_tag}.fc21.x86_64.rpm", 81 | "#{@ws}/contrail/controller/build/package-build/RPMS/x86_64/python-contrail-vrouter-api-#{@pkg_tag}.fc21.x86_64.rpm", 82 | "#{@ws}/contrail/controller/build/package-build/RPMS/x86_64/contrail-vrouter-utils-#{@pkg_tag}.fc21.x86_64.rpm", 83 | "#{@ws}/contrail/controller/build/package-build/RPMS/x86_64/contrail-vrouter-init-#{@pkg_tag}.fc21.x86_64.rpm", 84 | "#{@ws}/contrail/controller/build/package-build/RPMS/x86_64/contrail-lib-#{@pkg_tag}.fc21.x86_64.rpm", 85 | "#{@ws}/contrail/controller/build/package-build/RPMS/x86_64/contrail-vrouter-#{@pkg_tag}.fc21.x86_64.rpm", 86 | "#{@ws}/contrail/controller/build/package-build/RPMS/x86_64/contrail-vrouter-agent-#{@pkg_tag}.fc21.x86_64.rpm", 87 | "#{@ws}/contrail/controller/build/package-build/RPMS/noarch/contrail-setup-#{@pkg_tag}.fc21.noarch.rpm", 88 | "#{@ws}/contrail/controller/build/package-build/RPMS/noarch/contrail-vrouter-common-#{@pkg_tag}.fc21.noarch.rpm", 89 | "#{@ws}/contrail/controller/build/package-build/RPMS/x86_64/contrail-vrouter-init-#{@pkg_tag}.fc21.x86_64.rpm", 90 | "#{@ws}/contrail/controller/build/package-build/RPMS/x86_64/contrail-utils-#{@pkg_tag}.fc21.x86_64.rpm", 91 | "#{@ws}/contrail/controller/build/package-build/RPMS/x86_64/contrail-nodemgr-#{@pkg_tag}.fc21.x86_64.rpm", 92 | "#{@ws}/contrail/controller/build/package-build/RPMS/noarch/contrail-vrouter-common-#{@pkg_tag}.fc21.noarch.rpm", 93 | ] 94 | 95 | # Download and extract contrail and thirdparty rpms 96 | def download_contrail_software 97 | sh("wget -qO - https://github.com/rombie/opencontrail-packages/blob/master/fedora21/contrail.tar.xz?raw=true | tar Jx") 98 | sh("wget -qO - https://github.com/rombie/opencontrail-packages/blob/master/fedora21/thirdparty.tar.xz?raw=true | tar Jx") 99 | end 100 | 101 | # Install from /cs-shared/builder/cache/centoslinux70/juno 102 | def install_thirdparty_software_controller 103 | sh("yum -y remove java-1.8.0-openjdk java-1.8.0-openjdk-headless") 104 | sh("yum -y install #{@controller_thirdparty_packages.join(" ")}") 105 | end 106 | 107 | # Install contrail controller software 108 | def install_contrail_software_controller 109 | sh("yum -y install #{@controller_contrail_packages.join(" ")}") 110 | 111 | sh("rpm2cpio #{@ws}/contrail/controller/build/package-build/RPMS/noarch/contrail-openstack-database-#{@pkg_tag}.fc21.noarch.rpm | cpio -idmv") 112 | sh("cp etc/rc.d/init.d/zookeeper /etc/rc.d/init.d/") 113 | sh("rpm2cpio #{@ws}/contrail/controller/build/package-build/RPMS/noarch/contrail-openstack-config-#{@pkg_tag}.fc21.noarch.rpm | cpio -idmv") 114 | sh("cp etc/rc.d/init.d/rabbitmq-server.initd.supervisord /etc/rc.d/init.d/") 115 | sh("cp -a etc/contrail/supervisord_support_service_files/ /etc/contrail/") 116 | 117 | sh("rpm2cpio #{@ws}/contrail/controller/build/package-build/RPMS/noarch/contrail-openstack-control-#{@pkg_tag}.fc21.noarch.rpm | cpio -idmv") 118 | sh("cp -a etc/contrail/supervisord_support_service_files/ /etc/contrail/") 119 | sh("cp -a etc/contrail/supervisord_control_files/ /etc/contrail/") 120 | sh("cp etc/contrail/supervisord_config_files/* /etc/contrail/supervisord_config_files/") 121 | 122 | # XXX Install missing service files. 123 | sh("cp #{@ws}/contrail/controller/run/systemd/generator.late/*.service /run/systemd/generator.late/.") 124 | end 125 | 126 | def create_vhost_interface(ip, mask, gw) 127 | ifcfg = <, Netns : <%s>, IfName : <%s>, Args <%s>, Stdin %s\n", 59 | cmd, skelArgs.ContainerID, skelArgs.Netns, skelArgs.IfName, 60 | skelArgs.Args, string(skelArgs.StdinData))) 61 | glog.V(2).Info(fmt.Sprintf("Parsed information %+v\n", cniArgs)) 62 | 63 | return cniArgs, nil 64 | } 65 | 66 | /**************************************************************************** 67 | * ADD message handlers 68 | ****************************************************************************/ 69 | // Container handling for add message 70 | // 1. Create veth-pair interface 71 | // 2. Set MAC address for interface inside the container 72 | // 3. Apply IPAM configuration including, 73 | // - Assign IP address to the interface inside container 74 | // - Create the veth pairs and get host-os name for interface 75 | func processContainerAdd(netns ns.NetNS, cniArgs *args.CniArgs, mac string, 76 | typesResult *types.Result) (string, error) { 77 | // Validate MAC address 78 | hwAddr, mac_err := net.ParseMAC(mac) 79 | if mac_err != nil { 80 | return "", fmt.Errorf("Error parsing MAC address ", mac, " Error ", 81 | mac_err) 82 | } 83 | 84 | var hostIfName string 85 | // Configure the container 86 | err := netns.Do(func(hostNS ns.NetNS) error { 87 | // create veth pair in container and move host end into host netns 88 | link, _, err := ip.SetupVeth(cniArgs.IfName, cniArgs.ContrailArgs.Mtu, 89 | hostNS) 90 | if err != nil { 91 | return err 92 | } 93 | hostIfName = link.Attrs().Name 94 | 95 | // Update MAC address for the interface 96 | err = ip.SetHWAddr(cniArgs.IfName, hwAddr) 97 | if err != nil { 98 | return err 99 | } 100 | 101 | // Configure IPAM attributes 102 | err = ipam.ConfigureIface(cniArgs.IfName, typesResult) 103 | if err != nil { 104 | return err 105 | } 106 | 107 | return nil 108 | }) 109 | 110 | if err != nil { 111 | return "", err 112 | } 113 | 114 | return hostIfName, nil 115 | } 116 | 117 | // VRouter handling for add message 118 | func processVRouterAdd(agent *agent.Connection, cniArgs *args.CniArgs, 119 | hostIfName string) (error, error) { 120 | // Send AddVm IPC to contrail-vrouter agent 121 | return agent.AddVm(cniArgs.K8SArgs.PodUuid, cniArgs.K8SArgs.PodName, 122 | cniArgs.Netns, cniArgs.ContainerID, hostIfName, cniArgs.IfName) 123 | } 124 | 125 | // Get VMI parameters from VRouter 126 | func getVmiFromVRouter(agent *agent.Connection, 127 | cniArgs *args.CniArgs) (*agent.Result, error) { 128 | result, err := agent.GetVmiInfo(cniArgs.ContainerID, cniArgs.IfName, 129 | cniArgs.ContrailArgs.VRouterArgs.PollTimeout, 130 | cniArgs.ContrailArgs.VRouterArgs.PollRetries) 131 | if err != nil { 132 | return nil, err 133 | } 134 | 135 | return result, nil 136 | } 137 | 138 | // ADD command handler 139 | func CmdAdd(skelArgs *skel.CmdArgs) error { 140 | cniArgs, err := cmdCommon(skelArgs, "Add") 141 | defer glog.Flush() 142 | if err != nil { 143 | // Error parsing arguments is treated as Fatal error 144 | return err 145 | } 146 | 147 | // Get handle to the Namespace 148 | netns, err := ns.GetNS(cniArgs.Netns) 149 | if err != nil { 150 | // Error in opening namespace. Treat as fatal error 151 | msg := fmt.Sprintf("Failed to open netns %s: %v", cniArgs.Netns, err) 152 | glog.Error(msg) 153 | return fmt.Errorf(msg) 154 | } 155 | defer netns.Close() 156 | 157 | // Initialize connection to VRouter 158 | connection, err := agent.Init(cniArgs.K8SArgs.PodUuid, 159 | cniArgs.ContrailArgs.Dir, cniArgs.ContrailArgs.VRouterArgs.Ip, 160 | cniArgs.ContrailArgs.VRouterArgs.Port) 161 | if err != nil { 162 | glog.Error(fmt.Sprintf("Error initializing connection : %s", 163 | err.Error())) 164 | return err 165 | } 166 | defer connection.Close() 167 | 168 | // We need to get few arguments such as MAC address from VRouter. So, fetch 169 | // VMI parametrs from VRouter first 170 | result, err := getVmiFromVRouter(connection, cniArgs) 171 | if err != nil { 172 | glog.Error(fmt.Sprintf("Error querying vmi <%s> from VRouter : %s", 173 | cniArgs.K8SArgs.PodUuid, err.Error())) 174 | return err 175 | } 176 | 177 | // Translate result from VRouter format to Cni-Types 178 | typesResult := args.VRouterResultToCniResult(result) 179 | 180 | // Create the veth pairs and configure them 181 | // The container-part is added to the nets provided to CNI 182 | hostIfName, err := processContainerAdd(netns, cniArgs, result.Mac, 183 | typesResult) 184 | if err != nil { 185 | glog.Error(fmt.Sprintf("Error modifying container <%s> : %s", 186 | cniArgs.K8SArgs.PodUuid, err.Error())) 187 | return err 188 | } 189 | 190 | // Send add message to vrouter 191 | var warn error 192 | err, warn = processVRouterAdd(connection, cniArgs, hostIfName) 193 | if err != nil { 194 | glog.Error(fmt.Sprintf("Error adding interface to VRouter : %s", 195 | err.Error())) 196 | return err 197 | } 198 | 199 | if warn != nil { 200 | glog.Warning(fmt.Sprintf("Error adding interface to VRouter : %s", 201 | warn.Error())) 202 | } 203 | glog.V(2).Info(fmt.Sprintf("Result : %+v", typesResult)) 204 | return typesResult.Print() 205 | } 206 | 207 | /**************************************************************************** 208 | * DEL message handlers 209 | ****************************************************************************/ 210 | // Container DEL handler 211 | // Deletes interface from the container 212 | func processContainerDel(netns ns.NetNS, cniArgs *args.CniArgs) error { 213 | // Remove interface from the netlink 214 | var ipn *net.IPNet 215 | err := ns.WithNetNSPath(cniArgs.Netns, func(_ ns.NetNS) error { 216 | // Get the link 217 | iface, err := netlink.LinkByName(cniArgs.IfName) 218 | if err != nil { 219 | // Link already deleted. Nothing else to do 220 | return nil 221 | } 222 | 223 | // Delete the link from container 224 | ipn, err = ip.DelLinkByNameAddr(cniArgs.IfName, netlink.FAMILY_V4) 225 | if err != nil { 226 | if err = netlink.LinkDel(iface); err != nil { 227 | return fmt.Errorf("failed to delete %q: %v", cniArgs.IfName, 228 | err) 229 | } 230 | } 231 | 232 | return err 233 | }) 234 | if err != nil { 235 | return err 236 | } 237 | 238 | return nil 239 | } 240 | 241 | // VRouter DEL handler 242 | func processVRouterDel(agent *agent.Connection, cniArgs *args.CniArgs) error { 243 | // Let VRouter handle DEL command 244 | return agent.DelVm(cniArgs.K8SArgs.PodUuid) 245 | } 246 | 247 | // DEL command handler 248 | // Do best effort of cleanup ignoring any intermediate errors 249 | func CmdDel(skelArgs *skel.CmdArgs) error { 250 | var ret error 251 | cniArgs, err := cmdCommon(skelArgs, "Add") 252 | defer glog.Flush() 253 | if err != nil { 254 | // Error parsing arguments is treated as Fatal error 255 | return err 256 | } 257 | 258 | // Get handle to the Namespace 259 | netns, err := ns.GetNS(cniArgs.Netns) 260 | if err == nil { 261 | defer netns.Close() 262 | // Cleanup interfaces created 263 | // If namespace cannot be open, then most likely the interfaces are 264 | // already deleted. So no container cleanup is needed 265 | err = processContainerDel(netns, cniArgs) 266 | if err != nil { 267 | ret = fmt.Errorf("Error in cleanup of interfaces for DEL. Error ", 268 | err) 269 | } 270 | } 271 | 272 | connection, err := agent.Init(cniArgs.K8SArgs.PodUuid, 273 | cniArgs.ContrailArgs.Dir, cniArgs.ContrailArgs.VRouterArgs.Ip, 274 | cniArgs.ContrailArgs.VRouterArgs.Port) 275 | if err != nil { 276 | // Initialize connection to VRouter 277 | glog.Error(fmt.Sprintf("Error initializing connection : %s", 278 | err.Error())) 279 | } 280 | if err == nil { 281 | defer connection.Close() 282 | } 283 | 284 | // VRouter handling next 285 | err = processVRouterDel(connection, cniArgs) 286 | if err != nil { 287 | ret = fmt.Errorf("Error in VRouter cleanup for DEL. Error ", err) 288 | } 289 | 290 | if err != nil { 291 | glog.Error(ret.Error()) 292 | } else { 293 | glog.V(2).Info(fmt.Sprintf("Del successful")) 294 | } 295 | 296 | return ret 297 | } 298 | --------------------------------------------------------------------------------