cord-776 create build / runtime containers for autmation uservices
Change-Id: I246973192adef56a250ffe93a5f65fff488840c1
diff --git a/switchq/vendor/github.com/juju/gomaasapi/testservice_subnets.go b/switchq/vendor/github.com/juju/gomaasapi/testservice_subnets.go
new file mode 100644
index 0000000..5438669
--- /dev/null
+++ b/switchq/vendor/github.com/juju/gomaasapi/testservice_subnets.go
@@ -0,0 +1,396 @@
+// Copyright 2012-2016 Canonical Ltd.
+// Licensed under the LGPLv3, see LICENCE file for details.
+
+package gomaasapi
+
+import (
+ "encoding/json"
+ "fmt"
+ "io"
+ "net"
+ "net/http"
+ "net/url"
+ "regexp"
+ "sort"
+ "strings"
+)
+
+func getSubnetsEndpoint(version string) string {
+ return fmt.Sprintf("/api/%s/subnets/", version)
+}
+
+// CreateSubnet is used to receive new subnets via the MAAS API
+type CreateSubnet struct {
+ DNSServers []string `json:"dns_servers"`
+ Name string `json:"name"`
+ Space string `json:"space"`
+ GatewayIP string `json:"gateway_ip"`
+ CIDR string `json:"cidr"`
+
+ // VLAN this subnet belongs to. Currently ignored.
+ // TODO: Defaults to the default VLAN
+ // for the provided fabric or defaults to the default VLAN
+ // in the default fabric.
+ VLAN *uint `json:"vlan"`
+
+ // Fabric for the subnet. Currently ignored.
+ // TODO: Defaults to the fabric the provided
+ // VLAN belongs to or defaults to the default fabric.
+ Fabric *uint `json:"fabric"`
+
+ // VID of the VLAN this subnet belongs to. Currently ignored.
+ // TODO: Only used when vlan
+ // is not provided. Picks the VLAN with this VID in the provided
+ // fabric or the default fabric if one is not given.
+ VID *uint `json:"vid"`
+
+ // This is used for updates (PUT) and is ignored by create (POST)
+ ID uint `json:"id"`
+}
+
+// TestSubnet is the MAAS API subnet representation
+type TestSubnet struct {
+ DNSServers []string `json:"dns_servers"`
+ Name string `json:"name"`
+ Space string `json:"space"`
+ VLAN TestVLAN `json:"vlan"`
+ GatewayIP string `json:"gateway_ip"`
+ CIDR string `json:"cidr"`
+
+ ResourceURI string `json:"resource_uri"`
+ ID uint `json:"id"`
+ InUseIPAddresses []IP `json:"-"`
+ FixedAddressRanges []AddressRange `json:"-"`
+}
+
+// AddFixedAddressRange adds an AddressRange to the list of fixed address ranges
+// that subnet stores.
+func (server *TestServer) AddFixedAddressRange(subnetID uint, ar AddressRange) {
+ subnet := server.subnets[subnetID]
+ ar.startUint = IPFromString(ar.Start).UInt64()
+ ar.endUint = IPFromString(ar.End).UInt64()
+ subnet.FixedAddressRanges = append(subnet.FixedAddressRanges, ar)
+ server.subnets[subnetID] = subnet
+}
+
+// subnetsHandler handles requests for '/api/<version>/subnets/'.
+func subnetsHandler(server *TestServer, w http.ResponseWriter, r *http.Request) {
+ var err error
+ values, err := url.ParseQuery(r.URL.RawQuery)
+ checkError(err)
+ op := values.Get("op")
+ includeRangesString := strings.ToLower(values.Get("include_ranges"))
+ subnetsURLRE := regexp.MustCompile(`/subnets/(.+?)/`)
+ subnetsURLMatch := subnetsURLRE.FindStringSubmatch(r.URL.Path)
+ subnetsURL := getSubnetsEndpoint(server.version)
+
+ var ID uint
+ var gotID bool
+ if subnetsURLMatch != nil {
+ ID, err = NameOrIDToID(subnetsURLMatch[1], server.subnetNameToID, 1, uint(len(server.subnets)))
+
+ if err != nil {
+ http.NotFoundHandler().ServeHTTP(w, r)
+ return
+ }
+
+ gotID = true
+ }
+
+ var includeRanges bool
+ switch includeRangesString {
+ case "true", "yes", "1":
+ includeRanges = true
+ }
+
+ switch r.Method {
+ case "GET":
+ w.Header().Set("Content-Type", "application/vnd.api+json")
+ if len(server.subnets) == 0 {
+ // Until a subnet is registered, behave as if the endpoint
+ // does not exist. This way we can simulate older MAAS
+ // servers that do not support subnets.
+ http.NotFoundHandler().ServeHTTP(w, r)
+ return
+ }
+
+ if r.URL.Path == subnetsURL {
+ var subnets []TestSubnet
+ for i := uint(1); i < server.nextSubnet; i++ {
+ s, ok := server.subnets[i]
+ if ok {
+ subnets = append(subnets, s)
+ }
+ }
+ PrettyJsonWriter(subnets, w)
+ } else if gotID == false {
+ w.WriteHeader(http.StatusBadRequest)
+ } else {
+ switch op {
+ case "unreserved_ip_ranges":
+ PrettyJsonWriter(server.subnetUnreservedIPRanges(server.subnets[ID]), w)
+ case "reserved_ip_ranges":
+ PrettyJsonWriter(server.subnetReservedIPRanges(server.subnets[ID]), w)
+ case "statistics":
+ PrettyJsonWriter(server.subnetStatistics(server.subnets[ID], includeRanges), w)
+ default:
+ PrettyJsonWriter(server.subnets[ID], w)
+ }
+ }
+ checkError(err)
+ case "POST":
+ server.NewSubnet(r.Body)
+ case "PUT":
+ server.UpdateSubnet(r.Body)
+ case "DELETE":
+ delete(server.subnets, ID)
+ w.WriteHeader(http.StatusOK)
+ default:
+ w.WriteHeader(http.StatusBadRequest)
+ }
+}
+
+type addressList []IP
+
+func (a addressList) Len() int { return len(a) }
+func (a addressList) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
+func (a addressList) Less(i, j int) bool { return a[i].UInt64() < a[j].UInt64() }
+
+// AddressRange is used to generate reserved IP address range lists
+type AddressRange struct {
+ Start string `json:"start"`
+ startUint uint64
+ End string `json:"end"`
+ endUint uint64
+ Purpose []string `json:"purpose,omitempty"`
+ NumAddresses uint `json:"num_addresses"`
+}
+
+// AddressRangeList is a list of AddressRange
+type AddressRangeList struct {
+ ar []AddressRange
+}
+
+// Append appends a new AddressRange to an AddressRangeList
+func (ranges *AddressRangeList) Append(startIP, endIP IP) {
+ var i AddressRange
+ i.Start, i.End = startIP.String(), endIP.String()
+ i.startUint, i.endUint = startIP.UInt64(), endIP.UInt64()
+ i.NumAddresses = uint(1 + endIP.UInt64() - startIP.UInt64())
+ i.Purpose = startIP.Purpose
+ ranges.ar = append(ranges.ar, i)
+}
+
+func appendRangesToIPList(subnet TestSubnet, ipAddresses *[]IP) {
+ for _, r := range subnet.FixedAddressRanges {
+ for v := r.startUint; v <= r.endUint; v++ {
+ ip := IPFromInt64(v)
+ ip.Purpose = r.Purpose
+ *ipAddresses = append(*ipAddresses, ip)
+ }
+ }
+}
+
+func (server *TestServer) subnetUnreservedIPRanges(subnet TestSubnet) []AddressRange {
+ // Make a sorted copy of subnet.InUseIPAddresses
+ ipAddresses := make([]IP, len(subnet.InUseIPAddresses))
+ copy(ipAddresses, subnet.InUseIPAddresses)
+ appendRangesToIPList(subnet, &ipAddresses)
+ sort.Sort(addressList(ipAddresses))
+
+ // We need the first and last address in the subnet
+ var ranges AddressRangeList
+ var startIP, endIP, lastUsableIP IP
+
+ _, ipNet, err := net.ParseCIDR(subnet.CIDR)
+ checkError(err)
+ startIP = IPFromNetIP(ipNet.IP)
+ // Start with the lowest usable address in the range, which is 1 above
+ // what net.ParseCIDR will give back.
+ startIP.SetUInt64(startIP.UInt64() + 1)
+
+ ones, bits := ipNet.Mask.Size()
+ set := ^((^uint64(0)) << uint(bits-ones))
+
+ // The last usable address is one below the broadcast address, which is
+ // what you get by bitwise ORing 'set' with any IP address in the subnet.
+ lastUsableIP.SetUInt64((startIP.UInt64() | set) - 1)
+
+ for _, endIP = range ipAddresses {
+ end := endIP.UInt64()
+
+ if endIP.UInt64() == startIP.UInt64() {
+ if endIP.UInt64() != lastUsableIP.UInt64() {
+ startIP.SetUInt64(end + 1)
+ }
+ continue
+ }
+
+ if end == lastUsableIP.UInt64() {
+ continue
+ }
+
+ ranges.Append(startIP, IPFromInt64(end-1))
+ startIP.SetUInt64(end + 1)
+ }
+
+ if startIP.UInt64() != lastUsableIP.UInt64() {
+ ranges.Append(startIP, lastUsableIP)
+ }
+
+ return ranges.ar
+}
+
+func (server *TestServer) subnetReservedIPRanges(subnet TestSubnet) []AddressRange {
+ var ranges AddressRangeList
+ var startIP, thisIP IP
+
+ // Make a sorted copy of subnet.InUseIPAddresses
+ ipAddresses := make([]IP, len(subnet.InUseIPAddresses))
+ copy(ipAddresses, subnet.InUseIPAddresses)
+ appendRangesToIPList(subnet, &ipAddresses)
+ sort.Sort(addressList(ipAddresses))
+ if len(ipAddresses) == 0 {
+ ar := ranges.ar
+ if ar == nil {
+ ar = []AddressRange{}
+ }
+ return ar
+ }
+
+ startIP = ipAddresses[0]
+ lastIP := ipAddresses[0]
+ for _, thisIP = range ipAddresses {
+ var purposeMissmatch bool
+ for i, p := range thisIP.Purpose {
+ if startIP.Purpose[i] != p {
+ purposeMissmatch = true
+ }
+ }
+ if (thisIP.UInt64() != lastIP.UInt64() && thisIP.UInt64() != lastIP.UInt64()+1) || purposeMissmatch {
+ ranges.Append(startIP, lastIP)
+ startIP = thisIP
+ }
+ lastIP = thisIP
+ }
+
+ if len(ranges.ar) == 0 || ranges.ar[len(ranges.ar)-1].endUint != lastIP.UInt64() {
+ ranges.Append(startIP, lastIP)
+ }
+
+ return ranges.ar
+}
+
+// SubnetStats holds statistics about a subnet
+type SubnetStats struct {
+ NumAvailable uint `json:"num_available"`
+ LargestAvailable uint `json:"largest_available"`
+ NumUnavailable uint `json:"num_unavailable"`
+ TotalAddresses uint `json:"total_addresses"`
+ Usage float32 `json:"usage"`
+ UsageString string `json:"usage_string"`
+ Ranges []AddressRange `json:"ranges"`
+}
+
+func (server *TestServer) subnetStatistics(subnet TestSubnet, includeRanges bool) SubnetStats {
+ var stats SubnetStats
+ _, ipNet, err := net.ParseCIDR(subnet.CIDR)
+ checkError(err)
+
+ ones, bits := ipNet.Mask.Size()
+ stats.TotalAddresses = (1 << uint(bits-ones)) - 2
+ stats.NumUnavailable = uint(len(subnet.InUseIPAddresses))
+ stats.NumAvailable = stats.TotalAddresses - stats.NumUnavailable
+ stats.Usage = float32(stats.NumUnavailable) / float32(stats.TotalAddresses)
+ stats.UsageString = fmt.Sprintf("%0.1f%%", stats.Usage*100)
+
+ // Calculate stats.LargestAvailable - the largest contiguous block of IP addresses available
+ reserved := server.subnetUnreservedIPRanges(subnet)
+ for _, addressRange := range reserved {
+ if addressRange.NumAddresses > stats.LargestAvailable {
+ stats.LargestAvailable = addressRange.NumAddresses
+ }
+ }
+
+ if includeRanges {
+ stats.Ranges = reserved
+ }
+
+ return stats
+}
+
+func decodePostedSubnet(subnetJSON io.Reader) CreateSubnet {
+ var postedSubnet CreateSubnet
+ decoder := json.NewDecoder(subnetJSON)
+ err := decoder.Decode(&postedSubnet)
+ checkError(err)
+ if postedSubnet.DNSServers == nil {
+ postedSubnet.DNSServers = []string{}
+ }
+ return postedSubnet
+}
+
+// UpdateSubnet creates a subnet in the test server
+func (server *TestServer) UpdateSubnet(subnetJSON io.Reader) TestSubnet {
+ postedSubnet := decodePostedSubnet(subnetJSON)
+ updatedSubnet := subnetFromCreateSubnet(postedSubnet)
+ server.subnets[updatedSubnet.ID] = updatedSubnet
+ return updatedSubnet
+}
+
+// NewSubnet creates a subnet in the test server
+func (server *TestServer) NewSubnet(subnetJSON io.Reader) *TestSubnet {
+ postedSubnet := decodePostedSubnet(subnetJSON)
+ newSubnet := subnetFromCreateSubnet(postedSubnet)
+ newSubnet.ID = server.nextSubnet
+ server.subnets[server.nextSubnet] = newSubnet
+ server.subnetNameToID[newSubnet.Name] = newSubnet.ID
+
+ server.nextSubnet++
+ return &newSubnet
+}
+
+// NodeNetworkInterface represents a network interface attached to a node
+type NodeNetworkInterface struct {
+ Name string `json:"name"`
+ Links []NetworkLink `json:"links"`
+}
+
+// Node represents a node
+type Node struct {
+ SystemID string `json:"system_id"`
+ Interfaces []NodeNetworkInterface `json:"interface_set"`
+}
+
+// NetworkLink represents a MAAS network link
+type NetworkLink struct {
+ ID uint `json:"id"`
+ Mode string `json:"mode"`
+ Subnet *TestSubnet `json:"subnet"`
+}
+
+// SetNodeNetworkLink records that the given node + interface are in subnet
+func (server *TestServer) SetNodeNetworkLink(SystemID string, nodeNetworkInterface NodeNetworkInterface) {
+ for i, ni := range server.nodeMetadata[SystemID].Interfaces {
+ if ni.Name == nodeNetworkInterface.Name {
+ server.nodeMetadata[SystemID].Interfaces[i] = nodeNetworkInterface
+ return
+ }
+ }
+ n := server.nodeMetadata[SystemID]
+ n.Interfaces = append(n.Interfaces, nodeNetworkInterface)
+ server.nodeMetadata[SystemID] = n
+}
+
+// subnetFromCreateSubnet creates a subnet in the test server
+func subnetFromCreateSubnet(postedSubnet CreateSubnet) TestSubnet {
+ var newSubnet TestSubnet
+ newSubnet.DNSServers = postedSubnet.DNSServers
+ newSubnet.Name = postedSubnet.Name
+ newSubnet.Space = postedSubnet.Space
+ //TODO: newSubnet.VLAN = server.postedSubnetVLAN
+ newSubnet.GatewayIP = postedSubnet.GatewayIP
+ newSubnet.CIDR = postedSubnet.CIDR
+ newSubnet.ID = postedSubnet.ID
+ return newSubnet
+}