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  }