// Package ipcmd provides a wrapper around the "ip" command. package ipcmd import ( "fmt" "regexp" "strings" "github.com/golang/glog" "k8s.io/kubernetes/pkg/util/exec" ) var addressRegexp *regexp.Regexp func init() { addressRegexp = regexp.MustCompile("inet ([0-9.]*/[0-9]*) ") } type Transaction struct { execer exec.Interface link string err error } // NewTransaction begins a new transaction for a given interface. If an error // occurs at any step in the transaction, it will be recorded until // EndTransaction(), and any further calls on the transaction will be ignored. func NewTransaction(execer exec.Interface, link string) *Transaction { return &Transaction{execer: execer, link: link} } func (tx *Transaction) exec(args []string) (string, error) { if tx.err != nil { return "", tx.err } ipcmdPath, err := tx.execer.LookPath("ip") if err != nil { tx.err = fmt.Errorf("ip is not installed") return "", tx.err } glog.V(5).Infof("Executing: %s %s", ipcmdPath, strings.Join(args, " ")) var output []byte output, tx.err = tx.execer.Command(ipcmdPath, args...).CombinedOutput() if tx.err != nil { glog.V(5).Infof("Error executing %s: %s", ipcmdPath, string(output)) } return string(output), tx.err } // AddLink creates the interface associated with the transaction, optionally // with additional properties. func (tx *Transaction) AddLink(args ...string) { tx.exec(append([]string{"link", "add", tx.link}, args...)) } // DeleteLink deletes the interface associated with the transaction. (It is an // error if the interface does not exist.) func (tx *Transaction) DeleteLink() { tx.exec([]string{"link", "del", tx.link}) } // SetLink sets the indicated properties on the interface. func (tx *Transaction) SetLink(args ...string) { tx.exec(append([]string{"link", "set", tx.link}, args...)) } // AddAddress adds an address to the interface. func (tx *Transaction) AddAddress(cidr string, args ...string) { tx.exec(append([]string{"addr", "add", cidr, "dev", tx.link}, args...)) } // DeleteAddress deletes an address from the interface. (It is an error if the // address does not exist.) func (tx *Transaction) DeleteAddress(cidr string, args ...string) { tx.exec(append([]string{"addr", "del", cidr, "dev", tx.link}, args...)) } // GetAddresses returns the IPv4 addresses associated with the interface. Since // this function has a return value, it also returns an error immediately if an // error occurs. func (tx *Transaction) GetAddresses() ([]string, error) { out, err := tx.exec(append([]string{"addr", "show", "dev", tx.link})) if err != nil { return nil, err } matches := addressRegexp.FindAllStringSubmatch(out, -1) addrs := make([]string, len(matches)) for i, match := range matches { addrs[i] = match[1] } return addrs, nil } // AddRoute adds a route to the interface. func (tx *Transaction) AddRoute(cidr string, args ...string) { tx.exec(append([]string{"route", "add", cidr, "dev", tx.link}, args...)) } // DeleteRoute deletes a route from the interface. (It is an error if the route // does not exist.) func (tx *Transaction) DeleteRoute(cidr string, args ...string) { tx.exec(append([]string{"route", "del", cidr, "dev", tx.link}, args...)) } // GetRoutes returns the IPv4 routes associated with the interface (as an array // of route descriptions in the format output by "ip route show"). Since this // function has a return value, it also returns an error immediately if an error // occurs. func (tx *Transaction) GetRoutes() ([]string, error) { out, err := tx.exec(append([]string{"route", "show", "dev", tx.link})) if err != nil { return nil, err } lines := strings.Split(out, "\n") return lines[:len(lines)-1], nil } // AddSlave adds the indicated slave interface to the bridge, bond, or team // interface associated with the transaction. func (tx *Transaction) AddSlave(slave string) { tx.exec([]string{"link", "set", slave, "master", tx.link}) } // AddSlave remotes the indicated slave interface from the bridge, bond, or team // interface associated with the transaction. (No error occurs if the interface // is not actually a slave of the transaction interface.) func (tx *Transaction) DeleteSlave(slave string) { tx.exec([]string{"link", "set", slave, "nomaster"}) } // IgnoreError causes any error on the transaction to be discarded, in case you // don't care about errors from a particular command and want further commands // to be executed regardless. func (tx *Transaction) IgnoreError() { tx.err = nil } // EndTransaction ends a transaction and returns any error that occurred during // the transaction. You should not use the transaction again after calling this // function. func (tx *Transaction) EndTransaction() error { err := tx.err tx.err = nil return err }