gopkg.in/goose.v2@v2.0.1/testservices/novaservice/service.go (about) 1 // Nova double testing service - internal direct API implementation 2 3 package novaservice 4 5 import ( 6 "fmt" 7 "net/url" 8 "regexp" 9 "sort" 10 "strconv" 11 "strings" 12 13 "gopkg.in/goose.v2/errors" 14 "gopkg.in/goose.v2/nova" 15 "gopkg.in/goose.v2/testservices" 16 "gopkg.in/goose.v2/testservices/identityservice" 17 "gopkg.in/goose.v2/testservices/neutronmodel" 18 ) 19 20 var _ testservices.HttpService = (*Nova)(nil) 21 var _ identityservice.ServiceProvider = (*Nova)(nil) 22 23 // Nova implements a OpenStack Nova testing service and 24 // contains the service double's internal state. 25 type Nova struct { 26 testservices.ServiceInstance 27 neutronModel *neutronmodel.NeutronModel 28 flavors map[string]nova.FlavorDetail 29 servers map[string]nova.ServerDetail 30 groups map[string]nova.SecurityGroup 31 rules map[string]nova.SecurityGroupRule 32 floatingIPs map[string]nova.FloatingIP 33 networks map[string]nova.Network 34 serverGroups map[string][]string 35 serverIPs map[string][]string 36 availabilityZones map[string]nova.AvailabilityZone 37 serverIdToOSInterfaces map[string][]nova.OSInterface 38 serverIdToAttachedVolumes map[string][]nova.VolumeAttachment 39 nextServerId int 40 nextGroupId int 41 nextRuleId int 42 nextIPId int 43 nextAttachmentId int 44 nextOSInterfaceId int 45 useNeutronNetworking bool 46 noValidHostZone nova.AvailabilityZone 47 serverStatus string 48 } 49 50 func errorJSONEncode(err error) (int, string) { 51 serverError, ok := err.(*testservices.ServerError) 52 if !ok { 53 serverError = testservices.NewInternalServerError(err.Error()) 54 } 55 return serverError.Code(), serverError.AsJSON() 56 } 57 58 // endpoint returns either a versioned or non-versioned service 59 // endpoint URL from the given path. 60 func (n *Nova) endpointURL(version bool, path string) string { 61 ep := n.Scheme + "://" + n.Hostname 62 if version { 63 ep += n.VersionPath + "/" 64 } 65 ep += n.TenantId 66 if path != "" { 67 ep += "/" + strings.TrimLeft(path, "/") 68 } 69 return ep 70 } 71 72 func (n *Nova) Endpoints() []identityservice.Endpoint { 73 ep := identityservice.Endpoint{ 74 AdminURL: n.endpointURL(true, ""), 75 InternalURL: n.endpointURL(true, ""), 76 PublicURL: n.endpointURL(true, ""), 77 Region: n.Region, 78 } 79 return []identityservice.Endpoint{ep} 80 } 81 82 func (n *Nova) V3Endpoints() []identityservice.V3Endpoint { 83 url := n.endpointURL(true, "") 84 return identityservice.NewV3Endpoints(url, url, url, n.RegionID) 85 } 86 87 // New creates an instance of the Nova object, given the parameters. 88 func New(hostURL, versionPath, tenantId, region string, identityService, fallbackIdentity identityservice.IdentityService) *Nova { 89 URL, err := url.Parse(hostURL) 90 if err != nil { 91 panic(err) 92 } 93 hostname := URL.Host 94 if !strings.HasSuffix(hostname, "/") { 95 hostname += "/" 96 } 97 // Real openstack instances have flavours "out of the box". So we add some here. 98 defaultFlavors := []nova.FlavorDetail{ 99 {Id: "1", Name: "m1.tiny", RAM: 512, VCPUs: 1, Disk: 5}, 100 {Id: "2", Name: "m1.small", RAM: 2048, VCPUs: 1, Disk: 10}, 101 {Id: "3", Name: "m1.medium", RAM: 4096, VCPUs: 2, Disk: 15}, 102 } 103 // Real openstack instances have a default security group "out of the box". So we add it here. 104 defaultSecurityGroups := []nova.SecurityGroup{ 105 {Id: "999", Name: "default", Description: "default group"}, 106 } 107 novaService := &Nova{ 108 flavors: make(map[string]nova.FlavorDetail), 109 servers: make(map[string]nova.ServerDetail), 110 groups: make(map[string]nova.SecurityGroup), 111 rules: make(map[string]nova.SecurityGroupRule), 112 floatingIPs: make(map[string]nova.FloatingIP), 113 networks: make(map[string]nova.Network), 114 serverGroups: make(map[string][]string), 115 serverIPs: make(map[string][]string), 116 availabilityZones: make(map[string]nova.AvailabilityZone), 117 serverIdToOSInterfaces: make(map[string][]nova.OSInterface), 118 serverIdToAttachedVolumes: make(map[string][]nova.VolumeAttachment), 119 useNeutronNetworking: false, 120 ServiceInstance: testservices.ServiceInstance{ 121 IdentityService: identityService, 122 FallbackIdentityService: fallbackIdentity, 123 Scheme: URL.Scheme, 124 Hostname: hostname, 125 VersionPath: versionPath, 126 TenantId: tenantId, 127 Region: region, 128 }, 129 } 130 if identityService != nil { 131 identityService.RegisterServiceProvider("nova", "compute", novaService) 132 } 133 for i, flavor := range defaultFlavors { 134 novaService.buildFlavorLinks(&flavor) 135 defaultFlavors[i] = flavor 136 err := novaService.addFlavor(flavor) 137 if err != nil { 138 panic(err) 139 } 140 } 141 for _, group := range defaultSecurityGroups { 142 err := novaService.addSecurityGroup(group) 143 if err != nil { 144 panic(err) 145 } 146 } 147 // Add a sample default network 148 var id = "1" 149 novaService.networks[id] = nova.Network{ 150 Id: id, 151 Label: "net", 152 Cidr: "10.0.0.0/24", 153 } 154 return novaService 155 } 156 157 func (n *Nova) Stop() { 158 // noop 159 } 160 161 // AddNeutronModel setups up the test double to use Neutron networking 162 // which requires shared data between the nova and neutron test doubles. 163 func (n *Nova) AddNeutronModel(neutronModel *neutronmodel.NeutronModel) { 164 n.neutronModel = neutronModel 165 n.useNeutronNetworking = true 166 } 167 168 // SetAvailabilityZones sets the availability zones for setting 169 // availability zones. 170 // 171 // Note: this is implemented as a public method rather than as 172 // an HTTP API for two reasons: availability zones are created 173 // indirectly via "host aggregates", which are a cloud-provider 174 // concept that we have not implemented, and because we want to 175 // be able to synthesize zone state changes. 176 func (n *Nova) SetAvailabilityZones(zones ...nova.AvailabilityZone) { 177 n.availabilityZones = make(map[string]nova.AvailabilityZone) 178 for _, z := range zones { 179 n.availabilityZones[z.Name] = z 180 } 181 } 182 183 // SetAZForNoValidHosts sets an availability zone to cause a 184 // No valid host failures. 185 // 186 // Note: this is implemented as a public method rather than as 187 // an HTTP API for the same reasons as SetAvailabilityZones, as 188 // well as defining an availability zone to cause 'No valid host' 189 // failures. 190 func (n *Nova) SetAZForNoValidHosts(zone nova.AvailabilityZone) { 191 n.noValidHostZone = zone 192 // ensure the zone for failure, is on the list of 193 // possible zones 194 if _, ok := n.availabilityZones[zone.Name]; !ok { 195 n.availabilityZones[zone.Name] = zone 196 } 197 } 198 199 // SetServerStatus sets the ServerDetail.Status to a new 200 // value. 201 // 202 // Note: this is implemented as a public method rather than as 203 // an HTTP API to allow for changing the status inside of the 204 // returned data structure, not accomplished by the testservice 205 // hooks 206 func (n *Nova) SetServerStatus(status string) { 207 n.serverStatus = status 208 } 209 210 // buildFlavorLinks populates the Links field of the passed 211 // FlavorDetail as needed by OpenStack HTTP API. Call this 212 // before addFlavor(). 213 func (n *Nova) buildFlavorLinks(flavor *nova.FlavorDetail) { 214 url := "/flavors/" + flavor.Id 215 flavor.Links = []nova.Link{ 216 {Href: n.endpointURL(true, url), Rel: "self"}, 217 {Href: n.endpointURL(false, url), Rel: "bookmark"}, 218 } 219 } 220 221 // addFlavor creates a new flavor. 222 func (n *Nova) addFlavor(flavor nova.FlavorDetail) error { 223 if err := n.ProcessFunctionHook(n, flavor); err != nil { 224 return err 225 } 226 if _, err := n.flavor(flavor.Id); err == nil { 227 return testservices.NewAddFlavorError(flavor.Id) 228 } 229 n.flavors[flavor.Id] = flavor 230 return nil 231 } 232 233 // flavor retrieves an existing flavor by ID. 234 func (n *Nova) flavor(flavorId string) (*nova.FlavorDetail, error) { 235 if err := n.ProcessFunctionHook(n, flavorId); err != nil { 236 return nil, err 237 } 238 flavor, ok := n.flavors[flavorId] 239 if !ok { 240 return nil, testservices.NewNoSuchFlavorError(flavorId) 241 } 242 return &flavor, nil 243 } 244 245 // flavorAsEntity returns the stored FlavorDetail as Entity. 246 func (n *Nova) flavorAsEntity(flavorId string) (*nova.Entity, error) { 247 if err := n.ProcessFunctionHook(n, flavorId); err != nil { 248 return nil, err 249 } 250 flavor, err := n.flavor(flavorId) 251 if err != nil { 252 return nil, err 253 } 254 return &nova.Entity{ 255 Id: flavor.Id, 256 Name: flavor.Name, 257 Links: flavor.Links, 258 }, nil 259 } 260 261 // allFlavors returns a list of all existing flavors. 262 func (n *Nova) allFlavors() []nova.FlavorDetail { 263 var flavors []nova.FlavorDetail 264 for _, flavor := range n.flavors { 265 flavors = append(flavors, flavor) 266 } 267 return flavors 268 } 269 270 // allFlavorsAsEntities returns all flavors as Entity structs. 271 func (n *Nova) allFlavorsAsEntities() []nova.Entity { 272 var entities []nova.Entity 273 for _, flavor := range n.flavors { 274 entities = append(entities, nova.Entity{ 275 Id: flavor.Id, 276 Name: flavor.Name, 277 Links: flavor.Links, 278 }) 279 } 280 return entities 281 } 282 283 // removeFlavor deletes an existing flavor. 284 func (n *Nova) removeFlavor(flavorId string) error { 285 if err := n.ProcessFunctionHook(n, flavorId); err != nil { 286 return err 287 } 288 if _, err := n.flavor(flavorId); err != nil { 289 return err 290 } 291 delete(n.flavors, flavorId) 292 return nil 293 } 294 295 // buildServerLinks populates the Links field of the passed 296 // ServerDetail as needed by OpenStack HTTP API. Call this 297 // before addServer(). 298 func (n *Nova) buildServerLinks(server *nova.ServerDetail) { 299 url := "/servers/" + server.Id 300 server.Links = []nova.Link{ 301 {Href: n.endpointURL(true, url), Rel: "self"}, 302 {Href: n.endpointURL(false, url), Rel: "bookmark"}, 303 } 304 } 305 306 // addServer creates a new server. 307 func (n *Nova) addServer(server nova.ServerDetail) error { 308 if err := n.ProcessFunctionHook(n, &server); err != nil { 309 return err 310 } 311 if _, err := n.server(server.Id); err == nil { 312 return testservices.NewServerAlreadyExistsError(server.Id) 313 } 314 n.servers[server.Id] = server 315 return nil 316 } 317 318 // updateServerName creates a new server. 319 func (n *Nova) updateServerName(serverId, name string) error { 320 if err := n.ProcessFunctionHook(n, serverId); err != nil { 321 return err 322 } 323 server, err := n.server(serverId) 324 if err != nil { 325 return testservices.NewServerByIDNotFoundError(serverId) 326 } 327 server.Name = name 328 n.servers[serverId] = *server 329 return nil 330 } 331 332 // server retrieves an existing server by ID. 333 func (n *Nova) server(serverId string) (*nova.ServerDetail, error) { 334 if err := n.ProcessFunctionHook(n, serverId); err != nil { 335 return nil, err 336 } 337 server, ok := n.servers[serverId] 338 if !ok { 339 return nil, testservices.NewServerByIDNotFoundError(serverId) 340 } 341 groups := n.allServerSecurityGroups(serverId) 342 if len(groups) > 0 { 343 groupNames := make([]nova.SecurityGroupName, len(groups)) 344 for i, group := range groups { 345 groupNames[i] = nova.SecurityGroupName{Name: group.Name} 346 } 347 server.Groups = &groupNames 348 } else { 349 server.Groups = nil 350 } 351 if server.AvailabilityZone != "" && server.AvailabilityZone == n.noValidHostZone.Name { 352 server.Status = nova.StatusError 353 server.Fault = &nova.ServerFault{ 354 Code: 500, 355 Message: "No valid host was found. There are not enough hosts available.", 356 } 357 } else if n.serverStatus != "" { 358 server.Status = n.serverStatus 359 } else { 360 server.Status = nova.StatusActive 361 } 362 return &server, nil 363 } 364 365 // serverByName retrieves the first existing server with the given name. 366 func (n *Nova) serverByName(name string) (*nova.ServerDetail, error) { 367 if err := n.ProcessFunctionHook(n, name); err != nil { 368 return nil, err 369 } 370 for _, server := range n.servers { 371 if server.Name == name { 372 return &server, nil 373 } 374 } 375 return nil, testservices.NewServerByNameNotFoundError(name) 376 } 377 378 // serverAsEntity returns the stored ServerDetail as Entity. 379 func (n *Nova) serverAsEntity(serverId string) (*nova.Entity, error) { 380 if err := n.ProcessFunctionHook(n, serverId); err != nil { 381 return nil, err 382 } 383 server, err := n.server(serverId) 384 if err != nil { 385 return nil, err 386 } 387 return &nova.Entity{ 388 Id: server.Id, 389 UUID: server.UUID, 390 Name: server.Name, 391 Links: server.Links, 392 }, nil 393 } 394 395 // filter is used internally by matchServers. 396 type filter map[string]string 397 398 // matchServers returns a list of matching servers, after applying the 399 // given filter. Each separate filter is combined with a logical AND. 400 // Each filter can have only one value. A nil filter matches all servers. 401 // 402 // This is tested to match OpenStack behavior. Regular expression 403 // matching is supported for FilterServer only, and the supported 404 // syntax is limited to whatever DB backend is used (see SQL 405 // REGEXP/RLIKE). 406 // 407 // Example: 408 // 409 // f := filter{ 410 // nova.FilterStatus: nova.StatusActive, 411 // nova.FilterServer: `foo.*`, 412 // } 413 // 414 // This will match all servers with status "ACTIVE", and names starting 415 // with "foo". 416 func (n *Nova) matchServers(f filter) ([]nova.ServerDetail, error) { 417 if err := n.ProcessFunctionHook(n, f); err != nil { 418 return nil, err 419 } 420 var servers []nova.ServerDetail 421 for _, server := range n.servers { 422 servers = append(servers, server) 423 } 424 if len(f) == 0 { 425 return servers, nil // empty filter matches everything 426 } 427 if status := f[nova.FilterStatus]; status != "" { 428 matched := []nova.ServerDetail{} 429 for _, server := range servers { 430 if server.Status == status { 431 matched = append(matched, server) 432 } 433 } 434 if len(matched) == 0 { 435 // no match, so no need to look further 436 return nil, nil 437 } 438 servers = matched 439 } 440 if nameRex := f[nova.FilterServer]; nameRex != "" { 441 matched := []nova.ServerDetail{} 442 rex, err := regexp.Compile(nameRex) 443 if err != nil { 444 return nil, err 445 } 446 for _, server := range servers { 447 if rex.MatchString(server.Name) { 448 matched = append(matched, server) 449 } 450 } 451 if len(matched) == 0 { 452 // no match, here so ignore other results 453 return nil, nil 454 } 455 servers = matched 456 } 457 return servers, nil 458 // TODO(dimitern) - 2013-02-11 bug=1121690 459 // implement FilterFlavor, FilterImage, FilterMarker, FilterLimit and FilterChangesSince 460 } 461 462 // allServers returns a list of all existing servers. 463 // Filtering is supported, see filter type for more info. 464 func (n *Nova) allServers(f filter) ([]nova.ServerDetail, error) { 465 return n.matchServers(f) 466 } 467 468 // allServersAsEntities returns all servers as Entity structs. 469 // Filtering is supported, see filter type for more info. 470 func (n *Nova) allServersAsEntities(f filter) ([]nova.Entity, error) { 471 var entities []nova.Entity 472 servers, err := n.matchServers(f) 473 if err != nil { 474 return nil, err 475 } 476 for _, server := range servers { 477 entities = append(entities, nova.Entity{ 478 Id: server.Id, 479 UUID: server.UUID, 480 Name: server.Name, 481 Links: server.Links, 482 }) 483 } 484 return entities, nil 485 } 486 487 // removeServer deletes an existing server. 488 func (n *Nova) removeServer(serverId string) error { 489 if err := n.ProcessFunctionHook(n, serverId); err != nil { 490 return err 491 } 492 if _, err := n.server(serverId); err != nil { 493 return err 494 } 495 delete(n.servers, serverId) 496 return nil 497 } 498 499 func (n *Nova) updateSecurityGroup(group nova.SecurityGroup) error { 500 if err := n.ProcessFunctionHook(n, group); err != nil { 501 return err 502 } 503 if n.useNeutronNetworking { 504 return n.neutronModel.UpdateNovaSecurityGroup(group) 505 } 506 existingGroup, err := n.securityGroup(group.Id) 507 if err != nil { 508 return testservices.NewSecurityGroupByIDNotFoundError(group.Id) 509 } 510 existingGroup.Name = group.Name 511 existingGroup.Description = group.Description 512 n.groups[group.Id] = *existingGroup 513 return nil 514 } 515 516 // addSecurityGroup creates a new security group. 517 func (n *Nova) addSecurityGroup(group nova.SecurityGroup) error { 518 if err := n.ProcessFunctionHook(n, group); err != nil { 519 return err 520 } 521 if n.useNeutronNetworking { 522 return n.neutronModel.AddNovaSecurityGroup(group) 523 } 524 if _, err := n.securityGroup(group.Id); err == nil { 525 return testservices.NewSecurityGroupAlreadyExistsError(group.Id) 526 } 527 group.TenantId = n.TenantId 528 if group.Rules == nil { 529 group.Rules = []nova.SecurityGroupRule{} 530 } 531 n.groups[group.Id] = group 532 return nil 533 } 534 535 // securityGroup retrieves an existing group by ID. 536 func (n *Nova) securityGroup(groupId string) (*nova.SecurityGroup, error) { 537 if err := n.ProcessFunctionHook(n, groupId); err != nil { 538 return nil, err 539 } 540 if n.useNeutronNetworking { 541 return n.neutronModel.NovaSecurityGroup(groupId) 542 } 543 group, ok := n.groups[groupId] 544 if !ok { 545 return nil, testservices.NewSecurityGroupByIDNotFoundError(groupId) 546 } 547 return &group, nil 548 } 549 550 // securityGroupByName retrieves an existing named group. 551 func (n *Nova) securityGroupByName(groupName string) (*nova.SecurityGroup, error) { 552 if err := n.ProcessFunctionHook(n, groupName); err != nil { 553 return nil, err 554 } 555 if n.useNeutronNetworking { 556 return n.neutronModel.NovaSecurityGroupByName(groupName) 557 } 558 for _, group := range n.groups { 559 if group.Name == groupName { 560 return &group, nil 561 } 562 } 563 return nil, testservices.NewSecurityGroupByNameNotFoundError(groupName) 564 } 565 566 // allSecurityGroups returns a list of all existing groups. 567 func (n *Nova) allSecurityGroups() []nova.SecurityGroup { 568 var groups []nova.SecurityGroup 569 if n.useNeutronNetworking { 570 return n.neutronModel.AllNovaSecurityGroups() 571 } 572 for _, group := range n.groups { 573 groups = append(groups, group) 574 } 575 return groups 576 } 577 578 // removeSecurityGroup deletes an existing group. 579 func (n *Nova) removeSecurityGroup(groupId string) error { 580 if err := n.ProcessFunctionHook(n, groupId); err != nil { 581 return err 582 } 583 if n.useNeutronNetworking { 584 return n.neutronModel.RemoveSecurityGroup(groupId) 585 } 586 if _, err := n.securityGroup(groupId); err != nil { 587 return err 588 } 589 delete(n.groups, groupId) 590 return nil 591 } 592 593 // addSecurityGroupRule creates a new rule in an existing group. 594 // This can be either an ingress or a group rule (see the notes 595 // about nova.RuleInfo). 596 func (n *Nova) addSecurityGroupRule(ruleId string, rule nova.RuleInfo) error { 597 if err := n.ProcessFunctionHook(n, ruleId, rule); err != nil { 598 return err 599 } 600 if _, err := n.securityGroupRule(ruleId); err == nil { 601 return testservices.NewSecurityGroupRuleAlreadyExistsError(ruleId) 602 } 603 group, err := n.securityGroup(rule.ParentGroupId) 604 if err != nil { 605 return err 606 } 607 for _, ru := range group.Rules { 608 if ru.Id == ruleId { 609 return testservices.NewCannotAddTwiceRuleToGroupError(ru.Id, group.Id) 610 } 611 } 612 var zeroSecurityGroupRef nova.SecurityGroupRef 613 newrule := nova.SecurityGroupRule{ 614 ParentGroupId: rule.ParentGroupId, 615 Id: ruleId, 616 Group: zeroSecurityGroupRef, 617 } 618 if rule.GroupId != nil { 619 sourceGroup, err := n.securityGroup(*rule.GroupId) 620 if err != nil { 621 return testservices.NewUnknownSecurityGroupError(*rule.GroupId) 622 } 623 newrule.Group = nova.SecurityGroupRef{ 624 TenantId: sourceGroup.TenantId, 625 Name: sourceGroup.Name, 626 } 627 } else if rule.Cidr == "" { 628 // http://pad.lv/1226996 629 // It seems that if you don't supply Cidr or GroupId then 630 // Openstack treats the Cidr as 0.0.0.0/0 631 // However, since that is not clearly specified we just panic() 632 // because we don't want to rely on that behavior 633 panic(fmt.Sprintf("Neither Cidr nor GroupId are set for this SecurityGroup Rule: %v", rule)) 634 } 635 if rule.FromPort != 0 { 636 newrule.FromPort = &rule.FromPort 637 } 638 if rule.ToPort != 0 { 639 newrule.ToPort = &rule.ToPort 640 } 641 if rule.IPProtocol != "" { 642 newrule.IPProtocol = &rule.IPProtocol 643 } 644 if rule.Cidr != "" { 645 newrule.IPRange = make(map[string]string) 646 newrule.IPRange["cidr"] = rule.Cidr 647 } 648 649 group.Rules = append(group.Rules, newrule) 650 n.groups[group.Id] = *group 651 n.rules[newrule.Id] = newrule 652 return nil 653 } 654 655 // hasSecurityGroupRule returns whether the given group contains the given rule, 656 // or (when groupId="-1") whether the given rule exists. 657 func (n *Nova) hasSecurityGroupRule(groupId, ruleId string) bool { 658 rule, ok := n.rules[ruleId] 659 _, err := n.securityGroup(groupId) 660 return ok && (groupId == "-1" || (err == nil && rule.ParentGroupId == groupId)) 661 } 662 663 // securityGroupRule retrieves an existing rule by ID. 664 func (n *Nova) securityGroupRule(ruleId string) (*nova.SecurityGroupRule, error) { 665 if err := n.ProcessFunctionHook(n, ruleId); err != nil { 666 return nil, err 667 } 668 rule, ok := n.rules[ruleId] 669 if !ok { 670 return nil, testservices.NewSecurityGroupRuleNotFoundError(ruleId) 671 } 672 return &rule, nil 673 } 674 675 // removeSecurityGroupRule deletes an existing rule from its group. 676 func (n *Nova) removeSecurityGroupRule(ruleId string) error { 677 if err := n.ProcessFunctionHook(n, ruleId); err != nil { 678 return err 679 } 680 rule, err := n.securityGroupRule(ruleId) 681 if err != nil { 682 return err 683 } 684 if group, err := n.securityGroup(rule.ParentGroupId); err == nil { 685 idx := -1 686 for ri, ru := range group.Rules { 687 if ru.Id == ruleId { 688 idx = ri 689 break 690 } 691 } 692 if idx != -1 { 693 group.Rules = append(group.Rules[:idx], group.Rules[idx+1:]...) 694 n.groups[group.Id] = *group 695 } 696 // Silently ignore missing rules... 697 } 698 // ...or groups 699 delete(n.rules, ruleId) 700 return nil 701 } 702 703 // addServerSecurityGroup attaches an existing server to a group. 704 func (n *Nova) addServerSecurityGroup(serverId string, groupId string) error { 705 if err := n.ProcessFunctionHook(n, serverId, groupId); err != nil { 706 return err 707 } 708 if n.useNeutronNetworking { 709 if _, err := n.neutronModel.NovaSecurityGroup(groupId); err != nil { 710 return err 711 } 712 } else { 713 if _, err := n.securityGroup(groupId); err != nil { 714 return err 715 } 716 } 717 if _, err := n.server(serverId); err != nil { 718 return err 719 } 720 groups, ok := n.serverGroups[serverId] 721 if ok { 722 for _, gid := range groups { 723 if gid == groupId { 724 return testservices.NewServerBelongsToGroupError(serverId, groupId) 725 } 726 } 727 } 728 groups = append(groups, groupId) 729 n.serverGroups[serverId] = groups 730 return nil 731 } 732 733 // hasServerSecurityGroup returns whether the given server belongs to the group. 734 func (n *Nova) hasServerSecurityGroup(serverId string, groupId string) bool { 735 if n.useNeutronNetworking { 736 if _, err := n.neutronModel.NovaSecurityGroup(groupId); err != nil { 737 return false 738 } 739 } else { 740 if _, err := n.securityGroup(groupId); err != nil { 741 return false 742 } 743 } 744 if _, err := n.server(serverId); err != nil { 745 return false 746 } 747 groups, ok := n.serverGroups[serverId] 748 if !ok { 749 return false 750 } 751 for _, gid := range groups { 752 if gid == groupId { 753 return true 754 } 755 } 756 return false 757 } 758 759 // allServerSecurityGroups returns all security groups attached to the 760 // given server. 761 func (n *Nova) allServerSecurityGroups(serverId string) []nova.SecurityGroup { 762 var groups []nova.SecurityGroup 763 for _, gid := range n.serverGroups[serverId] { 764 group, err := n.securityGroup(gid) 765 if err != nil { 766 return nil 767 } 768 groups = append(groups, *group) 769 } 770 return groups 771 } 772 773 // removeServerSecurityGroup detaches an existing server from a group. 774 func (n *Nova) removeServerSecurityGroup(serverId string, groupId string) error { 775 if err := n.ProcessFunctionHook(n, serverId, groupId); err != nil { 776 return err 777 } 778 if n.useNeutronNetworking { 779 if _, err := n.neutronModel.NovaSecurityGroup(groupId); err != nil { 780 return err 781 } 782 } else { 783 if _, err := n.securityGroup(groupId); err != nil { 784 return err 785 } 786 } 787 if _, err := n.server(serverId); err != nil { 788 return err 789 } 790 groups, ok := n.serverGroups[serverId] 791 if !ok { 792 return testservices.NewServerDoesNotBelongToGroupsError(serverId) 793 } 794 idx := -1 795 for gi, gid := range groups { 796 if gid == groupId { 797 idx = gi 798 break 799 } 800 } 801 if idx == -1 { 802 return testservices.NewServerDoesNotBelongToGroupError(serverId, groupId) 803 } 804 groups = append(groups[:idx], groups[idx+1:]...) 805 n.serverGroups[serverId] = groups 806 return nil 807 } 808 809 // addFloatingIP creates a new floating IP address in the pool. 810 func (n *Nova) addFloatingIP(ip nova.FloatingIP) error { 811 if err := n.ProcessFunctionHook(n, ip); err != nil { 812 return err 813 } 814 if n.useNeutronNetworking { 815 return n.neutronModel.AddNovaFloatingIP(ip) 816 } 817 if _, err := n.floatingIP(ip.Id); err == nil { 818 return testservices.NewFloatingIPExistsError(ip.Id) 819 } 820 n.floatingIPs[ip.Id] = ip 821 return nil 822 } 823 824 // hasFloatingIP returns whether the given floating IP address exists. 825 func (n *Nova) hasFloatingIP(address string) bool { 826 if n.useNeutronNetworking { 827 return n.neutronModel.HasFloatingIP(address) 828 } 829 if len(n.floatingIPs) == 0 { 830 return false 831 } 832 for _, fip := range n.floatingIPs { 833 if fip.IP == address { 834 return true 835 } 836 } 837 return false 838 } 839 840 // floatingIP retrieves the floating IP by ID. 841 func (n *Nova) floatingIP(ipId string) (*nova.FloatingIP, error) { 842 if err := n.ProcessFunctionHook(n, ipId); err != nil { 843 return nil, err 844 } 845 if n.useNeutronNetworking { 846 return n.neutronModel.NovaFloatingIP(ipId) 847 } 848 ip, ok := n.floatingIPs[ipId] 849 if !ok { 850 return nil, testservices.NewFloatingIPNotFoundError(ipId) 851 } 852 return &ip, nil 853 } 854 855 // floatingIPByAddr retrieves the floating IP by address. 856 func (n *Nova) floatingIPByAddr(address string) (*nova.FloatingIP, error) { 857 if err := n.ProcessFunctionHook(n, address); err != nil { 858 return nil, err 859 } 860 if n.useNeutronNetworking { 861 return n.neutronModel.NovaFloatingIPByAddr(address) 862 } 863 for _, fip := range n.floatingIPs { 864 if fip.IP == address { 865 return &fip, nil 866 } 867 } 868 return nil, testservices.NewFloatingIPNotFoundError(address) 869 } 870 871 // allFloatingIPs returns a list of all created floating IPs. 872 func (n *Nova) allFloatingIPs() []nova.FloatingIP { 873 if n.useNeutronNetworking { 874 return n.neutronModel.AllNovaFloatingIPs() 875 } 876 var fips []nova.FloatingIP 877 for _, fip := range n.floatingIPs { 878 fips = append(fips, fip) 879 } 880 return fips 881 } 882 883 // removeFloatingIP deletes an existing floating IP by ID. 884 func (n *Nova) removeFloatingIP(ipId string) error { 885 if err := n.ProcessFunctionHook(n, ipId); err != nil { 886 return err 887 } 888 if n.useNeutronNetworking { 889 return n.neutronModel.RemoveFloatingIP(ipId) 890 } 891 if _, err := n.floatingIP(ipId); err != nil { 892 return err 893 } 894 delete(n.floatingIPs, ipId) 895 return nil 896 } 897 898 // addServerFloatingIP attaches an existing floating IP to a server. 899 func (n *Nova) addServerFloatingIP(serverId string, ipId string) error { 900 if err := n.ProcessFunctionHook(n, serverId, ipId); err != nil { 901 return err 902 } 903 if _, err := n.server(serverId); err != nil { 904 return err 905 } 906 fixedIP := "4.3.2.1" // not important really, unused 907 var fip *nova.FloatingIP 908 var err error 909 if n.useNeutronNetworking { 910 fip, err = n.neutronModel.NovaFloatingIP(ipId) 911 if err != nil { 912 return err 913 } 914 fip.FixedIP = &fixedIP 915 if err := n.neutronModel.UpdateNovaFloatingIP(fip); err != nil { 916 return err 917 } 918 } else { 919 fip, err = n.floatingIP(ipId) 920 if err != nil { 921 return err 922 } else { 923 fip.FixedIP = &fixedIP 924 fip.InstanceId = &serverId 925 n.floatingIPs[ipId] = *fip 926 } 927 } 928 fips, ok := n.serverIPs[serverId] 929 if ok { 930 for _, fipId := range fips { 931 if fipId == ipId { 932 return testservices.NewServerHasFloatingIPError(serverId, ipId) 933 } 934 } 935 } 936 fips = append(fips, ipId) 937 n.serverIPs[serverId] = fips 938 if err := n.addFloatingIPToServerAddresses(serverId, fip.IP); err != nil { 939 return err 940 } 941 return nil 942 } 943 944 // addFloatingIPToServerAddresses adds a floating ip address to the servers list 945 // of Addresses to facilitate juju openstack provider tests. 946 func (n *Nova) addFloatingIPToServerAddresses(serverId, address string) error { 947 server, err := n.server(serverId) 948 if err != nil { 949 return err 950 } 951 newAddresses := server.Addresses["private"] 952 if strings.Contains(address, ":") { 953 newAddresses = append(newAddresses, nova.IPAddress{6, address, "floating"}) 954 } else { 955 newAddresses = append(newAddresses, nova.IPAddress{4, address, "floating"}) 956 } 957 server.Addresses["private"] = newAddresses 958 n.servers[serverId] = *server 959 return nil 960 } 961 962 // hasServerFloatingIP verifies the given floating IP belongs to a server. 963 func (n *Nova) hasServerFloatingIP(serverId, address string) bool { 964 if _, err := n.server(serverId); err != nil { 965 return false 966 } 967 var fip *nova.FloatingIP 968 var err error 969 if n.useNeutronNetworking { 970 fip, err = n.neutronModel.NovaFloatingIPByAddr(address) 971 } else { 972 fip, err = n.floatingIPByAddr(address) 973 } 974 if err != nil { 975 return false 976 } 977 fips, ok := n.serverIPs[serverId] 978 if !ok { 979 return false 980 } 981 for _, fipId := range fips { 982 if fipId == fip.Id { 983 return true 984 } 985 } 986 return false 987 } 988 989 // removeFloatingIPFromServerAddresses removes a floating ip address from the 990 // servers list of Addresses to facilitate juju openstack provider tests. 991 func (n *Nova) removeFloatingIPFromServerAddresses(serverId, address string) error { 992 server, err := n.server(serverId) 993 if err != nil { 994 return err 995 } 996 serverAddresses := []nova.IPAddress{} 997 for _, serverAddress := range server.Addresses["private"] { 998 if serverAddress.Address != address { 999 serverAddresses = append(serverAddresses, serverAddress) 1000 } 1001 } 1002 if len(serverAddresses) != 0 { 1003 server.Addresses["private"] = serverAddresses 1004 } else { 1005 server.Addresses["private"] = []nova.IPAddress{} 1006 } 1007 n.servers[serverId] = *server 1008 return nil 1009 } 1010 1011 // removeServerFloatingIP deletes an attached floating IP from a server. 1012 func (n *Nova) removeServerFloatingIP(serverId string, ipId string) error { 1013 if err := n.ProcessFunctionHook(n, serverId); err != nil { 1014 return err 1015 } 1016 if _, err := n.server(serverId); err != nil { 1017 return err 1018 } 1019 var fip *nova.FloatingIP 1020 var err error 1021 if n.useNeutronNetworking { 1022 fip, err = n.neutronModel.NovaFloatingIP(ipId) 1023 if err != nil { 1024 return err 1025 } 1026 fip.FixedIP = nil 1027 if err = n.neutronModel.UpdateNovaFloatingIP(fip); err != nil { 1028 return err 1029 } 1030 } else { 1031 if fip, err = n.floatingIP(ipId); err != nil { 1032 return err 1033 } else { 1034 fip.FixedIP = nil 1035 fip.InstanceId = nil 1036 n.floatingIPs[ipId] = *fip 1037 } 1038 } 1039 if err := n.removeFloatingIPFromServerAddresses(serverId, fip.IP); err != nil { 1040 return err 1041 } 1042 fips, ok := n.serverIPs[serverId] 1043 if !ok { 1044 return testservices.NewNoFloatingIPsToRemoveError(serverId) 1045 } 1046 idx := -1 1047 for fi, fipId := range fips { 1048 if fipId == ipId { 1049 idx = fi 1050 break 1051 } 1052 } 1053 if idx == -1 { 1054 return testservices.NewNoFloatingIPsError(serverId, ipId) 1055 } 1056 fips = append(fips[:idx], fips[idx+1:]...) 1057 n.serverIPs[serverId] = fips 1058 return nil 1059 } 1060 1061 // allNetworks returns a list of all existing networks. 1062 func (n *Nova) allNetworks() (networks []nova.Network) { 1063 if n.useNeutronNetworking { 1064 return n.neutronModel.AllNovaNetworks() 1065 } else { 1066 for _, net := range n.networks { 1067 networks = append(networks, net) 1068 } 1069 return networks 1070 } 1071 } 1072 1073 // networks returns the named network if it exists 1074 func (n *Nova) network(name string) (*nova.Network, error) { 1075 if n.useNeutronNetworking { 1076 return n.neutronModel.NovaNetwork(name) 1077 } else { 1078 net, ok := n.networks[name] 1079 var err error 1080 if !ok { 1081 err = errors.NewNotFoundf(nil, nil, "network") 1082 } 1083 return &net, err 1084 } 1085 } 1086 1087 // allAvailabilityZones returns a list of all existing availability zones, 1088 // sorted by name. 1089 func (n *Nova) allAvailabilityZones() (zones []nova.AvailabilityZone) { 1090 for _, zone := range n.availabilityZones { 1091 zones = append(zones, zone) 1092 } 1093 sort.Sort(azByName(zones)) 1094 return zones 1095 } 1096 1097 type azByName []nova.AvailabilityZone 1098 1099 func (a azByName) Len() int { 1100 return len(a) 1101 } 1102 1103 func (a azByName) Less(i, j int) bool { 1104 return a[i].Name < a[j].Name 1105 } 1106 1107 func (a azByName) Swap(i, j int) { 1108 a[i], a[j] = a[j], a[i] 1109 } 1110 1111 // setServerMetadata sets metadata on a server. 1112 func (n *Nova) setServerMetadata(serverId string, metadata map[string]string) error { 1113 if err := n.ProcessFunctionHook(n, serverId, metadata); err != nil { 1114 return err 1115 } 1116 server, err := n.server(serverId) 1117 if err != nil { 1118 return err 1119 } 1120 if server.Metadata == nil { 1121 server.Metadata = make(map[string]string) 1122 } 1123 for k, v := range metadata { 1124 server.Metadata[k] = v 1125 } 1126 n.servers[serverId] = *server 1127 return nil 1128 } 1129 1130 // AddOSInterface adds a os-interface attachment to a server. 1131 func (n *Nova) AddOSInterface(serverID string, osInterfaces ...nova.OSInterface) error { 1132 for _, osInter := range osInterfaces { 1133 n.nextOSInterfaceId++ 1134 1135 port := &osInter 1136 port.PortID = strconv.Itoa(n.nextOSInterfaceId) 1137 1138 n.serverIdToOSInterfaces[serverID] = append(n.serverIdToOSInterfaces[serverID], *port) 1139 } 1140 1141 return nil 1142 } 1143 1144 // RemoveOSInterface removes a os-interface attachment from a server based 1145 // on the matching criteria. 1146 func (n *Nova) RemoveOSInterface(serverID, ipAddress string) error { 1147 interfaces, ok := n.serverIdToOSInterfaces[serverID] 1148 if !ok { 1149 return testservices.NewServerByIDNotFoundError(serverID) 1150 } 1151 1152 for i, v := range interfaces { 1153 if v.IPAddress == ipAddress { 1154 interfaces = append(interfaces[:i], interfaces[i+1:]...) 1155 n.serverIdToOSInterfaces[serverID] = interfaces 1156 return nil 1157 } 1158 } 1159 1160 return testservices.NewNoSuchOSInterfaceError(ipAddress) 1161 } 1162 1163 func (n *Nova) allOSInterfaces() []nova.OSInterface { 1164 var results []nova.OSInterface 1165 for serverID := range n.servers { 1166 results = append(results, n.serverOSInterfaces(serverID)...) 1167 } 1168 return results 1169 } 1170 1171 func (n *Nova) serverOSInterfaces(serverID string) []nova.OSInterface { 1172 if interfaces, ok := n.serverIdToOSInterfaces[serverID]; ok { 1173 return interfaces 1174 } 1175 return make([]nova.OSInterface, 0) 1176 } 1177 1178 func (n *Nova) serverOSInterface(serverID string, ipAddress string) (nova.OSInterface, error) { 1179 for _, osInterface := range n.serverOSInterfaces(serverID) { 1180 if osInterface.IPAddress == ipAddress { 1181 return osInterface, nil 1182 } 1183 } 1184 return nova.OSInterface{}, testservices.NewNoSuchOSInterfaceError(ipAddress) 1185 } 1186 1187 func (n *Nova) hasServerOSInterface(serverID string, ipAddress string) bool { 1188 for _, osInterface := range n.serverOSInterfaces(serverID) { 1189 for _, ips := range osInterface.FixedIPs { 1190 if ips.IPAddress == ipAddress { 1191 return true 1192 } 1193 } 1194 } 1195 return false 1196 }