Skip to content

Commit

Permalink
Add the initial binding/unbinding capability
Browse files Browse the repository at this point in the history
This patch adds the basic binding and unbinding capability for MidoNet
BEFORE v5.

Signed-off-by: Taku Fukushima <tfukushima@midokura.com>
  • Loading branch information
Taku Fukushima committed Nov 16, 2015
1 parent 1f38771 commit 1d5dbe5
Show file tree
Hide file tree
Showing 7 changed files with 556 additions and 0 deletions.
194 changes: 194 additions & 0 deletions binding.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
// Copyright 2015 Midokura SARL
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
"encoding/json"
"errors"
"flag"
"fmt"
"log"
"os"
"strings"
"time"

"github.com/samuel/go-zookeeper/zk"
)

// The address of the NSDB.
var nsdbAddresses = flag.String("zookeeper_hosts", "127.0.0.1:2181",
"The Addresses of ZooKeeper nodes separated by commas")

// The timout for the NSDB session in seconds.
const sessionTimeoutSec = 10

// The key for the ZOOM topology lock path.
const lockKey = "zoom-topology"

func connect() (*zk.Conn, <-chan zk.Event, error) {
addresses := strings.Split(*nsdbAddresses, ",")
for i := range addresses {
addresses[i] = strings.TrimSpace(addresses[i])
}
return zk.Connect(addresses,
time.Duration(sessionTimeoutSec)*time.Second)
}

func binding(portUuid, hostUuid, interfaceName string) error {
log.Println("binding port " + portUuid + " to " + interfaceName)

conn, _, err := connect()
if err != nil {
return err
}
defer conn.Close()

lock := zk.NewLock(conn, GetLockPath(lockKey), zk.WorldACL(zk.PermAll))
if err = lock.Lock(); err != nil {
return err
}
defer lock.Unlock()

portPath := GetPortPath(portUuid)
var data []byte
if data, _, err = conn.Get(portPath); err != nil {
fmt.Fprintf(os.Stderr, "Error on getting port: %s\n", err.Error())
return err
}
port := &WrappedPort{}
if err = json.Unmarshal(data, port); err != nil {
fmt.Fprintf(os.Stderr, "Error on deserializing port: %s\n", err.Error())
return err
}

port.Data.HostId = hostUuid
port.Data.InterfaceName = interfaceName

updatedPort, err := json.Marshal(port)
if err != nil {
fmt.Fprintf(os.Stderr, "Error on serializing port: %s\n", err.Error())
return err
}

if _, err = conn.Set(portPath, updatedPort, -1); err != nil {
fmt.Fprintf(os.Stderr, "Error on setting port: %s\n", err.Error())
return err
}

vrnMappingPath := GetVrnMappingPath(hostUuid, portUuid)
var exists bool
if exists, _, err = conn.Exists(vrnMappingPath); err != nil {
fmt.Fprintf(os.Stderr, "Error on examining vrnMapping %s\n", err.Error())
return err
}
var vrnMappingData []byte
vrnMapping := &WrappedVrnMapping{}
if exists {
if vrnMappingData, _, err = conn.Get(vrnMappingPath); err != nil {
fmt.Fprintf(os.Stderr, "Error on getting vrnMapping %s\n", err.Error())
return err
}
log.Println(fmt.Sprintf("Got vrnMapping data: %s", vrnMappingData))
if err = json.Unmarshal(vrnMappingData, vrnMapping); err != nil {
fmt.Fprintf(os.Stderr, "Error on deserializing vrnMapping: %s\n", err.Error())
return err
}
} else {
data := &VrnMapping{}
data.VirtualPortId = portUuid
data.LocalDeviceName = interfaceName
vrnMapping.Data = data
vrnMapping.Version = port.Version
}
updatedVrnMapping, err := json.Marshal(vrnMapping)
if err != nil {
fmt.Fprintf(os.Stderr, "Error on deserializing vrnMapping: %s\n", err.Error())
return err
}
if exists {
if _, err = conn.Set(vrnMappingPath, updatedVrnMapping, -1); err != nil {
fmt.Fprintf(os.Stderr, "Error on setting vrnMapping: %s\n", err.Error())
return err
}
} else {
if _, err = conn.Create(vrnMappingPath, updatedVrnMapping, 0, zk.WorldACL(zk.PermAll)); err != nil {
fmt.Fprintf(os.Stderr, "Error on creating a new vrnMapping: %s\n", err.Error())
return err
}
}

log.Println("Succeded to bind the port")

return nil
}

func unbinding(portUuid, hostUuid string) error {
log.Println("unbinding port " + portUuid)

conn, _, err := connect()
if err != nil {
return err
}
defer conn.Close()

lock := zk.NewLock(conn, GetLockPath(lockKey), zk.WorldACL(zk.PermAll))
if err = lock.Lock(); err != nil {
return err
}
defer lock.Unlock()

portPath := GetPortPath(portUuid)
var data []byte
if data, _, err = conn.Get(portPath); err != nil {
fmt.Fprintf(os.Stderr, "Error on getting port: %s\n", err.Error())
return err
}
port := &WrappedPort{}
if err = json.Unmarshal(data, port); err != nil {
fmt.Fprintf(os.Stderr, "Error on deserializing port: %s\n", err.Error())
return err
}

if port.Data.HostId != hostUuid {
return errors.New("The given host ID didn't match with one in NSDB")
}
port.Data.InterfaceName = ""

updatedPort, err := json.Marshal(port)
if err != nil {
fmt.Fprintf(os.Stderr, "Error on serializing port: %s\n", err.Error())
return err
}

if _, err = conn.Set(portPath, updatedPort, -1); err != nil {
return err
}

vrnMappingPath := GetVrnMappingPath(hostUuid, portUuid)
var exists bool
if exists, _, err = conn.Exists(vrnMappingPath); err != nil {
fmt.Fprintf(os.Stderr, "Error on examining vrnMapping %s\n", err.Error())
return err
}
if exists {
if err = conn.Delete(vrnMappingPath, -1); err != nil {
fmt.Fprintf(os.Stderr, "Error on deleging vrnMapping %s\n", err.Error())
return err
}
}
log.Println("Succeded to unbind the port")

return nil
}
145 changes: 145 additions & 0 deletions commands.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
// Copyright 2015 Midokura SARL
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
"flag"
"fmt"
"os"

"github.com/mitchellh/cli"
ini "gopkg.in/ini.v1"
)

// const HostUuid = "b53b82f3-ebe5-4e8d-9088-8b3ae4ade76e"w
var hostUuid = flag.String("host_uuid", "", "The UUID of the host (required).")

// the name of the command.
const CommandName = "mm-ctl"

func loadConfig() error {
midolmanConfigPath := flag.String("config", "/etc/midolman/midolman.conf",
"The location of the Midolamn config file")
hostConfigPath := flag.String("host-config", "/etc/midonet_host_id.properties",
"The location of the host UUID config file")
flag.Parse()

cfg, err := ini.Load(*midolmanConfigPath, *hostConfigPath)
if err != nil {
return err
}
zkSection, err := cfg.GetSection("zookeeper")
if err != nil {
return err
}
*rootPath = zkSection.Key("root_key").String()
*nsdbAddresses = zkSection.Key("zookeeper_hosts").String()
*hostUuid = cfg.Section("").Key("host_uuid").String()

return nil
}

// The "binding" command.
type Binding struct{}

func (b *Binding) Help() string {
return fmt.Sprintf(`Usage: %s bind c5951b28-0d72-41fa-9f29-650bbeae2ed3 8a1c0540-veth"
binding takes the Neutron port UUID and the interface name and binds the port to
the interface.
`, CommandName)
}

func (b *Binding) Run(args []string) int {
bindCmdFlag := flag.NewFlagSet("bind", flag.ContinueOnError)
if *hostUuid == "" {
bindCmdFlag.StringVar(hostUuid, "host_uuid", "", "The UUID of the host (required).")
}
// bindCmdFlag.StringVar(nsdbAddresses, "zookeeper_hosts", "127.0.0.1:2181",
// "The Addresses of ZooKeeper nodes separated by commas")
// bindCmdFlag.StringVar(rootPath, "root_key", "/midonet/v1", "The root path of the NSDB")

bindCmdFlag.Parse(args)

if bindCmdFlag.NArg() != 2 {
fmt.Fprintf(os.Stderr, "bind takes exactly 2 args but %d args are given.\n", bindCmdFlag.NArg())
return 1
}
portUuid := bindCmdFlag.Arg(0)
fmt.Printf("host_uuid: %s\n", *hostUuid)
if *hostUuid == "" {
fmt.Fprintf(os.Stderr, "Host UUID is required.\n")
return 1
}
interfaceName := bindCmdFlag.Arg(1)

if err := binding(portUuid, *hostUuid, interfaceName); err != nil {
fmt.Fprintf(os.Stderr, "Error on binding the port: %s\n", err.Error())
return 1
}

return 0
}

func (b *Binding) Synopsis() string {
return "bind <port-uuid> <interface-name>"
}

// The "unbinding" command.
type Unbinding struct{}

func (u *Unbinding) Help() string {
return fmt.Sprintf(`Usage: %s unbind c5951b28-0d72-41fa-9f29-650bbeae2ed3
unbinding takes the Neutron port UUID and unbinds the port.
`, CommandName)
}

func (u *Unbinding) Run(args []string) int {
unbindCmdFlag := flag.NewFlagSet("unbind", flag.ContinueOnError)
if *hostUuid == "" {
unbindCmdFlag.StringVar(hostUuid, "host_uuid", "", "The UUID of the host (required).")
}
// unbindCmdFlag.StringVar(nsdbAddresses, "zookeeper_hosts", "127.0.0.1:2181",
// "The Addresses of ZooKeeper nodes separated by commas")
// unbindCmdFlag.StringVar(rootPath, "root_key", "/midonet/v1", "The root path of the NSDB")

unbindCmdFlag.Parse(args)

if unbindCmdFlag.NArg() != 1 {
fmt.Fprintf(os.Stderr, "unbind takes exactly 1 arg but %d args are given.\n", unbindCmdFlag.NArg())
return 1
}
portUuid := unbindCmdFlag.Arg(0)

if err := unbinding(portUuid, *hostUuid); err != nil {
fmt.Fprintf(os.Stderr, "Error on unbinding the port: %s\n", err.Error())
return 1
}

return 0
}

func (u *Unbinding) Synopsis() string {
return "unbind <port-uuid>"
}

func bindingCommand() (cli.Command, error) {
return &Binding{}, nil
}

func unbindingCommand() (cli.Command, error) {
return &Unbinding{}, nil
}
50 changes: 50 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// Copyright 2015 Midokura SARL
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
"flag"
"fmt"
"os"

"github.com/mitchellh/cli"
)

func runCommand() int {
// iniflags.Parse()
if err := loadConfig(); err != nil {
fmt.Fprintf(os.Stderr, "Failed to load the config files: %s\n", err.Error())
return 1
}

c := cli.NewCLI(CommandName, Version)
c.Args = flag.Args()

c.Commands = map[string]cli.CommandFactory{
"bind": bindingCommand,
"unbind": unbindingCommand,
}
exitStatus, err := c.Run()
if err != nil {
fmt.Fprintf(os.Stderr, "Error executing CLI: %s\n", err.Error())
return 1
}

return exitStatus
}

func main() {
os.Exit(runCommand())
}
Loading

0 comments on commit 1d5dbe5

Please sign in to comment.