From 105b9b87e152f7f46240e9b926e2b9da0cbccbf0 Mon Sep 17 00:00:00 2001 From: Christian Muirhead Date: Tue, 23 Aug 2016 23:55:36 +0100 Subject: [PATCH] Enable devices to have multiple MAC addresses This is needed to more closely match the behaviour of the real server. Also change the query by mac address to match the server. If you query with multiple addresses that match a device, you'll get that device multiple times. Part of http://pad.lv/1585878 --- testservice.go | 91 ++++++++++++++++++++++++++++++++------------- testservice_test.go | 55 +++++++++++++++++++++++---- 2 files changed, 113 insertions(+), 33 deletions(-) diff --git a/testservice.go b/testservice.go index cc6dc5f..aa582da 100644 --- a/testservice.go +++ b/testservice.go @@ -108,11 +108,11 @@ type TestServer struct { } type TestDevice struct { - IPAddresses []string - SystemId string - MACAddress string - Parent string - Hostname string + IPAddresses []string + SystemId string + MACAddresses []string + Parent string + Hostname string // Not part of the device definition but used by the template. APIVersion string @@ -663,11 +663,8 @@ func devicesTopLevelHandler(server *TestServer, w http.ResponseWriter, r *http.R } } -func macMatches(device *TestDevice, macs []string, hasMac bool) bool { - if !hasMac { - return true - } - return contains(macs, device.MACAddress) +func macMatches(mac string, device *TestDevice) bool { + return contains(device.MACAddresses, mac) } // deviceListingHandler handles requests for '/devices/'. @@ -676,13 +673,25 @@ func deviceListingHandler(server *TestServer, w http.ResponseWriter, r *http.Req checkError(err) // TODO(mfoord): support filtering by hostname and id macs, hasMac := values["mac_address"] - var matchedDevices []string - for _, device := range server.devices { - if macMatches(device, macs, hasMac) { - matchedDevices = append(matchedDevices, renderDevice(device)) + var matchedDevices []*TestDevice + if !hasMac { + for _, device := range server.devices { + matchedDevices = append(matchedDevices, device) + } + } else { + for _, mac := range macs { + for _, device := range server.devices { + if macMatches(mac, device) { + matchedDevices = append(matchedDevices, device) + } + } } } - json := fmt.Sprintf("[%v]", strings.Join(matchedDevices, ", ")) + deviceChunks := make([]string, len(matchedDevices)) + for i := range matchedDevices { + deviceChunks[i] = renderDevice(matchedDevices[i]) + } + json := fmt.Sprintf("[%v]", strings.Join(deviceChunks, ", ")) w.WriteHeader(http.StatusOK) fmt.Fprint(w, json) @@ -696,16 +705,31 @@ var templateFuncs = template.FuncMap{ } return strings.Join(pieces, ", ") }, + "last": func(items []string) []string { + if len(items) == 0 { + return []string{} + } + return items[len(items)-1:] + }, + "allButLast": func(items []string) []string { + if len(items) < 2 { + return []string{} + } + return items[0 : len(items)-1] + }, } const ( // The json template for generating new devices. // TODO(mfoord): set resource_uri in MAC addresses deviceTemplate = `{ - "macaddress_set": [ + "macaddress_set": [{{range .MACAddresses | allButLast}} { - "mac_address": "{{.MACAddress}}" - } + "mac_address": "{{.}}" + },{{end}}{{range .MACAddresses | last}} + { + "mac_address": "{{.}}" + }{{end}} ], "zone": { "resource_uri": "/MAAS/api/{{.APIVersion}}/zones/default/", @@ -741,6 +765,23 @@ func getValue(values url.Values, value string) (string, bool) { return result[0], true } +func getValues(values url.Values, key string) ([]string, bool) { + result, hasResult := values[key] + if !hasResult { + return nil, false + } + var output []string + for _, val := range result { + if val != "" { + output = append(output, val) + } + } + if len(output) == 0 { + return nil, false + } + return output, true +} + // newDeviceHandler creates, stores and returns new devices. func newDeviceHandler(server *TestServer, w http.ResponseWriter, r *http.Request) { err := r.ParseForm() @@ -753,23 +794,23 @@ func newDeviceHandler(server *TestServer, w http.ResponseWriter, r *http.Request systemId := fmt.Sprintf("node-%v", uuid) // At least one MAC address must be specified. // TODO(mfoord) we only support a single MAC in the test server. - mac, hasMac := getValue(values, "mac_addresses") + macs, hasMacs := getValues(values, "mac_addresses") // hostname and parent are optional. // TODO(mfoord): we require both to be set in the test server. hostname, hasHostname := getValue(values, "hostname") parent, hasParent := getValue(values, "parent") - if !hasHostname || !hasMac || !hasParent { + if !hasHostname || !hasMacs || !hasParent { w.WriteHeader(http.StatusBadRequest) return } device := &TestDevice{ - MACAddress: mac, - APIVersion: server.version, - Parent: parent, - Hostname: hostname, - SystemId: systemId, + MACAddresses: macs, + APIVersion: server.version, + Parent: parent, + Hostname: hostname, + SystemId: systemId, } deviceJSON := renderDevice(device) diff --git a/testservice_test.go b/testservice_test.go index 35addca..c5c14de 100644 --- a/testservice_test.go +++ b/testservice_test.go @@ -66,10 +66,12 @@ func (suite *TestServerSuite) TestSetVersionJSON(c *C) { c.Assert(string(content), Equals, capabilities) } -func (suite *TestServerSuite) createDevice(c *C, mac, hostname, parent string) string { +func (suite *TestServerSuite) createDevice(c *C, macs, hostname, parent string) string { devicesURL := fmt.Sprintf("/api/%s/devices/", suite.server.version) + "?op=new" values := url.Values{} - values.Add("mac_addresses", mac) + for _, mac := range strings.Split(macs, ",") { + values.Add("mac_addresses", mac) + } values.Add("hostname", hostname) values.Add("parent", parent) result := suite.post(c, devicesURL, values) @@ -110,15 +112,19 @@ func (suite *TestServerSuite) get(c *C, url string) JSONObject { return result } -func checkDevice(c *C, device map[string]JSONObject, mac, hostname, parent string) { +func checkDevice(c *C, device map[string]JSONObject, macs, hostname, parent string) { + macSlice := strings.Split(macs, ",") macArray, err := device["macaddress_set"].GetArray() c.Assert(err, IsNil) - c.Assert(macArray, HasLen, 1) - macMap, err := macArray[0].GetMap() - c.Assert(err, IsNil) + c.Assert(macArray, HasLen, len(macSlice)) - actualMac := getString(c, macMap, "mac_address") - c.Assert(actualMac, Equals, mac) + for i := range macArray { + macMap, err := macArray[i].GetMap() + c.Assert(err, IsNil) + + actualMac := getString(c, macMap, "mac_address") + c.Check(actualMac, Equals, macSlice[i]) + } actualParent := getString(c, device, "parent") c.Assert(actualParent, Equals, parent) @@ -205,6 +211,18 @@ func (suite *TestServerSuite) TestGetDevice(c *C) { c.Assert(actualId, Equals, systemId) } +func (suite *TestServerSuite) TestGetDeviceWithMultipleMacs(c *C) { + systemId := suite.createDevice(c, "foo,boo", "bar", "baz") + deviceURL := fmt.Sprintf("/api/%v/devices/%v/", suite.server.version, systemId) + + result := suite.get(c, deviceURL) + resultMap, err := result.GetMap() + c.Assert(err, IsNil) + checkDevice(c, resultMap, "foo,boo", "bar", "baz") + actualId, err := resultMap["system_id"].GetString() + c.Assert(actualId, Equals, systemId) +} + func (suite *TestServerSuite) TestDevicesList(c *C) { firstId := suite.createDevice(c, "foo", "bar", "baz") c.Assert(firstId, Not(Equals), "") @@ -252,6 +270,27 @@ func (suite *TestServerSuite) TestDevicesListMacFiltering(c *C) { checkDevice(c, deviceMap, "foo", "bar", "baz") } +func (suite *TestServerSuite) TestDevicesListMacFilteringMultipleAddresses(c *C) { + firstId := suite.createDevice(c, "foo,boo", "bar", "baz") + c.Assert(firstId, Not(Equals), "") + secondId := suite.createDevice(c, "bam,boom", "bing", "bong") + c.Assert(secondId, Not(Equals), "") + + op := "?op=list&mac_address=foo&mac_address=boo" + devicesURL := fmt.Sprintf("/api/%s/devices/", suite.server.version) + op + result := suite.get(c, devicesURL) + + devicesArray, err := result.GetArray() + c.Assert(err, IsNil) + c.Assert(devicesArray, HasLen, 2) + deviceMap, err := devicesArray[0].GetMap() + c.Assert(err, IsNil) + checkDevice(c, deviceMap, "foo,boo", "bar", "baz") + deviceMap, err = devicesArray[1].GetMap() + c.Assert(err, IsNil) + checkDevice(c, deviceMap, "foo,boo", "bar", "baz") +} + func (suite *TestServerSuite) TestDeviceClaimStickyIPRequiresAddress(c *C) { systemId := suite.createDevice(c, "foo", "bar", "baz") op := "?op=claim_sticky_ip_address"