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 }