gopkg.in/goose.v2@v2.0.1/testservices/neutronservice/service_http_test.go (about) 1 // Neutron double testing service - HTTP API tests 2 3 package neutronservice 4 5 import ( 6 "bytes" 7 "encoding/json" 8 "fmt" 9 "io/ioutil" 10 "net/http" 11 "strconv" 12 "strings" 13 14 gc "gopkg.in/check.v1" 15 16 "gopkg.in/goose.v2/neutron" 17 "gopkg.in/goose.v2/testing/httpsuite" 18 "gopkg.in/goose.v2/testservices/identityservice" 19 "gopkg.in/goose.v2/testservices/neutronmodel" 20 ) 21 22 type NeutronHTTPSuite struct { 23 httpsuite.HTTPSuite 24 service *Neutron 25 token string 26 } 27 28 var _ = gc.Suite(&NeutronHTTPSuite{}) 29 30 type NeutronHTTPSSuite struct { 31 httpsuite.HTTPSuite 32 service *Neutron 33 token string 34 } 35 36 var _ = gc.Suite(&NeutronHTTPSSuite{HTTPSuite: httpsuite.HTTPSuite{UseTLS: true}}) 37 38 func (s *NeutronHTTPSuite) SetUpSuite(c *gc.C) { 39 s.HTTPSuite.SetUpSuite(c) 40 identityDouble := identityservice.NewUserPass() 41 userInfo := identityDouble.AddUser("fred", "secret", "tenant", "default") 42 s.token = userInfo.Token 43 s.service = New(s.Server.URL, versionPath, userInfo.TenantId, region, identityDouble, nil) 44 s.service.AddNeutronModel(neutronmodel.New()) 45 } 46 47 func (s *NeutronHTTPSuite) TearDownSuite(c *gc.C) { 48 s.HTTPSuite.TearDownSuite(c) 49 } 50 51 func (s *NeutronHTTPSuite) SetUpTest(c *gc.C) { 52 s.HTTPSuite.SetUpTest(c) 53 s.service.SetupHTTP(s.Mux) 54 // this is otherwise handled not directly by neutron test service 55 // but by openstack that tries for / before. 56 s.Mux.Handle("/", s.service.handler((*Neutron).handleRoot)) 57 } 58 59 func (s *NeutronHTTPSuite) TearDownTest(c *gc.C) { 60 s.HTTPSuite.TearDownTest(c) 61 } 62 63 // assertJSON asserts the passed http.Response's body can be 64 // unmarshalled into the given expected object, populating it with the 65 // successfully parsed data. 66 func assertJSON(c *gc.C, resp *http.Response, expected interface{}) { 67 body, err := ioutil.ReadAll(resp.Body) 68 defer resp.Body.Close() 69 c.Assert(err, gc.IsNil) 70 err = json.Unmarshal(body, &expected) 71 c.Assert(err, gc.IsNil) 72 } 73 74 // assertBody asserts the passed http.Response's body matches the 75 // expected response, replacing any variables in the expected body. 76 func assertBody(c *gc.C, resp *http.Response, expected *errorResponse) { 77 body, err := ioutil.ReadAll(resp.Body) 78 defer resp.Body.Close() 79 c.Assert(err, gc.IsNil) 80 expBody := expected.requestBody(resp.Request) 81 // cast to string for easier asserts debugging 82 c.Assert(string(body), gc.Equals, string(expBody)) 83 } 84 85 // sendRequest constructs an HTTP request from the parameters and 86 // sends it, returning the response or an error. 87 func (s *NeutronHTTPSuite) sendRequest(method, url string, body []byte, headers http.Header) (*http.Response, error) { 88 if !strings.HasPrefix(url, "http") { 89 url = "http://" + s.service.Hostname + strings.TrimLeft(url, "/") 90 } 91 req, err := http.NewRequest(method, url, bytes.NewReader(body)) 92 if err != nil { 93 return nil, err 94 } 95 for header, values := range headers { 96 for _, value := range values { 97 req.Header.Add(header, value) 98 } 99 } 100 // workaround for https://code.google.com/p/go/issues/detail?id=4454 101 req.Header.Set("Content-Length", strconv.Itoa(len(body))) 102 return http.DefaultClient.Do(req) 103 } 104 105 // authRequest is a shortcut for sending requests with pre-set token 106 // header and correct version prefix and tenant ID in the URL. 107 func (s *NeutronHTTPSuite) authRequest(method, path string, body []byte, headers http.Header) (*http.Response, error) { 108 if headers == nil { 109 headers = make(http.Header) 110 } 111 headers.Set(authToken, s.token) 112 url := s.service.endpointURL(true, path) 113 return s.sendRequest(method, url, body, headers) 114 } 115 116 // jsonRequest serializes the passed body object to JSON and sends a 117 // the request with authRequest(). 118 func (s *NeutronHTTPSuite) jsonRequest(method, path string, body interface{}, headers http.Header) (*http.Response, error) { 119 jsonBody, err := json.Marshal(body) 120 if err != nil { 121 return nil, err 122 } 123 return s.authRequest(method, path, jsonBody, headers) 124 } 125 126 // setHeader creates http.Header map, sets the given header, and 127 // returns the map. 128 func setHeader(header, value string) http.Header { 129 h := make(http.Header) 130 h.Set(header, value) 131 return h 132 } 133 134 // SimpleTest defines a simple request without a body and expected response. 135 type SimpleTest struct { 136 unauth bool 137 method string 138 url string 139 headers http.Header 140 expect *errorResponse 141 } 142 143 func (s *NeutronHTTPSuite) simpleTests() []SimpleTest { 144 var simpleTests = []SimpleTest{ 145 { 146 unauth: true, 147 method: "GET", 148 url: "/any", 149 headers: make(http.Header), 150 expect: errUnauthorized, 151 }, 152 { 153 unauth: true, 154 method: "POST", 155 url: "/any", 156 headers: setHeader(authToken, "phony"), 157 expect: errUnauthorized, 158 }, 159 { 160 unauth: true, 161 method: "GET", 162 url: "/any", 163 headers: setHeader(authToken, s.token), 164 expect: errMultipleChoices, 165 }, 166 { 167 unauth: true, 168 method: "POST", 169 url: "/any/unknown/one", 170 headers: setHeader(authToken, s.token), 171 expect: errMultipleChoices, 172 }, 173 { 174 unauth: true, 175 method: "GET", 176 url: versionPath + "/phony_token", 177 headers: setHeader(authToken, s.token), 178 expect: errBadRequestMalformedURL, 179 }, 180 181 { 182 method: "GET", 183 url: neutron.ApiSecurityGroupsV2 + "/42", 184 expect: errNotFoundJSONSG, 185 }, 186 { 187 method: "POST", 188 url: neutron.ApiSecurityGroupsV2, 189 expect: errBadRequestIncorrect, 190 }, 191 { 192 method: "POST", 193 url: neutron.ApiSecurityGroupsV2 + "/invalid", 194 expect: errNotFound, 195 }, 196 { 197 method: "PUT", 198 url: neutron.ApiSecurityGroupsV2, 199 expect: errNotFound, 200 }, 201 { 202 method: "DELETE", 203 url: neutron.ApiSecurityGroupsV2, 204 expect: errNotFound, 205 }, 206 { 207 method: "DELETE", 208 url: neutron.ApiSecurityGroupsV2 + "/42", 209 expect: errNotFoundJSONSG, 210 }, 211 212 { 213 method: "GET", 214 url: neutron.ApiSecurityGroupRulesV2, 215 expect: errNotFoundJSON, 216 }, 217 { 218 method: "GET", 219 url: neutron.ApiSecurityGroupRulesV2 + "/invalid", 220 expect: errNotFoundJSON, 221 }, 222 { 223 method: "GET", 224 url: neutron.ApiSecurityGroupRulesV2 + "/42", 225 expect: errNotFoundJSON, 226 }, 227 { 228 method: "POST", 229 url: neutron.ApiSecurityGroupRulesV2, 230 expect: errBadRequestIncorrect, 231 }, 232 { 233 method: "POST", 234 url: neutron.ApiSecurityGroupRulesV2 + "/invalid", 235 expect: errNotFound, 236 }, 237 { 238 method: "PUT", 239 url: neutron.ApiSecurityGroupRulesV2, 240 expect: errNotFound, 241 }, 242 { 243 method: "PUT", 244 url: neutron.ApiSecurityGroupRulesV2 + "/invalid", 245 expect: errNotFound, 246 }, 247 { 248 method: "DELETE", 249 url: neutron.ApiSecurityGroupRulesV2, 250 expect: errNotFound, 251 }, 252 { 253 method: "DELETE", 254 url: neutron.ApiSecurityGroupRulesV2 + "/42", 255 expect: errNotFoundJSONSGR, 256 }, 257 258 { 259 method: "GET", 260 url: neutron.ApiFloatingIPsV2 + "/42", 261 expect: errNotFoundJSON, 262 }, 263 { 264 method: "POST", 265 url: neutron.ApiFloatingIPsV2 + "/invalid", 266 expect: errNotFound, 267 }, 268 { 269 method: "PUT", 270 url: neutron.ApiFloatingIPsV2, 271 expect: errNotFound, 272 }, 273 { 274 method: "PUT", 275 url: neutron.ApiFloatingIPsV2 + "/invalid", 276 expect: errNotFound, 277 }, 278 { 279 method: "DELETE", 280 url: neutron.ApiFloatingIPsV2, 281 expect: errNotFound, 282 }, 283 { 284 method: "DELETE", 285 url: neutron.ApiFloatingIPsV2 + "/invalid", 286 expect: errNotFoundJSON, 287 }, 288 { 289 method: "GET", 290 url: neutron.ApiNetworksV2 + "/42", 291 expect: errNotFoundJSON, 292 }, 293 { 294 method: "POST", 295 url: neutron.ApiNetworksV2 + "/invalid", 296 expect: errNotFound, 297 }, 298 { 299 method: "PUT", 300 url: neutron.ApiNetworksV2, 301 expect: errNotFound, 302 }, 303 { 304 method: "PUT", 305 url: neutron.ApiNetworksV2 + "/invalid", 306 expect: errNotFound, 307 }, 308 { 309 method: "DELETE", 310 url: neutron.ApiNetworksV2, 311 expect: errNotFound, 312 }, 313 { 314 method: "DELETE", 315 url: neutron.ApiNetworksV2 + "/invalid", 316 expect: errNotFound, 317 }, 318 319 { 320 method: "GET", 321 url: neutron.ApiSubnetsV2 + "/42", 322 expect: errNotFoundJSON, 323 }, 324 { 325 method: "POST", 326 url: neutron.ApiSubnetsV2 + "/invalid", 327 expect: errNotFound, 328 }, 329 { 330 method: "PUT", 331 url: neutron.ApiSubnetsV2, 332 expect: errNotFound, 333 }, 334 { 335 method: "PUT", 336 url: neutron.ApiSubnetsV2 + "/invalid", 337 expect: errNotFound, 338 }, 339 { 340 method: "DELETE", 341 url: neutron.ApiSubnetsV2, 342 expect: errNotFound, 343 }, 344 { 345 method: "DELETE", 346 url: neutron.ApiSubnetsV2 + "/invalid", 347 expect: errNotFound, 348 }, 349 350 { 351 method: "GET", 352 url: neutron.ApiPortsV2 + "/42", 353 expect: errNotFoundJSONP, 354 }, 355 { 356 method: "POST", 357 url: neutron.ApiPortsV2, 358 expect: errBadRequestIncorrect, 359 }, 360 { 361 method: "POST", 362 url: neutron.ApiPortsV2 + "/invalid", 363 expect: errNotFound, 364 }, 365 { 366 method: "DELETE", 367 url: neutron.ApiPortsV2, 368 expect: errNotFound, 369 }, 370 { 371 method: "DELETE", 372 url: neutron.ApiPortsV2 + "/42", 373 expect: errNotFoundJSONP, 374 }, 375 } 376 return simpleTests 377 } 378 379 func (s *NeutronHTTPSuite) TestSimpleRequestTests(c *gc.C) { 380 simpleTests := s.simpleTests() 381 for i, t := range simpleTests { 382 c.Logf("#%d. %s %s -> %d", i, t.method, t.url, t.expect.code) 383 if t.headers == nil { 384 t.headers = make(http.Header) 385 t.headers.Set(authToken, s.token) 386 } 387 var ( 388 resp *http.Response 389 err error 390 ) 391 if t.unauth { 392 resp, err = s.sendRequest(t.method, t.url, nil, t.headers) 393 } else { 394 resp, err = s.authRequest(t.method, t.url, nil, t.headers) 395 } 396 c.Assert(err, gc.IsNil) 397 c.Assert(resp.StatusCode, gc.Equals, t.expect.code) 398 assertBody(c, resp, t.expect) 399 } 400 fmt.Printf("total: %d\n", len(simpleTests)) 401 } 402 403 func (s *NeutronHTTPSuite) TestNewUUID(c *gc.C) { 404 uuid, err := newUUID() 405 c.Assert(err, gc.IsNil) 406 var p1, p2, p3, p4, p5 string 407 num, err := fmt.Sscanf(uuid, "%8x-%4x-%4x-%4x-%12x", &p1, &p2, &p3, &p4, &p5) 408 c.Assert(err, gc.IsNil) 409 c.Assert(num, gc.Equals, 5) 410 uuid2, err := newUUID() 411 c.Assert(err, gc.IsNil) 412 c.Assert(uuid2, gc.Not(gc.Equals), uuid) 413 } 414 415 func (s *NeutronHTTPSuite) TestGetSecurityGroups(c *gc.C) { 416 // There is always a default security group. 417 groups := s.service.allSecurityGroups() 418 c.Assert(groups, gc.HasLen, 1) 419 var expected struct { 420 Groups []neutron.SecurityGroupV2 `json:"security_groups"` 421 } 422 resp, err := s.authRequest("GET", neutron.ApiSecurityGroupsV2, nil, nil) 423 c.Assert(err, gc.IsNil) 424 c.Assert(resp.StatusCode, gc.Equals, http.StatusOK) 425 assertJSON(c, resp, &expected) 426 c.Assert(expected.Groups, gc.HasLen, 1) 427 groups = []neutron.SecurityGroupV2{ 428 { 429 Id: "1", 430 Name: "group 1", 431 TenantId: s.service.TenantId, 432 Rules: []neutron.SecurityGroupRuleV2{}, 433 }, 434 { 435 Id: "2", 436 Name: "group 2", 437 TenantId: s.service.TenantId, 438 Rules: []neutron.SecurityGroupRuleV2{}, 439 }, 440 } 441 for _, group := range groups { 442 err := s.service.addSecurityGroup(group) 443 c.Assert(err, gc.IsNil) 444 defer s.service.removeSecurityGroup(group.Id) 445 } 446 groups[0].Rules = defaultSecurityGroupRules(groups[0].Id, groups[0].TenantId) 447 groups[1].Rules = defaultSecurityGroupRules(groups[1].Id, groups[1].TenantId) 448 resp, err = s.authRequest("GET", neutron.ApiSecurityGroupsV2, nil, nil) 449 c.Assert(err, gc.IsNil) 450 c.Assert(resp.StatusCode, gc.Equals, http.StatusOK) 451 assertJSON(c, resp, &expected) 452 c.Assert(expected.Groups, gc.HasLen, len(groups)+1) 453 checkGroupsInList(c, groups, expected.Groups) 454 var expectedGroup struct { 455 Group neutron.SecurityGroupV2 `json:"security_group"` 456 } 457 url := fmt.Sprintf("%s/%s", neutron.ApiSecurityGroupsV2, "1") 458 resp, err = s.authRequest("GET", url, nil, nil) 459 c.Assert(err, gc.IsNil) 460 c.Assert(resp.StatusCode, gc.Equals, http.StatusOK) 461 assertJSON(c, resp, &expectedGroup) 462 c.Assert(expectedGroup.Group, gc.DeepEquals, groups[0]) 463 } 464 465 func defaultSecurityGroupRules(groupId, tenantId string) []neutron.SecurityGroupRuleV2 { 466 id, _ := strconv.Atoi(groupId) 467 id1 := id * 999 468 id2 := id * 998 469 return []neutron.SecurityGroupRuleV2{ 470 { 471 Direction: "egress", 472 EthernetType: "IPv4", 473 Id: strconv.Itoa(id1), 474 TenantId: tenantId, 475 ParentGroupId: groupId, 476 }, 477 { 478 Direction: "egress", 479 EthernetType: "IPv6", 480 Id: strconv.Itoa(id2), 481 TenantId: tenantId, 482 ParentGroupId: groupId, 483 }, 484 } 485 } 486 487 func (s *NeutronHTTPSuite) TestAddSecurityGroup(c *gc.C) { 488 group := neutron.SecurityGroupV2{ 489 Id: "1", 490 Name: "group 1", 491 Description: "desc", 492 TenantId: s.service.TenantId, 493 Rules: []neutron.SecurityGroupRuleV2{}, 494 } 495 _, err := s.service.securityGroup(group.Id) 496 c.Assert(err, gc.NotNil) 497 group.Rules = defaultSecurityGroupRules(group.Id, group.TenantId) 498 var req struct { 499 Group struct { 500 Name string `json:"name"` 501 Description string `json:"description"` 502 } `json:"security_group"` 503 } 504 req.Group.Name = group.Name 505 req.Group.Description = group.Description 506 var expected struct { 507 Group neutron.SecurityGroupV2 `json:"security_group"` 508 } 509 resp, err := s.jsonRequest("POST", neutron.ApiSecurityGroupsV2, req, nil) 510 c.Assert(err, gc.IsNil) 511 c.Assert(resp.StatusCode, gc.Equals, http.StatusCreated) 512 assertJSON(c, resp, &expected) 513 c.Assert(expected.Group, gc.DeepEquals, group) 514 err = s.service.removeSecurityGroup(group.Id) 515 c.Assert(err, gc.IsNil) 516 } 517 518 func (s *NeutronHTTPSuite) TestDeleteSecurityGroup(c *gc.C) { 519 group := neutron.SecurityGroupV2{Id: "1", Name: "group 1", TenantId: s.service.TenantId} 520 _, err := s.service.securityGroup(group.Id) 521 c.Assert(err, gc.NotNil) 522 err = s.service.addSecurityGroup(group) 523 c.Assert(err, gc.IsNil) 524 defer s.service.removeSecurityGroup(group.Id) 525 url := fmt.Sprintf("%s/%s", neutron.ApiSecurityGroupsV2, "1") 526 resp, err := s.authRequest("DELETE", url, nil, nil) 527 c.Assert(err, gc.IsNil) 528 c.Assert(resp.StatusCode, gc.Equals, http.StatusNoContent) 529 _, err = s.service.securityGroup(group.Id) 530 c.Assert(err, gc.NotNil) 531 } 532 533 func (s *NeutronHTTPSuite) TestAddSecurityGroupRule(c *gc.C) { 534 group1 := neutron.SecurityGroupV2{Id: "1", Name: "src", TenantId: s.service.TenantId} 535 group2 := neutron.SecurityGroupV2{Id: "2", Name: "tgt", TenantId: s.service.TenantId} 536 err := s.service.addSecurityGroup(group1) 537 c.Assert(err, gc.IsNil) 538 defer s.service.removeSecurityGroup(group1.Id) 539 err = s.service.addSecurityGroup(group2) 540 c.Assert(err, gc.IsNil) 541 defer s.service.removeSecurityGroup(group2.Id) 542 riIngress := neutron.RuleInfoV2{ 543 ParentGroupId: "1", 544 Direction: "ingress", 545 PortRangeMax: 22, 546 PortRangeMin: 22, 547 IPProtocol: "tcp", 548 RemoteIPPrefix: "1.2.3.4/5", 549 } 550 riIngress2 := neutron.RuleInfoV2{ 551 ParentGroupId: "1", 552 Direction: "ingress", 553 PortRangeMax: 22, 554 PortRangeMin: 22, 555 IPProtocol: "tcp", 556 RemoteIPPrefix: "2.3.4.5/6", 557 } 558 riEgress := neutron.RuleInfoV2{ 559 ParentGroupId: group2.Id, 560 Direction: "egress", 561 PortRangeMax: 22, 562 PortRangeMin: 22, 563 IPProtocol: "tcp", 564 RemoteIPPrefix: "5.4.3.2/1", 565 } 566 riIngress6 := neutron.RuleInfoV2{ 567 ParentGroupId: "1", 568 Direction: "ingress", 569 PortRangeMax: 22, 570 PortRangeMin: 22, 571 IPProtocol: "tcp", 572 RemoteIPPrefix: "2001:db8:42::/64", 573 EthernetType: "IPv6", 574 } 575 rule1 := neutron.SecurityGroupRuleV2{ 576 Id: "1", 577 ParentGroupId: group1.Id, 578 Direction: riIngress.Direction, 579 PortRangeMax: &riIngress.PortRangeMax, 580 PortRangeMin: &riIngress.PortRangeMin, 581 IPProtocol: &riIngress.IPProtocol, 582 RemoteIPPrefix: riIngress.RemoteIPPrefix, 583 } 584 rule2 := neutron.SecurityGroupRuleV2{ 585 Id: "2", 586 ParentGroupId: group1.Id, 587 Direction: riIngress2.Direction, 588 PortRangeMax: &riIngress2.PortRangeMax, 589 PortRangeMin: &riIngress2.PortRangeMin, 590 IPProtocol: &riIngress2.IPProtocol, 591 RemoteIPPrefix: riIngress2.RemoteIPPrefix, 592 } 593 rule3 := neutron.SecurityGroupRuleV2{ 594 Id: "3", 595 ParentGroupId: group2.Id, 596 Direction: riEgress.Direction, 597 PortRangeMax: &riEgress.PortRangeMax, 598 PortRangeMin: &riEgress.PortRangeMin, 599 IPProtocol: &riEgress.IPProtocol, 600 } 601 rule6 := neutron.SecurityGroupRuleV2{ 602 Id: "5", 603 ParentGroupId: group1.Id, 604 Direction: riIngress6.Direction, 605 PortRangeMax: &riIngress6.PortRangeMax, 606 PortRangeMin: &riIngress6.PortRangeMin, 607 IPProtocol: &riIngress6.IPProtocol, 608 RemoteIPPrefix: riIngress6.RemoteIPPrefix, 609 } 610 ok := s.service.hasSecurityGroupRule(group1.Id, rule1.Id) 611 c.Assert(ok, gc.Equals, false) 612 ok = s.service.hasSecurityGroupRule(group2.Id, rule2.Id) 613 c.Assert(ok, gc.Equals, false) 614 var req struct { 615 Rule neutron.RuleInfoV2 `json:"security_group_rule"` 616 } 617 req.Rule = riIngress 618 var expected struct { 619 Rule neutron.SecurityGroupRuleV2 `json:"security_group_rule"` 620 } 621 resp, err := s.jsonRequest("POST", neutron.ApiSecurityGroupRulesV2, req, nil) 622 c.Assert(err, gc.IsNil) 623 c.Assert(resp.StatusCode, gc.Equals, http.StatusCreated) 624 assertJSON(c, resp, &expected) 625 c.Assert(expected.Rule.Id, gc.Equals, rule1.Id) 626 c.Assert(expected.Rule.ParentGroupId, gc.Equals, rule1.ParentGroupId) 627 c.Assert(*expected.Rule.PortRangeMax, gc.Equals, *rule1.PortRangeMax) 628 c.Assert(*expected.Rule.PortRangeMin, gc.Equals, *rule1.PortRangeMin) 629 c.Assert(*expected.Rule.IPProtocol, gc.Equals, *rule1.IPProtocol) 630 c.Assert(expected.Rule.Direction, gc.Equals, rule1.Direction) 631 c.Assert(expected.Rule.RemoteIPPrefix, gc.Equals, rule1.RemoteIPPrefix) 632 // Attempt to create duplicate rule should fail 633 resp, err = s.jsonRequest("POST", neutron.ApiSecurityGroupRulesV2, req, nil) 634 c.Assert(resp.StatusCode, gc.Equals, http.StatusBadRequest) 635 err = s.service.removeSecurityGroupRule(rule1.Id) 636 c.Assert(err, gc.IsNil) 637 // Attempt to create rule with all fields but RemoteIPPrefix identical should pass 638 req.Rule = riIngress2 639 resp, err = s.jsonRequest("POST", neutron.ApiSecurityGroupRulesV2, req, nil) 640 c.Assert(err, gc.IsNil) 641 c.Assert(resp.StatusCode, gc.Equals, http.StatusCreated) 642 err = s.service.removeSecurityGroupRule(rule2.Id) 643 c.Assert(err, gc.IsNil) 644 assertJSON(c, resp, &expected) 645 c.Assert(expected.Rule.Id, gc.Equals, rule2.Id) 646 c.Assert(expected.Rule.ParentGroupId, gc.Equals, rule2.ParentGroupId) 647 c.Assert(*expected.Rule.PortRangeMax, gc.Equals, *rule2.PortRangeMax) 648 c.Assert(*expected.Rule.PortRangeMin, gc.Equals, *rule2.PortRangeMin) 649 c.Assert(*expected.Rule.IPProtocol, gc.Equals, *rule2.IPProtocol) 650 c.Assert(expected.Rule.Direction, gc.Equals, rule2.Direction) 651 c.Assert(expected.Rule.RemoteIPPrefix, gc.Equals, rule2.RemoteIPPrefix) 652 req.Rule = riEgress 653 resp, err = s.jsonRequest("POST", neutron.ApiSecurityGroupRulesV2, req, nil) 654 c.Assert(err, gc.IsNil) 655 c.Assert(resp.StatusCode, gc.Equals, http.StatusCreated) 656 err = s.service.removeSecurityGroupRule(rule3.Id) 657 c.Assert(err, gc.IsNil) 658 assertJSON(c, resp, &expected) 659 c.Assert(expected.Rule.Id, gc.Equals, rule3.Id) 660 c.Assert(expected.Rule.ParentGroupId, gc.Equals, rule3.ParentGroupId) 661 // Attempt to create rule with IPv6 RemoteIPPrefix without specifying EthernetType, should fail 662 req.Rule = riIngress6 663 req.Rule.EthernetType = "" 664 resp, err = s.jsonRequest("POST", neutron.ApiSecurityGroupRulesV2, req, nil) 665 c.Assert(resp.StatusCode, gc.Equals, http.StatusBadRequest) 666 // Attempt to create rule with IPv6 RemoteIPPrefix with correct EthernetType, should pass 667 req.Rule = riIngress6 668 resp, err = s.jsonRequest("POST", neutron.ApiSecurityGroupRulesV2, req, nil) 669 c.Assert(err, gc.IsNil) 670 c.Assert(resp.StatusCode, gc.Equals, http.StatusCreated) 671 err = s.service.removeSecurityGroupRule(rule6.Id) 672 c.Assert(err, gc.IsNil) 673 assertJSON(c, resp, &expected) 674 c.Assert(expected.Rule.Id, gc.Equals, rule6.Id) 675 c.Assert(expected.Rule.ParentGroupId, gc.Equals, rule6.ParentGroupId) 676 } 677 678 func (s *NeutronHTTPSuite) TestDeleteSecurityGroupRule(c *gc.C) { 679 group1 := neutron.SecurityGroupV2{Id: "1", Name: "src", TenantId: s.service.TenantId} 680 group2 := neutron.SecurityGroupV2{Id: "2", Name: "tgt", TenantId: s.service.TenantId} 681 err := s.service.addSecurityGroup(group1) 682 c.Assert(err, gc.IsNil) 683 defer s.service.removeSecurityGroup(group1.Id) 684 err = s.service.addSecurityGroup(group2) 685 c.Assert(err, gc.IsNil) 686 defer s.service.removeSecurityGroup(group2.Id) 687 riGroup := neutron.RuleInfoV2{ 688 ParentGroupId: group2.Id, 689 Direction: "egress", 690 } 691 rule := neutron.SecurityGroupRuleV2{ 692 Id: "1", 693 ParentGroupId: group2.Id, 694 Direction: "egress", 695 } 696 err = s.service.addSecurityGroupRule(rule.Id, riGroup) 697 c.Assert(err, gc.IsNil) 698 url := fmt.Sprintf("%s/%s", neutron.ApiSecurityGroupRulesV2, "1") 699 resp, err := s.authRequest("DELETE", url, nil, nil) 700 c.Assert(err, gc.IsNil) 701 c.Assert(resp.StatusCode, gc.Equals, http.StatusNoContent) 702 ok := s.service.hasSecurityGroupRule(group2.Id, rule.Id) 703 c.Assert(ok, gc.Equals, false) 704 } 705 706 func (s *NeutronHTTPSuite) TestPostFloatingIPV2(c *gc.C) { 707 // network 998 has External = true 708 fip := neutron.FloatingIPV2{Id: "1", IP: "10.0.0.1", FloatingNetworkId: "998"} 709 c.Assert(s.service.allFloatingIPs(nil), gc.HasLen, 0) 710 var req struct { 711 IP neutron.FloatingIPV2 `json:"floatingip"` 712 } 713 req.IP = fip 714 resp, err := s.jsonRequest("POST", neutron.ApiFloatingIPsV2, req, nil) 715 c.Assert(err, gc.IsNil) 716 c.Assert(resp.StatusCode, gc.Equals, http.StatusCreated) 717 var expected struct { 718 IP neutron.FloatingIPV2 `json:"floatingip"` 719 } 720 assertJSON(c, resp, &expected) 721 c.Assert(expected.IP, gc.DeepEquals, fip) 722 err = s.service.removeFloatingIP(fip.Id) 723 c.Assert(err, gc.IsNil) 724 // network 999 has External = false 725 req.IP.FloatingNetworkId = "999" 726 resp, err = s.jsonRequest("POST", neutron.ApiFloatingIPsV2, req, nil) 727 c.Assert(resp.StatusCode, gc.Equals, http.StatusNotFound) 728 } 729 730 func (s *NeutronHTTPSuite) TestGetFloatingIPs(c *gc.C) { 731 c.Assert(s.service.allFloatingIPs(nil), gc.HasLen, 0) 732 var expected struct { 733 IPs []neutron.FloatingIPV2 `json:"floatingips"` 734 } 735 resp, err := s.authRequest("GET", neutron.ApiFloatingIPsV2, nil, nil) 736 c.Assert(err, gc.IsNil) 737 c.Assert(resp.StatusCode, gc.Equals, http.StatusOK) 738 assertJSON(c, resp, &expected) 739 c.Assert(expected.IPs, gc.HasLen, 0) 740 fips := []neutron.FloatingIPV2{ 741 {Id: "1", IP: "1.2.3.4"}, 742 {Id: "2", IP: "4.3.2.1"}, 743 } 744 for _, fip := range fips { 745 err := s.service.addFloatingIP(fip) 746 defer s.service.removeFloatingIP(fip.Id) 747 c.Assert(err, gc.IsNil) 748 } 749 resp, err = s.authRequest("GET", neutron.ApiFloatingIPsV2, nil, nil) 750 c.Assert(err, gc.IsNil) 751 c.Assert(resp.StatusCode, gc.Equals, http.StatusOK) 752 assertJSON(c, resp, &expected) 753 if expected.IPs[0].Id != fips[0].Id { 754 expected.IPs[0], expected.IPs[1] = expected.IPs[1], expected.IPs[0] 755 } 756 c.Assert(expected.IPs, gc.DeepEquals, fips) 757 var expectedIP struct { 758 IP neutron.FloatingIPV2 `json:"floatingip"` 759 } 760 url := fmt.Sprintf("%s/%s", neutron.ApiFloatingIPsV2, "1") 761 resp, err = s.authRequest("GET", url, nil, nil) 762 c.Assert(err, gc.IsNil) 763 c.Assert(resp.StatusCode, gc.Equals, http.StatusOK) 764 assertJSON(c, resp, &expectedIP) 765 c.Assert(expectedIP.IP, gc.DeepEquals, fips[0]) 766 } 767 768 func (s *NeutronHTTPSuite) TestDeleteFloatingIP(c *gc.C) { 769 fip := neutron.FloatingIPV2{Id: "1", IP: "10.0.0.1"} 770 err := s.service.addFloatingIP(fip) 771 c.Assert(err, gc.IsNil) 772 defer s.service.removeFloatingIP(fip.Id) 773 url := fmt.Sprintf("%s/%s", neutron.ApiFloatingIPsV2, "1") 774 resp, err := s.authRequest("DELETE", url, nil, nil) 775 c.Assert(err, gc.IsNil) 776 c.Assert(resp.StatusCode, gc.Equals, http.StatusNoContent) 777 _, err = s.service.floatingIP(fip.Id) 778 c.Assert(err, gc.NotNil) 779 } 780 781 func (s *NeutronHTTPSuite) TestGetNetworks(c *gc.C) { 782 // There are always 4 networks 783 networks, err := s.service.allNetworks(nil) 784 c.Assert(err, gc.IsNil) 785 c.Assert(networks, gc.HasLen, 5) 786 var expected struct { 787 Networks []neutron.NetworkV2 `json:"networks"` 788 } 789 resp, err := s.authRequest("GET", neutron.ApiNetworksV2, nil, nil) 790 c.Assert(err, gc.IsNil) 791 c.Assert(resp.StatusCode, gc.Equals, http.StatusOK) 792 assertJSON(c, resp, &expected) 793 c.Assert(expected.Networks, gc.HasLen, len(networks)) 794 var expectedNetwork struct { 795 Network neutron.NetworkV2 `json:"network"` 796 } 797 url := fmt.Sprintf("%s/%s", neutron.ApiNetworksV2, networks[0].Id) 798 resp, err = s.authRequest("GET", url, nil, nil) 799 c.Assert(err, gc.IsNil) 800 c.Assert(resp.StatusCode, gc.Equals, http.StatusOK) 801 assertJSON(c, resp, &expectedNetwork) 802 c.Assert(expectedNetwork.Network, gc.DeepEquals, networks[0]) 803 } 804 805 func (s *NeutronHTTPSuite) TestGetSubnets(c *gc.C) { 806 // There are always 3 subnets 807 subnets := s.service.allSubnets() 808 c.Assert(subnets, gc.HasLen, 3) 809 var expected struct { 810 Subnets []neutron.SubnetV2 `json:"subnets"` 811 } 812 resp, err := s.authRequest("GET", neutron.ApiSubnetsV2, nil, nil) 813 c.Assert(err, gc.IsNil) 814 c.Assert(resp.StatusCode, gc.Equals, http.StatusOK) 815 assertJSON(c, resp, &expected) 816 c.Assert(expected.Subnets, gc.HasLen, 3) 817 var expectedSubnet struct { 818 Subnet neutron.SubnetV2 `json:"subnet"` 819 } 820 url := fmt.Sprintf("%s/%s", neutron.ApiSubnetsV2, subnets[0].Id) 821 resp, err = s.authRequest("GET", url, nil, nil) 822 c.Assert(err, gc.IsNil) 823 c.Assert(resp.StatusCode, gc.Equals, http.StatusOK) 824 assertJSON(c, resp, &expectedSubnet) 825 c.Assert(expectedSubnet.Subnet, gc.DeepEquals, subnets[0]) 826 } 827 828 func (s *NeutronHTTPSuite) TestGetPorts(c *gc.C) { 829 // There is always a default port. 830 ports := s.service.allPorts() 831 c.Assert(ports, gc.HasLen, 0) 832 var expected struct { 833 Ports []neutron.PortV2 `json:"ports"` 834 } 835 resp, err := s.authRequest("GET", neutron.ApiPortsV2, nil, nil) 836 c.Assert(err, gc.IsNil) 837 c.Assert(resp.StatusCode, gc.Equals, http.StatusOK) 838 assertJSON(c, resp, &expected) 839 c.Assert(expected.Ports, gc.HasLen, 0) 840 841 ports = []neutron.PortV2{ 842 { 843 Id: "1", 844 Name: "group 1", 845 TenantId: s.service.TenantId, 846 NetworkId: "a87cc70a-3e15-4acf-8205-9b711a3531b7", 847 Tags: []string{"tag0", "tag1"}, 848 }, 849 { 850 Id: "2", 851 Name: "group 2", 852 TenantId: s.service.TenantId, 853 NetworkId: "a87cc70a-3e15-4acf-8205-9b711a3531xx", 854 Tags: []string{"tag3", "tag2"}, 855 }, 856 } 857 858 for _, group := range ports { 859 err := s.service.addPort(group) 860 c.Assert(err, gc.IsNil) 861 defer s.service.removePort(group.Id) 862 } 863 864 resp, err = s.authRequest("GET", neutron.ApiPortsV2, nil, nil) 865 c.Assert(err, gc.IsNil) 866 c.Assert(resp.StatusCode, gc.Equals, http.StatusOK) 867 assertJSON(c, resp, &expected) 868 c.Assert(expected.Ports, gc.HasLen, len(ports)) 869 870 checkPortsInList(c, ports, expected.Ports) 871 872 var expectedPort struct { 873 Port neutron.PortV2 `json:"port"` 874 } 875 url := fmt.Sprintf("%s/%s", neutron.ApiPortsV2, "1") 876 resp, err = s.authRequest("GET", url, nil, nil) 877 c.Assert(err, gc.IsNil) 878 c.Assert(resp.StatusCode, gc.Equals, http.StatusOK) 879 assertJSON(c, resp, &expectedPort) 880 c.Assert(expectedPort.Port, gc.DeepEquals, ports[0]) 881 } 882 883 func (s *NeutronHTTPSuite) TestAddPort(c *gc.C) { 884 port := neutron.PortV2{ 885 Id: "1", 886 Name: "port 1", 887 Description: "desc", 888 TenantId: s.service.TenantId, 889 NetworkId: "a87cc70a-3e15-4acf-8205-9b711a3531b7", 890 Tags: []string{"tag0", "tag1"}, 891 } 892 _, err := s.service.port(port.Id) 893 c.Assert(err, gc.NotNil) 894 895 var req struct { 896 Port struct { 897 Name string `json:"name"` 898 Description string `json:"description"` 899 NetworkId string `json:"network_id"` 900 Tags []string `json:"tags"` 901 } `json:"port"` 902 } 903 req.Port.Name = port.Name 904 req.Port.Description = port.Description 905 req.Port.NetworkId = port.NetworkId 906 req.Port.Tags = port.Tags 907 908 var expected struct { 909 Port neutron.PortV2 `json:"port"` 910 } 911 resp, err := s.jsonRequest("POST", neutron.ApiPortsV2, req, nil) 912 c.Assert(err, gc.IsNil) 913 c.Assert(resp.StatusCode, gc.Equals, http.StatusCreated) 914 assertJSON(c, resp, &expected) 915 c.Assert(expected.Port, gc.DeepEquals, port) 916 917 err = s.service.removePort(port.Id) 918 c.Assert(err, gc.IsNil) 919 } 920 921 func (s *NeutronHTTPSuite) TestDeletePort(c *gc.C) { 922 port := neutron.PortV2{Id: "1", Name: "port 1", TenantId: s.service.TenantId} 923 _, err := s.service.port(port.Id) 924 c.Assert(err, gc.NotNil) 925 926 err = s.service.addPort(port) 927 c.Assert(err, gc.IsNil) 928 defer s.service.removePort(port.Id) 929 930 url := fmt.Sprintf("%s/%s", neutron.ApiPortsV2, "1") 931 resp, err := s.authRequest("DELETE", url, nil, nil) 932 c.Assert(err, gc.IsNil) 933 c.Assert(resp.StatusCode, gc.Equals, http.StatusNoContent) 934 935 _, err = s.service.port(port.Id) 936 c.Assert(err, gc.NotNil) 937 } 938 939 func (s *NeutronHTTPSSuite) SetUpSuite(c *gc.C) { 940 s.HTTPSuite.SetUpSuite(c) 941 identityDouble := identityservice.NewUserPass() 942 userInfo := identityDouble.AddUser("fred", "secret", "tenant", "default") 943 s.token = userInfo.Token 944 c.Assert(s.Server.URL[:8], gc.Equals, "https://") 945 s.service = New(s.Server.URL, versionPath, userInfo.TenantId, region, identityDouble, nil) 946 s.service.AddNeutronModel(neutronmodel.New()) 947 } 948 949 func (s *NeutronHTTPSSuite) TearDownSuite(c *gc.C) { 950 s.HTTPSuite.TearDownSuite(c) 951 } 952 953 func (s *NeutronHTTPSSuite) SetUpTest(c *gc.C) { 954 s.HTTPSuite.SetUpTest(c) 955 s.service.SetupHTTP(s.Mux) 956 } 957 958 func (s *NeutronHTTPSSuite) TearDownTest(c *gc.C) { 959 s.HTTPSuite.TearDownTest(c) 960 } 961 962 func (s *NeutronHTTPSSuite) TestHasHTTPSServiceURL(c *gc.C) { 963 endpoints := s.service.Endpoints() 964 c.Assert(endpoints[0].PublicURL[:8], gc.Equals, "https://") 965 }