gopkg.in/goose.v2@v2.0.1/testservices/novaservice/service_http_test.go (about)

     1  // Nova double testing service - HTTP API tests
     2  
     3  package novaservice
     4  
     5  import (
     6  	"bytes"
     7  	"encoding/json"
     8  	"fmt"
     9  	"io/ioutil"
    10  	"net/http"
    11  	"sort"
    12  	"strconv"
    13  	"strings"
    14  
    15  	gc "gopkg.in/check.v1"
    16  
    17  	"gopkg.in/goose.v2/nova"
    18  	"gopkg.in/goose.v2/testing/httpsuite"
    19  	"gopkg.in/goose.v2/testservices/hook"
    20  	"gopkg.in/goose.v2/testservices/identityservice"
    21  	"gopkg.in/goose.v2/testservices/neutronmodel"
    22  )
    23  
    24  type NovaHTTPSuite struct {
    25  	httpsuite.HTTPSuite
    26  	service              *Nova
    27  	token                string
    28  	useNeutronNetworking bool
    29  }
    30  
    31  var _ = gc.Suite(&NovaHTTPSuite{useNeutronNetworking: false})
    32  
    33  type NovaHTTPSSuite struct {
    34  	httpsuite.HTTPSuite
    35  	service              *Nova
    36  	token                string
    37  	useNeutronNetworking bool
    38  }
    39  
    40  var _ = gc.Suite(&NovaHTTPSSuite{HTTPSuite: httpsuite.HTTPSuite{UseTLS: true}, useNeutronNetworking: true})
    41  
    42  func (s *NovaHTTPSuite) SetUpSuite(c *gc.C) {
    43  	s.HTTPSuite.SetUpSuite(c)
    44  	identityDouble := identityservice.NewUserPass()
    45  	userInfo := identityDouble.AddUser("fred", "secret", "tenant", "default")
    46  	s.token = userInfo.Token
    47  	s.service = New(s.Server.URL, versionPath, userInfo.TenantId, region, identityDouble, nil)
    48  	if s.useNeutronNetworking {
    49  		c.Logf("Nova Service using Neutron Networking")
    50  		s.service.AddNeutronModel(neutronmodel.New())
    51  	} else {
    52  		c.Logf("Nova Service using Nova Networking")
    53  	}
    54  }
    55  
    56  func (s *NovaHTTPSuite) TearDownSuite(c *gc.C) {
    57  	s.HTTPSuite.TearDownSuite(c)
    58  }
    59  
    60  func (s *NovaHTTPSuite) SetUpTest(c *gc.C) {
    61  	s.HTTPSuite.SetUpTest(c)
    62  	s.service.SetupHTTP(s.Mux)
    63  	// this is otherwise handled not directly by nova test service
    64  	// but by openstack that tries for / before.
    65  	s.Mux.Handle("/", s.service.handler((*Nova).handleRoot))
    66  }
    67  
    68  func (s *NovaHTTPSuite) TearDownTest(c *gc.C) {
    69  	s.HTTPSuite.TearDownTest(c)
    70  }
    71  
    72  // assertJSON asserts the passed http.Response's body can be
    73  // unmarshalled into the given expected object, populating it with the
    74  // successfully parsed data.
    75  func assertJSON(c *gc.C, resp *http.Response, expected interface{}) {
    76  	body, err := ioutil.ReadAll(resp.Body)
    77  	defer resp.Body.Close()
    78  	c.Assert(err, gc.IsNil)
    79  	err = json.Unmarshal(body, &expected)
    80  	c.Assert(err, gc.IsNil)
    81  }
    82  
    83  // assertBody asserts the passed http.Response's body matches the
    84  // expected response, replacing any variables in the expected body.
    85  func assertBody(c *gc.C, resp *http.Response, expected *errorResponse) {
    86  	body, err := ioutil.ReadAll(resp.Body)
    87  	defer resp.Body.Close()
    88  	c.Assert(err, gc.IsNil)
    89  	expBody := expected.requestBody(resp.Request)
    90  	// cast to string for easier asserts debugging
    91  	c.Assert(string(body), gc.Equals, string(expBody))
    92  }
    93  
    94  // sendRequest constructs an HTTP request from the parameters and
    95  // sends it, returning the response or an error.
    96  func (s *NovaHTTPSuite) sendRequest(method, url string, body []byte, headers http.Header) (*http.Response, error) {
    97  	if !strings.HasPrefix(url, "http") {
    98  		url = "http://" + s.service.Hostname + strings.TrimLeft(url, "/")
    99  	}
   100  	req, err := http.NewRequest(method, url, bytes.NewReader(body))
   101  	if err != nil {
   102  		return nil, err
   103  	}
   104  	for header, values := range headers {
   105  		for _, value := range values {
   106  			req.Header.Add(header, value)
   107  		}
   108  	}
   109  	// workaround for https://code.google.com/p/go/issues/detail?id=4454
   110  	req.Header.Set("Content-Length", strconv.Itoa(len(body)))
   111  	return http.DefaultClient.Do(req)
   112  }
   113  
   114  // authRequest is a shortcut for sending requests with pre-set token
   115  // header and correct version prefix and tenant ID in the URL.
   116  func (s *NovaHTTPSuite) authRequest(method, path string, body []byte, headers http.Header) (*http.Response, error) {
   117  	if headers == nil {
   118  		headers = make(http.Header)
   119  	}
   120  	headers.Set(authToken, s.token)
   121  	url := s.service.endpointURL(true, path)
   122  	return s.sendRequest(method, url, body, headers)
   123  }
   124  
   125  // jsonRequest serializes the passed body object to JSON and sends a
   126  // the request with authRequest().
   127  func (s *NovaHTTPSuite) jsonRequest(method, path string, body interface{}, headers http.Header) (*http.Response, error) {
   128  	jsonBody, err := json.Marshal(body)
   129  	if err != nil {
   130  		return nil, err
   131  	}
   132  	return s.authRequest(method, path, jsonBody, headers)
   133  }
   134  
   135  // setHeader creates http.Header map, sets the given header, and
   136  // returns the map.
   137  func setHeader(header, value string) http.Header {
   138  	h := make(http.Header)
   139  	h.Set(header, value)
   140  	return h
   141  }
   142  
   143  // SimpleTest defines a simple request without a body and expected response.
   144  type SimpleTest struct {
   145  	unauth  bool
   146  	method  string
   147  	url     string
   148  	headers http.Header
   149  	expect  *errorResponse
   150  }
   151  
   152  func (s *NovaHTTPSuite) simpleTests() []SimpleTest {
   153  	var simpleTests = []SimpleTest{
   154  		{
   155  			unauth:  true,
   156  			method:  "GET",
   157  			url:     "/any",
   158  			headers: make(http.Header),
   159  			expect:  errUnauthorized,
   160  		},
   161  		{
   162  			unauth:  true,
   163  			method:  "POST",
   164  			url:     "/any",
   165  			headers: setHeader(authToken, "phony"),
   166  			expect:  errUnauthorized,
   167  		},
   168  		{
   169  			unauth:  true,
   170  			method:  "GET",
   171  			url:     "/any",
   172  			headers: setHeader(authToken, s.token),
   173  			expect:  errMultipleChoices,
   174  		},
   175  		{
   176  			unauth:  true,
   177  			method:  "POST",
   178  			url:     "/any/unknown/one",
   179  			headers: setHeader(authToken, s.token),
   180  			expect:  errMultipleChoices,
   181  		},
   182  		{
   183  			method: "POST",
   184  			url:    "/any/unknown/one",
   185  			expect: errNotFound,
   186  		},
   187  		{
   188  			unauth:  true,
   189  			method:  "GET",
   190  			url:     versionPath + "/phony_token",
   191  			headers: setHeader(authToken, s.token),
   192  			expect:  errBadRequest,
   193  		},
   194  		{
   195  			method: "GET",
   196  			url:    "/flavors/",
   197  			expect: errNotFound,
   198  		},
   199  		{
   200  			method: "GET",
   201  			url:    "/flavors/invalid",
   202  			expect: errNotFound,
   203  		},
   204  		{
   205  			method: "POST",
   206  			url:    "/flavors",
   207  			expect: errBadRequest2,
   208  		},
   209  		{
   210  			method: "POST",
   211  			url:    "/flavors/invalid",
   212  			expect: errNotFound,
   213  		},
   214  		{
   215  			method: "PUT",
   216  			url:    "/flavors",
   217  			expect: errNotFound,
   218  		},
   219  		{
   220  			method: "PUT",
   221  			url:    "/flavors/invalid",
   222  			expect: errNotFoundJSON,
   223  		},
   224  		{
   225  			method: "DELETE",
   226  			url:    "/flavors",
   227  			expect: errNotFound,
   228  		},
   229  		{
   230  			method: "DELETE",
   231  			url:    "/flavors/invalid",
   232  			expect: errForbidden,
   233  		},
   234  		{
   235  			method: "GET",
   236  			url:    "/flavors/detail/invalid",
   237  			expect: errNotFound,
   238  		},
   239  		{
   240  			method: "POST",
   241  			url:    "/flavors/detail",
   242  			expect: errNotFound,
   243  		},
   244  		{
   245  			method: "POST",
   246  			url:    "/flavors/detail/invalid",
   247  			expect: errNotFound,
   248  		},
   249  		{
   250  			method: "PUT",
   251  			url:    "/flavors/detail",
   252  			expect: errNotFoundJSON,
   253  		},
   254  		{
   255  			method: "PUT",
   256  			url:    "/flavors/detail/invalid",
   257  			expect: errNotFound,
   258  		},
   259  		{
   260  			method: "DELETE",
   261  			url:    "/flavors/detail",
   262  			expect: errForbidden,
   263  		},
   264  		{
   265  			method: "DELETE",
   266  			url:    "/flavors/detail/invalid",
   267  			expect: errNotFound,
   268  		},
   269  		{
   270  			method: "GET",
   271  			url:    "/servers/invalid",
   272  			expect: &errorResponse{code: 404, body: "{\"itemNotFound\":{\"message\":\"No such server \\\"invalid\\\"\", \"code\":404}}"},
   273  		},
   274  		{
   275  			method: "POST",
   276  			url:    "/servers",
   277  			expect: errBadRequest2,
   278  		},
   279  		{
   280  			method: "POST",
   281  			url:    "/servers/invalid",
   282  			expect: errNotFound,
   283  		},
   284  		{
   285  			method: "PUT",
   286  			url:    "/servers",
   287  			expect: errNotFound,
   288  		},
   289  		{
   290  			method: "PUT",
   291  			url:    "/servers/invalid",
   292  			expect: errBadRequest2,
   293  		},
   294  		{
   295  			method: "DELETE",
   296  			url:    "/servers",
   297  			expect: errNotFound,
   298  		},
   299  		{
   300  			method: "DELETE",
   301  			url:    "/servers/invalid",
   302  			expect: errNotFoundJSON,
   303  		},
   304  		{
   305  			method: "GET",
   306  			url:    "/servers/detail/invalid",
   307  			expect: errNotFound,
   308  		},
   309  		{
   310  			method: "POST",
   311  			url:    "/servers/detail",
   312  			expect: errNotFound,
   313  		},
   314  		{
   315  			method: "POST",
   316  			url:    "/servers/detail/invalid",
   317  			expect: errNotFound,
   318  		},
   319  		{
   320  			method: "PUT",
   321  			url:    "/servers/detail",
   322  			expect: errBadRequest2,
   323  		},
   324  		{
   325  			method: "PUT",
   326  			url:    "/servers/detail/invalid",
   327  			expect: errNotFound,
   328  		},
   329  		{
   330  			method: "DELETE",
   331  			url:    "/servers/detail",
   332  			expect: errNotFoundJSON,
   333  		},
   334  		{
   335  			method: "DELETE",
   336  			url:    "/servers/detail/invalid",
   337  			expect: errNotFound,
   338  		},
   339  	}
   340  	return simpleTests
   341  }
   342  
   343  func (s *NovaHTTPSuite) simpleNovaNetworkingTests() []SimpleTest {
   344  	var simpleTests = []SimpleTest{
   345  		{
   346  			method: "GET",
   347  			url:    "/os-security-groups/42",
   348  			expect: errNotFoundJSONSG,
   349  		},
   350  		{
   351  			method: "POST",
   352  			url:    "/os-security-groups",
   353  			expect: errBadRequest2,
   354  		},
   355  		{
   356  			method: "POST",
   357  			url:    "/os-security-groups/invalid",
   358  			expect: errNotFound,
   359  		},
   360  		{
   361  			method: "PUT",
   362  			url:    "/os-security-groups",
   363  			expect: errNotFound,
   364  		},
   365  		{
   366  			method: "PUT",
   367  			url:    "/os-security-groups/invalid",
   368  			expect: errNotFoundJSONSG,
   369  		},
   370  		{
   371  			method: "DELETE",
   372  			url:    "/os-security-groups",
   373  			expect: errNotFound,
   374  		},
   375  		{
   376  			method: "DELETE",
   377  			url:    "/os-security-groups/42",
   378  			expect: errNotFoundJSONSG,
   379  		},
   380  		{
   381  			method: "GET",
   382  			url:    "/os-security-group-rules",
   383  			expect: errNotFoundJSON,
   384  		},
   385  		{
   386  			method: "GET",
   387  			url:    "/os-security-group-rules/invalid",
   388  			expect: errNotFoundJSON,
   389  		},
   390  		{
   391  			method: "GET",
   392  			url:    "/os-security-group-rules/42",
   393  			expect: errNotFoundJSON,
   394  		},
   395  		{
   396  			method: "POST",
   397  			url:    "/os-security-group-rules",
   398  			expect: errBadRequest2,
   399  		},
   400  		{
   401  			method: "POST",
   402  			url:    "/os-security-group-rules/invalid",
   403  			expect: errNotFound,
   404  		},
   405  		{
   406  			method: "PUT",
   407  			url:    "/os-security-group-rules",
   408  			expect: errNotFound,
   409  		},
   410  		{
   411  			method: "PUT",
   412  			url:    "/os-security-group-rules/invalid",
   413  			expect: errNotFoundJSON,
   414  		},
   415  		{
   416  			method: "DELETE",
   417  			url:    "/os-security-group-rules",
   418  			expect: errNotFound,
   419  		},
   420  		{
   421  			method: "DELETE",
   422  			url:    "/os-security-group-rules/42",
   423  			expect: errNotFoundJSONSGR,
   424  		},
   425  		{
   426  			method: "GET",
   427  			url:    "/os-floating-ips/42",
   428  			expect: errNotFoundJSON,
   429  		},
   430  		{
   431  			method: "POST",
   432  			url:    "/os-floating-ips/invalid",
   433  			expect: errNotFound,
   434  		},
   435  		{
   436  			method: "PUT",
   437  			url:    "/os-floating-ips",
   438  			expect: errNotFound,
   439  		},
   440  		{
   441  			method: "PUT",
   442  			url:    "/os-floating-ips/invalid",
   443  			expect: errNotFoundJSON,
   444  		},
   445  		{
   446  			method: "DELETE",
   447  			url:    "/os-floating-ips",
   448  			expect: errNotFound,
   449  		},
   450  		{
   451  			method: "DELETE",
   452  			url:    "/os-floating-ips/invalid",
   453  			expect: errNotFoundJSON,
   454  		},
   455  	}
   456  	return simpleTests
   457  }
   458  
   459  func (s *NovaHTTPSuite) TestSimpleRequestTests(c *gc.C) {
   460  	s.runSimpleTests(c, s.simpleTests())
   461  	if !s.useNeutronNetworking {
   462  		s.runSimpleTests(c, s.simpleNovaNetworkingTests())
   463  	}
   464  }
   465  
   466  func (s *NovaHTTPSuite) runSimpleTests(c *gc.C, simpleTests []SimpleTest) {
   467  	for i, t := range simpleTests {
   468  		c.Logf("#%d. %s %s -> %d", i, t.method, t.url, t.expect.code)
   469  		if t.headers == nil {
   470  			t.headers = make(http.Header)
   471  			t.headers.Set(authToken, s.token)
   472  		}
   473  		var (
   474  			resp *http.Response
   475  			err  error
   476  		)
   477  		if t.unauth {
   478  			resp, err = s.sendRequest(t.method, t.url, nil, t.headers)
   479  		} else {
   480  			resp, err = s.authRequest(t.method, t.url, nil, t.headers)
   481  		}
   482  		c.Assert(err, gc.IsNil)
   483  		c.Assert(resp.StatusCode, gc.Equals, t.expect.code)
   484  		assertBody(c, resp, t.expect)
   485  	}
   486  	fmt.Printf("total: %d\n", len(simpleTests))
   487  }
   488  
   489  func (s *NovaHTTPSuite) TestGetFlavors(c *gc.C) {
   490  	// The test service has 3 default flavours.
   491  	var expected struct {
   492  		Flavors []nova.Entity
   493  	}
   494  	resp, err := s.authRequest("GET", "/flavors", nil, nil)
   495  	c.Assert(err, gc.IsNil)
   496  	c.Assert(resp.StatusCode, gc.Equals, http.StatusOK)
   497  	assertJSON(c, resp, &expected)
   498  	c.Assert(expected.Flavors, gc.HasLen, 3)
   499  	entities := s.service.allFlavorsAsEntities()
   500  	c.Assert(entities, gc.HasLen, 3)
   501  	sort.Sort(nova.EntitySortBy{Attr: "Id", Entities: expected.Flavors})
   502  	sort.Sort(nova.EntitySortBy{Attr: "Id", Entities: entities})
   503  	c.Assert(expected.Flavors, gc.DeepEquals, entities)
   504  	var expectedFlavor struct {
   505  		Flavor nova.FlavorDetail
   506  	}
   507  	resp, err = s.authRequest("GET", "/flavors/1", nil, nil)
   508  	c.Assert(err, gc.IsNil)
   509  	c.Assert(resp.StatusCode, gc.Equals, http.StatusOK)
   510  	assertJSON(c, resp, &expectedFlavor)
   511  	c.Assert(expectedFlavor.Flavor.Name, gc.Equals, "m1.tiny")
   512  }
   513  
   514  func (s *NovaHTTPSuite) TestGetFlavorsDetail(c *gc.C) {
   515  	// The test service has 3 default flavours.
   516  	flavors := s.service.allFlavors()
   517  	c.Assert(flavors, gc.HasLen, 3)
   518  	var expected struct {
   519  		Flavors []nova.FlavorDetail
   520  	}
   521  	resp, err := s.authRequest("GET", "/flavors/detail", nil, nil)
   522  	c.Assert(err, gc.IsNil)
   523  	c.Assert(resp.StatusCode, gc.Equals, http.StatusOK)
   524  	assertJSON(c, resp, &expected)
   525  	c.Assert(expected.Flavors, gc.HasLen, 3)
   526  	sort.Sort(nova.FlavorDetailSortBy{Attr: "Id", FlavorDetails: expected.Flavors})
   527  	sort.Sort(nova.FlavorDetailSortBy{Attr: "Id", FlavorDetails: flavors})
   528  	c.Assert(expected.Flavors, gc.DeepEquals, flavors)
   529  	resp, err = s.authRequest("GET", "/flavors/detail/1", nil, nil)
   530  	c.Assert(err, gc.IsNil)
   531  	assertBody(c, resp, errNotFound)
   532  }
   533  
   534  func (s *NovaHTTPSuite) TestGetServers(c *gc.C) {
   535  	entities, err := s.service.allServersAsEntities(nil)
   536  	c.Assert(err, gc.IsNil)
   537  	c.Assert(entities, gc.HasLen, 0)
   538  	var expected struct {
   539  		Servers []nova.Entity
   540  	}
   541  	resp, err := s.authRequest("GET", "/servers", nil, nil)
   542  	c.Assert(err, gc.IsNil)
   543  	c.Assert(resp.StatusCode, gc.Equals, http.StatusOK)
   544  	assertJSON(c, resp, &expected)
   545  	c.Assert(expected.Servers, gc.HasLen, 0)
   546  	servers := []nova.ServerDetail{
   547  		{Id: "sr1", Name: "server 1"},
   548  		{Id: "sr2", Name: "server 2"},
   549  	}
   550  	for i, server := range servers {
   551  		s.service.buildServerLinks(&server)
   552  		servers[i] = server
   553  		err := s.service.addServer(server)
   554  		c.Assert(err, gc.IsNil)
   555  		defer s.service.removeServer(server.Id)
   556  	}
   557  	entities, err = s.service.allServersAsEntities(nil)
   558  	c.Assert(err, gc.IsNil)
   559  	resp, err = s.authRequest("GET", "/servers", nil, nil)
   560  	c.Assert(err, gc.IsNil)
   561  	c.Assert(resp.StatusCode, gc.Equals, http.StatusOK)
   562  	assertJSON(c, resp, &expected)
   563  	c.Assert(expected.Servers, gc.HasLen, 2)
   564  	if expected.Servers[0].Id != entities[0].Id {
   565  		expected.Servers[0], expected.Servers[1] = expected.Servers[1], expected.Servers[0]
   566  	}
   567  	c.Assert(expected.Servers, gc.DeepEquals, entities)
   568  	var expectedServer struct {
   569  		Server nova.ServerDetail
   570  	}
   571  	resp, err = s.authRequest("GET", "/servers/sr1", nil, nil)
   572  	c.Assert(err, gc.IsNil)
   573  	c.Assert(resp.StatusCode, gc.Equals, http.StatusOK)
   574  	assertJSON(c, resp, &expectedServer)
   575  	servers[0].Status = nova.StatusActive
   576  	c.Assert(expectedServer.Server, gc.DeepEquals, servers[0])
   577  }
   578  
   579  func (s *NovaHTTPSuite) TestGetServersWithFilters(c *gc.C) {
   580  	entities, err := s.service.allServersAsEntities(nil)
   581  	c.Assert(err, gc.IsNil)
   582  	c.Assert(entities, gc.HasLen, 0)
   583  	var expected struct {
   584  		Servers []nova.Entity
   585  	}
   586  	url := "/servers?status=RESCUE&status=BUILD&name=srv2&name=srv1"
   587  	resp, err := s.authRequest("GET", url, nil, nil)
   588  	c.Assert(err, gc.IsNil)
   589  	c.Assert(resp.StatusCode, gc.Equals, http.StatusOK)
   590  	assertJSON(c, resp, &expected)
   591  	c.Assert(expected.Servers, gc.HasLen, 0)
   592  	servers := []nova.ServerDetail{
   593  		{Id: "sr1", Name: "srv1", Status: nova.StatusBuild},
   594  		{Id: "sr2", Name: "srv2", Status: nova.StatusRescue},
   595  		{Id: "sr3", Name: "srv3", Status: nova.StatusActive},
   596  	}
   597  	for i, server := range servers {
   598  		s.service.buildServerLinks(&server)
   599  		servers[i] = server
   600  		err := s.service.addServer(server)
   601  		c.Assert(err, gc.IsNil)
   602  		defer s.service.removeServer(server.Id)
   603  	}
   604  	resp, err = s.authRequest("GET", url, nil, nil)
   605  	c.Assert(err, gc.IsNil)
   606  	c.Assert(resp.StatusCode, gc.Equals, http.StatusOK)
   607  	assertJSON(c, resp, &expected)
   608  	c.Assert(expected.Servers, gc.HasLen, 1)
   609  	c.Assert(expected.Servers[0].Id, gc.Equals, servers[0].Id)
   610  	c.Assert(expected.Servers[0].Name, gc.Equals, servers[0].Name)
   611  }
   612  
   613  func (s *NovaHTTPSuite) TestGetServersWithBadFilter(c *gc.C) {
   614  	url := "/servers?name=(server"
   615  	resp, err := s.authRequest("GET", url, nil, nil)
   616  	c.Assert(err, gc.IsNil)
   617  	c.Assert(resp.StatusCode, gc.Equals, http.StatusInternalServerError)
   618  	type novaError struct {
   619  		Code    int
   620  		Message string
   621  	}
   622  	var expected struct {
   623  		novaError `json:"computeFault"`
   624  	}
   625  	assertJSON(c, resp, &expected)
   626  	c.Check(expected.Code, gc.Equals, 500)
   627  	c.Check(expected.Message, gc.Matches, `error parsing.*\(server.*`)
   628  }
   629  
   630  func (s *NovaHTTPSuite) TestGetServersPatchMatch(c *gc.C) {
   631  	cleanup := s.service.RegisterControlPoint(
   632  		"matchServers",
   633  		func(sc hook.ServiceControl, args ...interface{}) error {
   634  			return fmt.Errorf("Unexpected error")
   635  		},
   636  	)
   637  	defer cleanup()
   638  	resp, err := s.authRequest("GET", "/servers", nil, nil)
   639  	c.Assert(err, gc.IsNil)
   640  	c.Assert(resp.StatusCode, gc.Equals, http.StatusInternalServerError)
   641  	type novaError struct {
   642  		Code    int
   643  		Message string
   644  	}
   645  	var expected struct {
   646  		novaError `json:"computeFault"`
   647  	}
   648  	assertJSON(c, resp, &expected)
   649  	c.Check(expected.Code, gc.Equals, 500)
   650  	c.Check(expected.Message, gc.Equals, "Unexpected error")
   651  }
   652  
   653  func (s *NovaHTTPSuite) TestNewUUID(c *gc.C) {
   654  	uuid, err := newUUID()
   655  	c.Assert(err, gc.IsNil)
   656  	var p1, p2, p3, p4, p5 string
   657  	num, err := fmt.Sscanf(uuid, "%8x-%4x-%4x-%4x-%12x", &p1, &p2, &p3, &p4, &p5)
   658  	c.Assert(err, gc.IsNil)
   659  	c.Assert(num, gc.Equals, 5)
   660  	uuid2, err := newUUID()
   661  	c.Assert(err, gc.IsNil)
   662  	c.Assert(uuid2, gc.Not(gc.Equals), uuid)
   663  }
   664  
   665  func (s *NovaHTTPSuite) assertAddresses(c *gc.C, serverId string) {
   666  	server, err := s.service.server(serverId)
   667  	c.Assert(err, gc.IsNil)
   668  	c.Assert(server.Addresses, gc.HasLen, 2)
   669  	c.Assert(server.Addresses["public"], gc.HasLen, 2)
   670  	c.Assert(server.Addresses["private"], gc.HasLen, 2)
   671  	for network, addresses := range server.Addresses {
   672  		for _, addr := range addresses {
   673  			if addr.Version == 4 && network == "public" {
   674  				c.Assert(addr.Address, gc.Matches, `127\.10\.0\.\d{1,3}`)
   675  			} else if addr.Version == 4 && network == "private" {
   676  				c.Assert(addr.Address, gc.Matches, `127\.0\.0\.\d{1,3}`)
   677  			}
   678  		}
   679  
   680  	}
   681  }
   682  
   683  func (s *NovaHTTPSuite) TestRunServer(c *gc.C) {
   684  	entities, err := s.service.allServersAsEntities(nil)
   685  	c.Assert(err, gc.IsNil)
   686  	c.Assert(entities, gc.HasLen, 0)
   687  	var req struct {
   688  		Server struct {
   689  			FlavorRef      string              `json:"flavorRef"`
   690  			ImageRef       string              `json:"imageRef"`
   691  			Name           string              `json:"name"`
   692  			SecurityGroups []map[string]string `json:"security_groups"`
   693  		} `json:"server"`
   694  	}
   695  	resp, err := s.jsonRequest("POST", "/servers", req, nil)
   696  	c.Assert(err, gc.IsNil)
   697  	c.Assert(resp.StatusCode, gc.Equals, http.StatusBadRequest)
   698  	assertBody(c, resp, errBadRequestSrvName)
   699  	req.Server.Name = "srv1"
   700  	resp, err = s.jsonRequest("POST", "/servers", req, nil)
   701  	c.Assert(err, gc.IsNil)
   702  	c.Assert(resp.StatusCode, gc.Equals, http.StatusBadRequest)
   703  	assertBody(c, resp, errBadRequestSrvImage)
   704  	req.Server.ImageRef = "image"
   705  	resp, err = s.jsonRequest("POST", "/servers", req, nil)
   706  	c.Assert(err, gc.IsNil)
   707  	c.Assert(resp.StatusCode, gc.Equals, http.StatusBadRequest)
   708  	assertBody(c, resp, errBadRequestSrvFlavor)
   709  	req.Server.FlavorRef = "flavor"
   710  	var expected struct {
   711  		Server struct {
   712  			SecurityGroups []map[string]string `json:"security_groups"`
   713  			Id             string
   714  			Links          []nova.Link
   715  			AdminPass      string
   716  		}
   717  	}
   718  	resp, err = s.jsonRequest("POST", "/servers", req, nil)
   719  	c.Assert(err, gc.IsNil)
   720  	c.Assert(resp.StatusCode, gc.Equals, http.StatusAccepted)
   721  	assertJSON(c, resp, &expected)
   722  	c.Assert(expected.Server.SecurityGroups, gc.HasLen, 1)
   723  	c.Assert(expected.Server.SecurityGroups[0]["name"], gc.Equals, "default")
   724  	c.Assert(expected.Server.Id, gc.Not(gc.Equals), "")
   725  	c.Assert(expected.Server.Links, gc.HasLen, 2)
   726  	c.Assert(expected.Server.AdminPass, gc.Not(gc.Equals), "")
   727  	s.assertAddresses(c, expected.Server.Id)
   728  	srv, err := s.service.server(expected.Server.Id)
   729  	c.Assert(err, gc.IsNil)
   730  	c.Assert(srv.Links, gc.DeepEquals, expected.Server.Links)
   731  	s.service.removeServer(srv.Id)
   732  	req.Server.Name = "test2"
   733  	req.Server.SecurityGroups = []map[string]string{
   734  		{"name": "default"},
   735  		{"name": "group1"},
   736  		{"name": "group2"},
   737  	}
   738  	err = s.service.addSecurityGroup(nova.SecurityGroup{Id: "1", Name: "group1"})
   739  	c.Assert(err, gc.IsNil)
   740  	defer s.service.removeSecurityGroup("1")
   741  	err = s.service.addSecurityGroup(nova.SecurityGroup{Id: "2", Name: "group2"})
   742  	c.Assert(err, gc.IsNil)
   743  	defer s.service.removeSecurityGroup("2")
   744  	resp, err = s.jsonRequest("POST", "/servers", req, nil)
   745  	c.Assert(err, gc.IsNil)
   746  	c.Assert(resp.StatusCode, gc.Equals, http.StatusAccepted)
   747  	assertJSON(c, resp, &expected)
   748  	c.Assert(expected.Server.SecurityGroups, gc.DeepEquals, req.Server.SecurityGroups)
   749  	srv, err = s.service.server(expected.Server.Id)
   750  	c.Assert(err, gc.IsNil)
   751  	ok := s.service.hasServerSecurityGroup(srv.Id, "1")
   752  	c.Assert(ok, gc.Equals, true)
   753  	ok = s.service.hasServerSecurityGroup(srv.Id, "2")
   754  	c.Assert(ok, gc.Equals, true)
   755  	ok = s.service.hasServerSecurityGroup(srv.Id, "999")
   756  	c.Assert(ok, gc.Equals, true)
   757  	s.service.removeServerSecurityGroup(srv.Id, "1")
   758  	s.service.removeServerSecurityGroup(srv.Id, "2")
   759  	s.service.removeServerSecurityGroup(srv.Id, "999")
   760  	s.service.removeServer(srv.Id)
   761  }
   762  
   763  func (s *NovaHTTPSuite) TestDeleteServer(c *gc.C) {
   764  	server := nova.ServerDetail{Id: "sr1"}
   765  	_, err := s.service.server(server.Id)
   766  	c.Assert(err, gc.NotNil)
   767  	err = s.service.addServer(server)
   768  	c.Assert(err, gc.IsNil)
   769  	defer s.service.removeServer(server.Id)
   770  	resp, err := s.authRequest("DELETE", "/servers/sr1", nil, nil)
   771  	c.Assert(err, gc.IsNil)
   772  	c.Assert(resp.StatusCode, gc.Equals, http.StatusNoContent)
   773  	_, err = s.service.server(server.Id)
   774  	c.Assert(err, gc.NotNil)
   775  }
   776  
   777  func (s *NovaHTTPSuite) TestGetServersDetail(c *gc.C) {
   778  	servers, err := s.service.allServers(nil)
   779  	c.Assert(err, gc.IsNil)
   780  	c.Assert(servers, gc.HasLen, 0)
   781  	var expected struct {
   782  		Servers []nova.ServerDetail `json:"servers"`
   783  	}
   784  	resp, err := s.authRequest("GET", "/servers/detail", nil, nil)
   785  	c.Assert(err, gc.IsNil)
   786  	c.Assert(resp.StatusCode, gc.Equals, http.StatusOK)
   787  	assertJSON(c, resp, &expected)
   788  	c.Assert(expected.Servers, gc.HasLen, 0)
   789  	servers = []nova.ServerDetail{
   790  		{Id: "sr1", Name: "server 1"},
   791  		{Id: "sr2", Name: "server 2"},
   792  	}
   793  	for i, server := range servers {
   794  		s.service.buildServerLinks(&server)
   795  		servers[i] = server
   796  		err := s.service.addServer(server)
   797  		c.Assert(err, gc.IsNil)
   798  		defer s.service.removeServer(server.Id)
   799  	}
   800  	resp, err = s.authRequest("GET", "/servers/detail", nil, nil)
   801  	c.Assert(err, gc.IsNil)
   802  	c.Assert(resp.StatusCode, gc.Equals, http.StatusOK)
   803  	assertJSON(c, resp, &expected)
   804  	c.Assert(expected.Servers, gc.HasLen, 2)
   805  	if expected.Servers[0].Id != servers[0].Id {
   806  		expected.Servers[0], expected.Servers[1] = expected.Servers[1], expected.Servers[0]
   807  	}
   808  	c.Assert(expected.Servers, gc.DeepEquals, servers)
   809  	resp, err = s.authRequest("GET", "/servers/detail/sr1", nil, nil)
   810  	c.Assert(err, gc.IsNil)
   811  	assertBody(c, resp, errNotFound)
   812  }
   813  
   814  func (s *NovaHTTPSuite) TestGetServersDetailWithFilters(c *gc.C) {
   815  	servers, err := s.service.allServers(nil)
   816  	c.Assert(err, gc.IsNil)
   817  	c.Assert(servers, gc.HasLen, 0)
   818  	var expected struct {
   819  		Servers []nova.ServerDetail `json:"servers"`
   820  	}
   821  	url := "/servers/detail?status=RESCUE&status=BUILD&name=srv2&name=srv1"
   822  	resp, err := s.authRequest("GET", url, nil, nil)
   823  	c.Assert(err, gc.IsNil)
   824  	c.Assert(resp.StatusCode, gc.Equals, http.StatusOK)
   825  	assertJSON(c, resp, &expected)
   826  	c.Assert(expected.Servers, gc.HasLen, 0)
   827  	servers = []nova.ServerDetail{
   828  		{Id: "sr1", Name: "srv1", Status: nova.StatusBuild},
   829  		{Id: "sr2", Name: "srv2", Status: nova.StatusRescue},
   830  		{Id: "sr3", Name: "srv3", Status: nova.StatusActive},
   831  	}
   832  	for i, server := range servers {
   833  		s.service.buildServerLinks(&server)
   834  		servers[i] = server
   835  		err := s.service.addServer(server)
   836  		c.Assert(err, gc.IsNil)
   837  		defer s.service.removeServer(server.Id)
   838  	}
   839  	resp, err = s.authRequest("GET", url, nil, nil)
   840  	c.Assert(err, gc.IsNil)
   841  	c.Assert(resp.StatusCode, gc.Equals, http.StatusOK)
   842  	assertJSON(c, resp, &expected)
   843  	c.Assert(expected.Servers, gc.HasLen, 1)
   844  	c.Assert(expected.Servers[0], gc.DeepEquals, servers[0])
   845  }
   846  
   847  func (s *NovaHTTPSuite) TestGetSecurityGroups(c *gc.C) {
   848  	if s.service.useNeutronNetworking {
   849  		c.Skip("skipped in novaservice when using Neutron Model")
   850  	}
   851  	// There is always a default security group.
   852  	groups := s.service.allSecurityGroups()
   853  	c.Assert(groups, gc.HasLen, 1)
   854  	var expected struct {
   855  		Groups []nova.SecurityGroup `json:"security_groups"`
   856  	}
   857  	resp, err := s.authRequest("GET", "/os-security-groups", nil, nil)
   858  	c.Assert(err, gc.IsNil)
   859  	c.Assert(resp.StatusCode, gc.Equals, http.StatusOK)
   860  	assertJSON(c, resp, &expected)
   861  	c.Assert(expected.Groups, gc.HasLen, 1)
   862  	groups = []nova.SecurityGroup{
   863  		{
   864  			Id:       "1",
   865  			Name:     "group 1",
   866  			TenantId: s.service.TenantId,
   867  			Rules:    []nova.SecurityGroupRule{},
   868  		},
   869  		{
   870  			Id:       "2",
   871  			Name:     "group 2",
   872  			TenantId: s.service.TenantId,
   873  			Rules:    []nova.SecurityGroupRule{},
   874  		},
   875  	}
   876  	for _, group := range groups {
   877  		err := s.service.addSecurityGroup(group)
   878  		c.Assert(err, gc.IsNil)
   879  		defer s.service.removeSecurityGroup(group.Id)
   880  	}
   881  	resp, err = s.authRequest("GET", "/os-security-groups", nil, nil)
   882  	c.Assert(err, gc.IsNil)
   883  	c.Assert(resp.StatusCode, gc.Equals, http.StatusOK)
   884  	assertJSON(c, resp, &expected)
   885  	c.Assert(expected.Groups, gc.HasLen, len(groups)+1)
   886  	checkGroupsInList(c, groups, expected.Groups)
   887  	var expectedGroup struct {
   888  		Group nova.SecurityGroup `json:"security_group"`
   889  	}
   890  	resp, err = s.authRequest("GET", "/os-security-groups/1", nil, nil)
   891  	c.Assert(err, gc.IsNil)
   892  	c.Assert(resp.StatusCode, gc.Equals, http.StatusOK)
   893  	assertJSON(c, resp, &expectedGroup)
   894  	c.Assert(expectedGroup.Group, gc.DeepEquals, groups[0])
   895  }
   896  
   897  func (s *NovaHTTPSuite) TestAddSecurityGroup(c *gc.C) {
   898  	if s.service.useNeutronNetworking {
   899  		c.Skip("skipped in novaservice when using Neutron Model")
   900  	}
   901  	group := nova.SecurityGroup{
   902  		Id:          "1",
   903  		Name:        "group 1",
   904  		Description: "desc",
   905  		TenantId:    s.service.TenantId,
   906  		Rules:       []nova.SecurityGroupRule{},
   907  	}
   908  	_, err := s.service.securityGroup(group.Id)
   909  	c.Assert(err, gc.NotNil)
   910  	var req struct {
   911  		Group struct {
   912  			Name        string `json:"name"`
   913  			Description string `json:"description"`
   914  		} `json:"security_group"`
   915  	}
   916  	req.Group.Name = group.Name
   917  	req.Group.Description = group.Description
   918  	var expected struct {
   919  		Group nova.SecurityGroup `json:"security_group"`
   920  	}
   921  	resp, err := s.jsonRequest("POST", "/os-security-groups", req, nil)
   922  	c.Assert(err, gc.IsNil)
   923  	c.Assert(resp.StatusCode, gc.Equals, http.StatusOK)
   924  	assertJSON(c, resp, &expected)
   925  	c.Assert(expected.Group, gc.DeepEquals, group)
   926  	err = s.service.removeSecurityGroup(group.Id)
   927  	c.Assert(err, gc.IsNil)
   928  }
   929  
   930  func (s *NovaHTTPSuite) TestDeleteSecurityGroup(c *gc.C) {
   931  	if s.service.useNeutronNetworking {
   932  		c.Skip("skipped in novaservice when using Neutron Model")
   933  	}
   934  	group := nova.SecurityGroup{Id: "1", Name: "group 1"}
   935  	_, err := s.service.securityGroup(group.Id)
   936  	c.Assert(err, gc.NotNil)
   937  	err = s.service.addSecurityGroup(group)
   938  	c.Assert(err, gc.IsNil)
   939  	defer s.service.removeSecurityGroup(group.Id)
   940  	resp, err := s.authRequest("DELETE", "/os-security-groups/1", nil, nil)
   941  	c.Assert(err, gc.IsNil)
   942  	c.Assert(resp.StatusCode, gc.Equals, http.StatusAccepted)
   943  	_, err = s.service.securityGroup(group.Id)
   944  	c.Assert(err, gc.NotNil)
   945  }
   946  
   947  func (s *NovaHTTPSuite) TestAddSecurityGroupRule(c *gc.C) {
   948  	if s.service.useNeutronNetworking {
   949  		c.Skip("skipped in novaservice when using Neutron Model")
   950  	}
   951  	group1 := nova.SecurityGroup{Id: "1", Name: "src"}
   952  	group2 := nova.SecurityGroup{Id: "2", Name: "tgt"}
   953  	err := s.service.addSecurityGroup(group1)
   954  	c.Assert(err, gc.IsNil)
   955  	defer s.service.removeSecurityGroup(group1.Id)
   956  	err = s.service.addSecurityGroup(group2)
   957  	c.Assert(err, gc.IsNil)
   958  	defer s.service.removeSecurityGroup(group2.Id)
   959  	riIngress := nova.RuleInfo{
   960  		ParentGroupId: "1",
   961  		FromPort:      1234,
   962  		ToPort:        4321,
   963  		IPProtocol:    "tcp",
   964  		Cidr:          "1.2.3.4/5",
   965  	}
   966  	riGroup := nova.RuleInfo{
   967  		ParentGroupId: group2.Id,
   968  		GroupId:       &group1.Id,
   969  	}
   970  	iprange := make(map[string]string)
   971  	iprange["cidr"] = riIngress.Cidr
   972  	rule1 := nova.SecurityGroupRule{
   973  		Id:            "1",
   974  		ParentGroupId: group1.Id,
   975  		FromPort:      &riIngress.FromPort,
   976  		ToPort:        &riIngress.ToPort,
   977  		IPProtocol:    &riIngress.IPProtocol,
   978  		IPRange:       iprange,
   979  	}
   980  	rule2 := nova.SecurityGroupRule{
   981  		Id:            "2",
   982  		ParentGroupId: group2.Id,
   983  		Group: nova.SecurityGroupRef{
   984  			Name:     group1.Name,
   985  			TenantId: s.service.TenantId,
   986  		},
   987  	}
   988  	ok := s.service.hasSecurityGroupRule(group1.Id, rule1.Id)
   989  	c.Assert(ok, gc.Equals, false)
   990  	ok = s.service.hasSecurityGroupRule(group2.Id, rule2.Id)
   991  	c.Assert(ok, gc.Equals, false)
   992  	var req struct {
   993  		Rule nova.RuleInfo `json:"security_group_rule"`
   994  	}
   995  	req.Rule = riIngress
   996  	var expected struct {
   997  		Rule nova.SecurityGroupRule `json:"security_group_rule"`
   998  	}
   999  	resp, err := s.jsonRequest("POST", "/os-security-group-rules", req, nil)
  1000  	c.Assert(err, gc.IsNil)
  1001  	c.Assert(resp.StatusCode, gc.Equals, http.StatusOK)
  1002  	assertJSON(c, resp, &expected)
  1003  	c.Assert(expected.Rule.Id, gc.Equals, rule1.Id)
  1004  	c.Assert(expected.Rule.ParentGroupId, gc.Equals, rule1.ParentGroupId)
  1005  	c.Assert(expected.Rule.Group, gc.Equals, nova.SecurityGroupRef{})
  1006  	c.Assert(*expected.Rule.FromPort, gc.Equals, *rule1.FromPort)
  1007  	c.Assert(*expected.Rule.ToPort, gc.Equals, *rule1.ToPort)
  1008  	c.Assert(*expected.Rule.IPProtocol, gc.Equals, *rule1.IPProtocol)
  1009  	c.Assert(expected.Rule.IPRange, gc.DeepEquals, rule1.IPRange)
  1010  	defer s.service.removeSecurityGroupRule(rule1.Id)
  1011  	req.Rule = riGroup
  1012  	resp, err = s.jsonRequest("POST", "/os-security-group-rules", req, nil)
  1013  	c.Assert(err, gc.IsNil)
  1014  	c.Assert(resp.StatusCode, gc.Equals, http.StatusOK)
  1015  	assertJSON(c, resp, &expected)
  1016  	c.Assert(expected.Rule.Id, gc.Equals, rule2.Id)
  1017  	c.Assert(expected.Rule.ParentGroupId, gc.Equals, rule2.ParentGroupId)
  1018  	c.Assert(expected.Rule.Group, gc.DeepEquals, rule2.Group)
  1019  	err = s.service.removeSecurityGroupRule(rule2.Id)
  1020  	c.Assert(err, gc.IsNil)
  1021  }
  1022  
  1023  func (s *NovaHTTPSuite) TestDeleteSecurityGroupRule(c *gc.C) {
  1024  	if s.service.useNeutronNetworking {
  1025  		c.Skip("skipped in novaservice when using Neutron Model")
  1026  	}
  1027  	group1 := nova.SecurityGroup{Id: "1", Name: "src"}
  1028  	group2 := nova.SecurityGroup{Id: "2", Name: "tgt"}
  1029  	err := s.service.addSecurityGroup(group1)
  1030  	c.Assert(err, gc.IsNil)
  1031  	defer s.service.removeSecurityGroup(group1.Id)
  1032  	err = s.service.addSecurityGroup(group2)
  1033  	c.Assert(err, gc.IsNil)
  1034  	defer s.service.removeSecurityGroup(group2.Id)
  1035  	riGroup := nova.RuleInfo{
  1036  		ParentGroupId: group2.Id,
  1037  		GroupId:       &group1.Id,
  1038  	}
  1039  	rule := nova.SecurityGroupRule{
  1040  		Id:            "1",
  1041  		ParentGroupId: group2.Id,
  1042  		Group: nova.SecurityGroupRef{
  1043  			Name:     group1.Name,
  1044  			TenantId: group1.TenantId,
  1045  		},
  1046  	}
  1047  	err = s.service.addSecurityGroupRule(rule.Id, riGroup)
  1048  	c.Assert(err, gc.IsNil)
  1049  	resp, err := s.authRequest("DELETE", "/os-security-group-rules/1", nil, nil)
  1050  	c.Assert(err, gc.IsNil)
  1051  	c.Assert(resp.StatusCode, gc.Equals, http.StatusAccepted)
  1052  	ok := s.service.hasSecurityGroupRule(group2.Id, rule.Id)
  1053  	c.Assert(ok, gc.Equals, false)
  1054  }
  1055  
  1056  func (s *NovaHTTPSuite) TestAddServerSecurityGroup(c *gc.C) {
  1057  	group := nova.SecurityGroup{Id: "1", Name: "group"}
  1058  	err := s.service.addSecurityGroup(group)
  1059  	c.Assert(err, gc.IsNil)
  1060  	defer s.service.removeSecurityGroup(group.Id)
  1061  	server := nova.ServerDetail{Id: "sr1"}
  1062  	err = s.service.addServer(server)
  1063  	c.Assert(err, gc.IsNil)
  1064  	defer s.service.removeServer(server.Id)
  1065  	ok := s.service.hasServerSecurityGroup(server.Id, group.Id)
  1066  	c.Assert(ok, gc.Equals, false)
  1067  	var req struct {
  1068  		Group struct {
  1069  			Name string `json:"name"`
  1070  		} `json:"addSecurityGroup"`
  1071  	}
  1072  	req.Group.Name = group.Name
  1073  	resp, err := s.jsonRequest("POST", "/servers/"+server.Id+"/action", req, nil)
  1074  	c.Assert(err, gc.IsNil)
  1075  	c.Assert(resp.StatusCode, gc.Equals, http.StatusAccepted)
  1076  	ok = s.service.hasServerSecurityGroup(server.Id, group.Id)
  1077  	c.Assert(ok, gc.Equals, true)
  1078  	err = s.service.removeServerSecurityGroup(server.Id, group.Id)
  1079  	c.Assert(err, gc.IsNil)
  1080  }
  1081  
  1082  func (s *NovaHTTPSuite) TestGetServerSecurityGroups(c *gc.C) {
  1083  	server := nova.ServerDetail{Id: "sr1"}
  1084  	groups := []nova.SecurityGroup{
  1085  		{
  1086  			Id:       "1",
  1087  			Name:     "group1",
  1088  			TenantId: s.service.TenantId,
  1089  			Rules:    []nova.SecurityGroupRule{},
  1090  		},
  1091  		{
  1092  			Id:       "2",
  1093  			Name:     "group2",
  1094  			TenantId: s.service.TenantId,
  1095  			Rules:    []nova.SecurityGroupRule{},
  1096  		},
  1097  	}
  1098  	srvGroups := s.service.allServerSecurityGroups(server.Id)
  1099  	c.Assert(srvGroups, gc.HasLen, 0)
  1100  	err := s.service.addServer(server)
  1101  	c.Assert(err, gc.IsNil)
  1102  	defer s.service.removeServer(server.Id)
  1103  	for _, group := range groups {
  1104  		err = s.service.addSecurityGroup(group)
  1105  		c.Assert(err, gc.IsNil)
  1106  		defer s.service.removeSecurityGroup(group.Id)
  1107  		err = s.service.addServerSecurityGroup(server.Id, group.Id)
  1108  		c.Assert(err, gc.IsNil)
  1109  		defer s.service.removeServerSecurityGroup(server.Id, group.Id)
  1110  	}
  1111  	srvGroups = s.service.allServerSecurityGroups(server.Id)
  1112  	var expected struct {
  1113  		Groups []nova.SecurityGroup `json:"security_groups"`
  1114  	}
  1115  	resp, err := s.authRequest("GET", "/servers/"+server.Id+"/os-security-groups", nil, nil)
  1116  	c.Assert(err, gc.IsNil)
  1117  	assertJSON(c, resp, &expected)
  1118  	// nova networking doesn't know about neutron egress direction rules,
  1119  	// created by default with a new security group
  1120  	if s.service.useNeutronNetworking {
  1121  		expected.Groups[0].Rules = []nova.SecurityGroupRule{}
  1122  		expected.Groups[1].Rules = []nova.SecurityGroupRule{}
  1123  	}
  1124  	c.Assert(expected.Groups, gc.DeepEquals, groups)
  1125  }
  1126  
  1127  func (s *NovaHTTPSuite) TestDeleteServerSecurityGroup(c *gc.C) {
  1128  	if s.service.useNeutronNetworking {
  1129  		c.Skip("skipped in novaservice when using Neutron Model")
  1130  	}
  1131  	group := nova.SecurityGroup{Id: "1", Name: "group"}
  1132  	err := s.service.addSecurityGroup(group)
  1133  	c.Assert(err, gc.IsNil)
  1134  	defer s.service.removeSecurityGroup(group.Id)
  1135  	server := nova.ServerDetail{Id: "sr1"}
  1136  	err = s.service.addServer(server)
  1137  	c.Assert(err, gc.IsNil)
  1138  	defer s.service.removeServer(server.Id)
  1139  	ok := s.service.hasServerSecurityGroup(server.Id, group.Id)
  1140  	c.Assert(ok, gc.Equals, false)
  1141  	err = s.service.addServerSecurityGroup(server.Id, group.Id)
  1142  	c.Assert(err, gc.IsNil)
  1143  	var req struct {
  1144  		Group struct {
  1145  			Name string `json:"name"`
  1146  		} `json:"removeSecurityGroup"`
  1147  	}
  1148  	req.Group.Name = group.Name
  1149  	resp, err := s.jsonRequest("POST", "/servers/"+server.Id+"/action", req, nil)
  1150  	c.Assert(err, gc.IsNil)
  1151  	c.Assert(resp.StatusCode, gc.Equals, http.StatusAccepted)
  1152  	ok = s.service.hasServerSecurityGroup(server.Id, group.Id)
  1153  	c.Assert(ok, gc.Equals, false)
  1154  }
  1155  
  1156  func (s *NovaHTTPSuite) TestPostFloatingIP(c *gc.C) {
  1157  	if s.service.useNeutronNetworking {
  1158  		c.Skip("skipped in novaservice when using Neutron Model")
  1159  	}
  1160  	fip := nova.FloatingIP{Id: "1", IP: "10.0.0.1", Pool: "nova"}
  1161  	c.Assert(s.service.allFloatingIPs(), gc.HasLen, 0)
  1162  	var expected struct {
  1163  		IP nova.FloatingIP `json:"floating_ip"`
  1164  	}
  1165  	resp, err := s.authRequest("POST", "/os-floating-ips", nil, nil)
  1166  	c.Assert(err, gc.IsNil)
  1167  	c.Assert(resp.StatusCode, gc.Equals, http.StatusOK)
  1168  	assertJSON(c, resp, &expected)
  1169  	c.Assert(expected.IP, gc.DeepEquals, fip)
  1170  	err = s.service.removeFloatingIP(fip.Id)
  1171  	c.Assert(err, gc.IsNil)
  1172  }
  1173  
  1174  func (s *NovaHTTPSuite) TestGetFloatingIPs(c *gc.C) {
  1175  	if s.service.useNeutronNetworking {
  1176  		c.Skip("skipped in novaservice when using Neutron Model")
  1177  	}
  1178  	c.Assert(s.service.allFloatingIPs(), gc.HasLen, 0)
  1179  	var expected struct {
  1180  		IPs []nova.FloatingIP `json:"floating_ips"`
  1181  	}
  1182  	resp, err := s.authRequest("GET", "/os-floating-ips", nil, nil)
  1183  	c.Assert(err, gc.IsNil)
  1184  	c.Assert(resp.StatusCode, gc.Equals, http.StatusOK)
  1185  	assertJSON(c, resp, &expected)
  1186  	c.Assert(expected.IPs, gc.HasLen, 0)
  1187  	fips := []nova.FloatingIP{
  1188  		{Id: "1", IP: "1.2.3.4", Pool: "nova"},
  1189  		{Id: "2", IP: "4.3.2.1", Pool: "nova"},
  1190  	}
  1191  	for _, fip := range fips {
  1192  		err := s.service.addFloatingIP(fip)
  1193  		defer s.service.removeFloatingIP(fip.Id)
  1194  		c.Assert(err, gc.IsNil)
  1195  	}
  1196  	resp, err = s.authRequest("GET", "/os-floating-ips", nil, nil)
  1197  	c.Assert(err, gc.IsNil)
  1198  	c.Assert(resp.StatusCode, gc.Equals, http.StatusOK)
  1199  	assertJSON(c, resp, &expected)
  1200  	if expected.IPs[0].Id != fips[0].Id {
  1201  		expected.IPs[0], expected.IPs[1] = expected.IPs[1], expected.IPs[0]
  1202  	}
  1203  	c.Assert(expected.IPs, gc.DeepEquals, fips)
  1204  	var expectedIP struct {
  1205  		IP nova.FloatingIP `json:"floating_ip"`
  1206  	}
  1207  	resp, err = s.authRequest("GET", "/os-floating-ips/1", nil, nil)
  1208  	c.Assert(err, gc.IsNil)
  1209  	c.Assert(resp.StatusCode, gc.Equals, http.StatusOK)
  1210  	assertJSON(c, resp, &expectedIP)
  1211  	c.Assert(expectedIP.IP, gc.DeepEquals, fips[0])
  1212  }
  1213  
  1214  func (s *NovaHTTPSuite) TestDeleteFloatingIP(c *gc.C) {
  1215  	if s.service.useNeutronNetworking {
  1216  		c.Skip("skipped in novaservice when using Neutron Model")
  1217  	}
  1218  	fip := nova.FloatingIP{Id: "1", IP: "10.0.0.1", Pool: "nova"}
  1219  	err := s.service.addFloatingIP(fip)
  1220  	c.Assert(err, gc.IsNil)
  1221  	defer s.service.removeFloatingIP(fip.Id)
  1222  	resp, err := s.authRequest("DELETE", "/os-floating-ips/1", nil, nil)
  1223  	c.Assert(err, gc.IsNil)
  1224  	c.Assert(resp.StatusCode, gc.Equals, http.StatusAccepted)
  1225  	_, err = s.service.floatingIP(fip.Id)
  1226  	c.Assert(err, gc.NotNil)
  1227  }
  1228  
  1229  func (s *NovaHTTPSuite) TestAddServerFloatingIP(c *gc.C) {
  1230  	fip := nova.FloatingIP{Id: "1", IP: "1.2.3.4"}
  1231  	server := nova.ServerDetail{
  1232  		Id:        "sr1",
  1233  		Addresses: map[string][]nova.IPAddress{"private": []nova.IPAddress{}},
  1234  	}
  1235  	err := s.service.addFloatingIP(fip)
  1236  	c.Assert(err, gc.IsNil)
  1237  	defer s.service.removeFloatingIP(fip.Id)
  1238  	err = s.service.addServer(server)
  1239  	c.Assert(err, gc.IsNil)
  1240  	defer s.service.removeServer(server.Id)
  1241  	c.Assert(s.service.hasServerFloatingIP(server.Id, fip.IP), gc.Equals, false)
  1242  	var req struct {
  1243  		AddFloatingIP struct {
  1244  			Address string `json:"address"`
  1245  		} `json:"addFloatingIp"`
  1246  	}
  1247  	req.AddFloatingIP.Address = fip.IP
  1248  	resp, err := s.jsonRequest("POST", "/servers/"+server.Id+"/action", req, nil)
  1249  	c.Assert(err, gc.IsNil)
  1250  	c.Assert(resp.StatusCode, gc.Equals, http.StatusAccepted)
  1251  	c.Assert(s.service.hasServerFloatingIP(server.Id, fip.IP), gc.Equals, true)
  1252  	err = s.service.removeServerFloatingIP(server.Id, fip.Id)
  1253  	c.Assert(err, gc.IsNil)
  1254  }
  1255  
  1256  func (s *NovaHTTPSuite) TestRemoveServerFloatingIP(c *gc.C) {
  1257  	fip := nova.FloatingIP{Id: "1", IP: "1.2.3.4"}
  1258  	server := nova.ServerDetail{
  1259  		Id:        "sr1",
  1260  		Addresses: map[string][]nova.IPAddress{"private": []nova.IPAddress{}},
  1261  	}
  1262  	err := s.service.addFloatingIP(fip)
  1263  	c.Assert(err, gc.IsNil)
  1264  	defer s.service.removeFloatingIP(fip.Id)
  1265  	err = s.service.addServer(server)
  1266  	c.Assert(err, gc.IsNil)
  1267  	defer s.service.removeServer(server.Id)
  1268  	err = s.service.addServerFloatingIP(server.Id, fip.Id)
  1269  	c.Assert(err, gc.IsNil)
  1270  	defer s.service.removeServerFloatingIP(server.Id, fip.Id)
  1271  	c.Assert(s.service.hasServerFloatingIP(server.Id, fip.IP), gc.Equals, true)
  1272  	var req struct {
  1273  		RemoveFloatingIP struct {
  1274  			Address string `json:"address"`
  1275  		} `json:"removeFloatingIp"`
  1276  	}
  1277  	req.RemoveFloatingIP.Address = fip.IP
  1278  	resp, err := s.jsonRequest("POST", "/servers/"+server.Id+"/action", req, nil)
  1279  	c.Assert(err, gc.IsNil)
  1280  	c.Assert(resp.StatusCode, gc.Equals, http.StatusAccepted)
  1281  	c.Assert(s.service.hasServerFloatingIP(server.Id, fip.IP), gc.Equals, false)
  1282  }
  1283  
  1284  func (s *NovaHTTPSuite) TestListAvailabilityZones(c *gc.C) {
  1285  	resp, err := s.jsonRequest("GET", "/os-availability-zone", nil, nil)
  1286  	c.Assert(err, gc.IsNil)
  1287  	assertBody(c, resp, errNotFoundJSON)
  1288  
  1289  	zones := []nova.AvailabilityZone{
  1290  		{Name: "az1"},
  1291  		{
  1292  			Name: "az2", State: nova.AvailabilityZoneState{Available: true},
  1293  		},
  1294  	}
  1295  	s.service.SetAvailabilityZones(zones...)
  1296  	resp, err = s.jsonRequest("GET", "/os-availability-zone", nil, nil)
  1297  	c.Assert(err, gc.IsNil)
  1298  	var expected struct {
  1299  		Zones []nova.AvailabilityZone `json:"availabilityZoneInfo"`
  1300  	}
  1301  	assertJSON(c, resp, &expected)
  1302  	c.Assert(expected.Zones, gc.DeepEquals, zones)
  1303  }
  1304  
  1305  func (s *NovaHTTPSuite) TestAddServerOSInterface(c *gc.C) {
  1306  	osInterface := nova.OSInterface{
  1307  		FixedIPs: []nova.PortFixedIP{
  1308  			{IPAddress: "10.0.0.1", SubnetID: "sub-net-id"},
  1309  		},
  1310  		IPAddress: "10.0.0.1",
  1311  	}
  1312  	server := nova.ServerDetail{
  1313  		Id:        "sr1",
  1314  		Addresses: map[string][]nova.IPAddress{"private": []nova.IPAddress{}},
  1315  	}
  1316  	s.service.AddOSInterface(server.Id, osInterface)
  1317  	c.Assert(s.service.hasServerOSInterface(server.Id, osInterface.IPAddress), gc.Equals, true)
  1318  
  1319  	defer s.service.RemoveOSInterface(server.Id, osInterface.IPAddress)
  1320  	s.service.RemoveOSInterface(server.Id, osInterface.IPAddress)
  1321  
  1322  	defer s.service.removeServer(server.Id)
  1323  	c.Assert(s.service.hasServerOSInterface(server.Id, osInterface.IPAddress), gc.Equals, false)
  1324  
  1325  	s.service.AddOSInterface(server.Id, osInterface)
  1326  
  1327  	resp, err := s.jsonRequest("GET", "/servers/"+server.Id+"/os-interface", nil, nil)
  1328  	c.Assert(err, gc.IsNil)
  1329  	c.Assert(resp.StatusCode, gc.Equals, http.StatusOK)
  1330  }
  1331  
  1332  func (s *NovaHTTPSSuite) SetUpSuite(c *gc.C) {
  1333  	s.HTTPSuite.SetUpSuite(c)
  1334  	identityDouble := identityservice.NewUserPass()
  1335  	userInfo := identityDouble.AddUser("fred", "secret", "tenant", "default")
  1336  	s.token = userInfo.Token
  1337  	c.Assert(s.Server.URL[:8], gc.Equals, "https://")
  1338  	s.service = New(s.Server.URL, versionPath, userInfo.TenantId, region, identityDouble, nil)
  1339  	if s.useNeutronNetworking {
  1340  		c.Logf("Nova Service using Neutron Networking")
  1341  		s.service.AddNeutronModel(neutronmodel.New())
  1342  	} else {
  1343  		c.Logf("Nova Service using Nova Networking")
  1344  	}
  1345  }
  1346  
  1347  func (s *NovaHTTPSSuite) TearDownSuite(c *gc.C) {
  1348  	s.HTTPSuite.TearDownSuite(c)
  1349  }
  1350  
  1351  func (s *NovaHTTPSSuite) SetUpTest(c *gc.C) {
  1352  	s.HTTPSuite.SetUpTest(c)
  1353  	s.service.SetupHTTP(s.Mux)
  1354  }
  1355  
  1356  func (s *NovaHTTPSSuite) TearDownTest(c *gc.C) {
  1357  	s.HTTPSuite.TearDownTest(c)
  1358  }
  1359  
  1360  func (s *NovaHTTPSSuite) TestHasHTTPSServiceURL(c *gc.C) {
  1361  	endpoints := s.service.Endpoints()
  1362  	c.Assert(endpoints[0].PublicURL[:8], gc.Equals, "https://")
  1363  }
  1364  
  1365  func (s *NovaHTTPSuite) TestSetServerMetadata(c *gc.C) {
  1366  	const serverId = "sr1"
  1367  
  1368  	err := s.service.addServer(nova.ServerDetail{Id: serverId})
  1369  	c.Assert(err, gc.IsNil)
  1370  	defer s.service.removeServer(serverId)
  1371  	var req struct {
  1372  		Metadata map[string]string `json:"metadata"`
  1373  	}
  1374  	req.Metadata = map[string]string{
  1375  		"k1": "v1",
  1376  		"k2": "v2",
  1377  	}
  1378  	resp, err := s.jsonRequest("POST", "/servers/"+serverId+"/metadata", req, nil)
  1379  	c.Assert(err, gc.IsNil)
  1380  	c.Assert(resp.StatusCode, gc.Equals, http.StatusOK)
  1381  
  1382  	server, err := s.service.server(serverId)
  1383  	c.Assert(err, gc.IsNil)
  1384  	c.Assert(server.Metadata, gc.DeepEquals, req.Metadata)
  1385  }
  1386  
  1387  func (s *NovaHTTPSuite) TestAttachVolumeBlankDeviceName(c *gc.C) {
  1388  	var req struct {
  1389  		VolumeAttachment struct {
  1390  			Device string `json:"device"`
  1391  		} `json:"volumeAttachment"`
  1392  	}
  1393  	resp, err := s.jsonRequest("POST", "/servers/123/os-volume_attachments", req, nil)
  1394  	c.Assert(err, gc.IsNil)
  1395  	c.Assert(resp.StatusCode, gc.Equals, http.StatusBadRequest)
  1396  
  1397  	// Passing an empty string in the "device" attribute
  1398  	// is invalid. It should be omitted instead.
  1399  	message := "Invalid input for field/attribute device. Value: '' does not match '(^/dev/x{0,1}[a-z]{0,1}d{0,1})([a-z]+)[0-9]*$'"
  1400  	assertBody(c, resp, &errorResponse{
  1401  		http.StatusBadRequest,
  1402  		fmt.Sprintf(`{"badRequest": {"message": "%s", "code": 400}}`, message),
  1403  		"application/json; charset=UTF-8",
  1404  		message,
  1405  		nil,
  1406  		nil,
  1407  	})
  1408  
  1409  }