gopkg.in/goose.v2@v2.0.1/testservices/neutronservice/service.go (about)

     1  // Neutron double testing service - internal direct API implementation
     2  
     3  package neutronservice
     4  
     5  import (
     6  	"net/url"
     7  	"strconv"
     8  	"strings"
     9  
    10  	"gopkg.in/goose.v2/neutron"
    11  	"gopkg.in/goose.v2/testservices"
    12  	"gopkg.in/goose.v2/testservices/identityservice"
    13  	"gopkg.in/goose.v2/testservices/neutronmodel"
    14  )
    15  
    16  var _ testservices.HttpService = (*Neutron)(nil)
    17  var _ identityservice.ServiceProvider = (*Neutron)(nil)
    18  
    19  // Neutron implements a OpenStack Neutron testing service and
    20  // contains the service double's internal state.
    21  type Neutron struct {
    22  	testservices.ServiceInstance
    23  	neutronModel *neutronmodel.NeutronModel
    24  	groups       map[string]neutron.SecurityGroupV2
    25  	rules        map[string]neutron.SecurityGroupRuleV2
    26  	floatingIPs  map[string]neutron.FloatingIPV2
    27  	networks     map[string]neutron.NetworkV2
    28  	subnets      map[string]neutron.SubnetV2
    29  	nextGroupId  int
    30  	nextRuleId   int
    31  	nextPortId   int
    32  	nextIPId     int
    33  }
    34  
    35  func errorJSONEncode(err error) (int, string) {
    36  	serverError, ok := err.(*testservices.ServerError)
    37  	if !ok {
    38  		serverError = testservices.NewInternalServerError(err.Error())
    39  	}
    40  	return serverError.Code(), serverError.AsJSON()
    41  }
    42  
    43  // endpoint returns endpoint URL from the given path.
    44  // openstack catalog list
    45  // | neutron  | network      | RegionOne
    46  // |          |              |   publicURL: http://<keystone ip>:9696
    47  // |          |              |   internalURL: http://<keystone ip>:9696
    48  // |          |              |   adminURL: http://<keystone ip>:9696
    49  func (n *Neutron) endpointURL(version bool, path string) string {
    50  	ep := n.Scheme + "://" + n.Hostname
    51  	if version {
    52  		ep += n.VersionPath + "/"
    53  	}
    54  	if path != "" {
    55  		ep += strings.TrimLeft(path, "/")
    56  	}
    57  	return ep
    58  }
    59  
    60  func (n *Neutron) Endpoints() []identityservice.Endpoint {
    61  	ep := identityservice.Endpoint{
    62  		AdminURL:    n.endpointURL(false, ""),
    63  		InternalURL: n.endpointURL(false, ""),
    64  		PublicURL:   n.endpointURL(false, ""),
    65  		Region:      n.Region,
    66  	}
    67  	return []identityservice.Endpoint{ep}
    68  }
    69  
    70  func (n *Neutron) V3Endpoints() []identityservice.V3Endpoint {
    71  	url := n.endpointURL(false, "")
    72  	return identityservice.NewV3Endpoints(url, url, url, n.RegionID)
    73  }
    74  
    75  // New creates an instance of the Neutron object, given the parameters.
    76  func New(hostURL, versionPath, tenantId, region string, identityService, fallbackIdentity identityservice.IdentityService) *Neutron {
    77  	URL, err := url.Parse(hostURL)
    78  	if err != nil {
    79  		panic(err)
    80  	}
    81  	hostname := URL.Host
    82  	if !strings.HasSuffix(hostname, "/") {
    83  		hostname += "/"
    84  	}
    85  	defaultSubnets := []neutron.SubnetV2{
    86  		{
    87  			Id:        "999-01",
    88  			NetworkId: "999",
    89  			Name:      "subnet-999",
    90  			Cidr:      "10.9.0.0/24",
    91  		},
    92  		{
    93  			Id:        "998-01",
    94  			NetworkId: "998",
    95  			Name:      "subnet-998",
    96  			Cidr:      "10.8.0.0/24",
    97  		},
    98  		{
    99  			Id:        "997-01",
   100  			NetworkId: "997",
   101  			Name:      "subnet-997",
   102  			Cidr:      "2001:db8::/32",
   103  		},
   104  	}
   105  	neutronService := &Neutron{
   106  		subnets: make(map[string]neutron.SubnetV2),
   107  		ServiceInstance: testservices.ServiceInstance{
   108  			IdentityService:         identityService,
   109  			FallbackIdentityService: fallbackIdentity,
   110  			Scheme:                  URL.Scheme,
   111  			Hostname:                hostname,
   112  			VersionPath:             versionPath,
   113  			TenantId:                tenantId,
   114  			Region:                  region,
   115  		},
   116  	}
   117  	if identityService != nil {
   118  		identityService.RegisterServiceProvider("neutron", "network", neutronService)
   119  	}
   120  	for _, subnet := range defaultSubnets {
   121  		err := neutronService.addSubnet(subnet)
   122  		if err != nil {
   123  			panic(err)
   124  		}
   125  	}
   126  	return neutronService
   127  }
   128  
   129  func (n *Neutron) Stop() {
   130  	// noop
   131  }
   132  
   133  // AddNeutronModel setups up the test double for shared data between the nova
   134  // and neutron test doubles.  Required for the neutron test double.
   135  func (n *Neutron) AddNeutronModel(neutronModel *neutronmodel.NeutronModel) {
   136  	n.neutronModel = neutronModel
   137  }
   138  
   139  // NeutronModel returns the current NeutronModel, which can then be used to
   140  // update internal state for neutron test doubles.
   141  func (n *Neutron) NeutronModel() *neutronmodel.NeutronModel {
   142  	return n.neutronModel
   143  }
   144  
   145  // updateSecurityGroup updates an existing security group.
   146  func (n *Neutron) updateSecurityGroup(group neutron.SecurityGroupV2) error {
   147  	if err := n.ProcessFunctionHook(n, group); err != nil {
   148  		return err
   149  	}
   150  	return n.neutronModel.UpdateSecurityGroup(group)
   151  }
   152  
   153  // addPort creates a new port.
   154  func (n *Neutron) addPort(port neutron.PortV2) error {
   155  	if err := n.ProcessFunctionHook(n, port); err != nil {
   156  		return err
   157  	}
   158  	return n.neutronModel.AddPort(port)
   159  }
   160  
   161  // port retrieves an existing port by ID.
   162  func (n *Neutron) port(portId string) (*neutron.PortV2, error) {
   163  	if err := n.ProcessFunctionHook(n, portId); err != nil {
   164  		return nil, err
   165  	}
   166  	return n.neutronModel.Port(portId)
   167  }
   168  
   169  // allPorts returns a list of all existing ports.
   170  func (n *Neutron) allPorts() []neutron.PortV2 {
   171  	return n.neutronModel.AllPorts()
   172  }
   173  
   174  // removePort deletes an existing port.
   175  func (n *Neutron) removePort(portId string) error {
   176  	if err := n.ProcessFunctionHook(n, portId); err != nil {
   177  		return err
   178  	}
   179  	return n.neutronModel.RemovePort(portId)
   180  }
   181  
   182  // addSecurityGroup creates a new security group.
   183  func (n *Neutron) addSecurityGroup(group neutron.SecurityGroupV2) error {
   184  	if err := n.ProcessFunctionHook(n, group); err != nil {
   185  		return err
   186  	}
   187  	return n.neutronModel.AddSecurityGroup(group)
   188  }
   189  
   190  // securityGroup retrieves an existing group by ID.
   191  func (n *Neutron) securityGroup(groupId string) (*neutron.SecurityGroupV2, error) {
   192  	if err := n.ProcessFunctionHook(n, groupId); err != nil {
   193  		return nil, err
   194  	}
   195  	return n.neutronModel.SecurityGroup(groupId)
   196  }
   197  
   198  // securityGroupByName retrieves an existing named group.
   199  func (n *Neutron) securityGroupByName(groupName string) ([]neutron.SecurityGroupV2, error) {
   200  	if err := n.ProcessFunctionHook(n, groupName); err != nil {
   201  		return nil, err
   202  	}
   203  	return n.neutronModel.SecurityGroupByName(groupName)
   204  }
   205  
   206  // allSecurityGroups returns a list of all existing groups.
   207  func (n *Neutron) allSecurityGroups() []neutron.SecurityGroupV2 {
   208  	return n.neutronModel.AllSecurityGroups()
   209  }
   210  
   211  // removeSecurityGroup deletes an existing group.
   212  func (n *Neutron) removeSecurityGroup(groupId string) error {
   213  	if err := n.ProcessFunctionHook(n, groupId); err != nil {
   214  		return err
   215  	}
   216  	return n.neutronModel.RemoveSecurityGroup(groupId)
   217  }
   218  
   219  // addSecurityGroupRule creates a new rule in an existing group.
   220  // This can be either an ingress or an egress rule (see the notes
   221  // about neutron.RuleInfoV2).
   222  func (n *Neutron) addSecurityGroupRule(ruleId string, rule neutron.RuleInfoV2) error {
   223  	if err := n.ProcessFunctionHook(n, ruleId, rule); err != nil {
   224  		return err
   225  	}
   226  	return n.neutronModel.AddSecurityGroupRule(ruleId, rule)
   227  }
   228  
   229  // hasSecurityGroupRule returns whether the given group contains the given rule.
   230  func (n *Neutron) hasSecurityGroupRule(groupId, ruleId string) bool {
   231  	return n.neutronModel.HasSecurityGroupRule(groupId, ruleId)
   232  }
   233  
   234  // securityGroupRule retrieves an existing rule by ID.
   235  func (n *Neutron) securityGroupRule(ruleId string) (*neutron.SecurityGroupRuleV2, error) {
   236  	if err := n.ProcessFunctionHook(n, ruleId); err != nil {
   237  		return nil, err
   238  	}
   239  	return n.neutronModel.SecurityGroupRule(ruleId)
   240  }
   241  
   242  // removeSecurityGroupRule deletes an existing rule from its group.
   243  func (n *Neutron) removeSecurityGroupRule(ruleId string) error {
   244  	if err := n.ProcessFunctionHook(n, ruleId); err != nil {
   245  		return err
   246  	}
   247  	return n.neutronModel.RemoveSecurityGroupRule(ruleId)
   248  }
   249  
   250  // addFloatingIP creates a new floating IP address in the pool.
   251  func (n *Neutron) addFloatingIP(ip neutron.FloatingIPV2) error {
   252  	if err := n.ProcessFunctionHook(n, ip); err != nil {
   253  		return err
   254  	}
   255  	return n.neutronModel.AddFloatingIP(ip)
   256  }
   257  
   258  // hasFloatingIP returns whether the given floating IP address exists.
   259  func (n *Neutron) hasFloatingIP(address string) bool {
   260  	return n.neutronModel.HasFloatingIP(address)
   261  }
   262  
   263  // floatingIP retrieves the floating IP by ID.
   264  func (n *Neutron) floatingIP(ipId string) (*neutron.FloatingIPV2, error) {
   265  	if err := n.ProcessFunctionHook(n, ipId); err != nil {
   266  		return nil, err
   267  	}
   268  	return n.neutronModel.FloatingIP(ipId)
   269  }
   270  
   271  // floatingIPByAddr retrieves the floating IP by address.
   272  func (n *Neutron) floatingIPByAddr(address string) (*neutron.FloatingIPV2, error) {
   273  	if err := n.ProcessFunctionHook(n, address); err != nil {
   274  		return nil, err
   275  	}
   276  	return n.neutronModel.FloatingIPByAddr(address)
   277  }
   278  
   279  // matchFloatingIPs returns a list of matching FloatingIPs, after applying the
   280  // given filter. Each separate filter is combined with a logical AND.
   281  // Each filter can have only one value. A nil filter matches all FloatingIPs.
   282  //
   283  // This is tested to match OpenStack behavior. Regular expression
   284  // matching is supported for FilterProjectId only, and the supported
   285  // syntax is limited to whatever DB backend is used (see SQL
   286  // REGEXP/RLIKE).
   287  //
   288  // Example:
   289  //
   290  // f := filter{
   291  //     neutron.FilterProjectId: `foo.*`,
   292  // }
   293  //
   294  // This will match all FloatingIPs with project_id starting
   295  // with "foo".
   296  func (n *Neutron) matchFloatingIPs(f filter) []neutron.FloatingIPV2 {
   297  	fips := n.neutronModel.AllFloatingIPs()
   298  	if len(f) == 0 {
   299  		return fips
   300  	}
   301  	if projectId := f[neutron.FilterProjectId]; projectId != "" {
   302  		matched := []neutron.FloatingIPV2{}
   303  		externalNetworks, err := n.matchNetworks(filter{neutron.FilterRouterExternal: "true"})
   304  		if err != nil {
   305  			return fips
   306  		}
   307  		for _, fip := range fips {
   308  			for _, network := range externalNetworks {
   309  				if fip.FloatingNetworkId == network.Id && projectId == network.TenantId {
   310  					matched = append(matched, fip)
   311  				}
   312  			}
   313  		}
   314  		fips = matched
   315  	}
   316  	return fips
   317  }
   318  
   319  // allFloatingIPs returns a list of all created floating IPs.
   320  func (n *Neutron) allFloatingIPs(f filter) []neutron.FloatingIPV2 {
   321  	return n.matchFloatingIPs(f)
   322  }
   323  
   324  // removeFloatingIP deletes an existing floating IP by ID.
   325  func (n *Neutron) removeFloatingIP(ipId string) error {
   326  	if err := n.ProcessFunctionHook(n, ipId); err != nil {
   327  		return err
   328  	}
   329  	return n.neutronModel.RemoveFloatingIP(ipId)
   330  }
   331  
   332  // filter is used internally by matchNetworks and matchFloatingIPs.
   333  type filter map[string]string
   334  
   335  // matchNetworks returns a list of matching networks, after applying the
   336  // given filter. Each separate filter is combined with a logical AND.
   337  // Each filter can have only one value. A nil filter matches all networks.
   338  //
   339  // This is tested to match OpenStack behavior. Regular expression
   340  // matching is supported for FilterNetwork only, and the supported
   341  // syntax is limited to whatever DB backend is used (see SQL
   342  // REGEXP/RLIKE).
   343  //
   344  // Example:
   345  //
   346  // f := filter{
   347  //     neutron.FilterRouterExternal: true,
   348  //     neutron.FilterNetwork: `foo.*`,
   349  // }
   350  //
   351  // This will match all external neworks with names starting
   352  // with "foo".
   353  func (n *Neutron) matchNetworks(f filter) ([]neutron.NetworkV2, error) {
   354  	networks := n.neutronModel.AllNetworks()
   355  	if len(f) == 0 {
   356  		return networks, nil
   357  	}
   358  	if external := f[neutron.FilterRouterExternal]; external != "" {
   359  		matched := []neutron.NetworkV2{}
   360  		externalBool, err := strconv.ParseBool(external)
   361  		if err != nil {
   362  			return nil, err
   363  		}
   364  		for _, network := range networks {
   365  			if network.External == externalBool {
   366  				matched = append(matched, network)
   367  			}
   368  		}
   369  		if len(matched) == 0 {
   370  			// no match, so no need to look further
   371  			return nil, nil
   372  		}
   373  		networks = matched
   374  	}
   375  	if name := f[neutron.FilterNetwork]; name != "" {
   376  		matched := []neutron.NetworkV2{}
   377  		for _, network := range networks {
   378  			if name == network.Name {
   379  				matched = append(matched, network)
   380  			}
   381  		}
   382  		if len(matched) == 0 {
   383  			// no match, so no need to look further
   384  			return nil, nil
   385  		}
   386  		networks = matched
   387  	}
   388  	return networks, nil
   389  }
   390  
   391  // allNetworks returns a list of all existing networks.
   392  func (n *Neutron) allNetworks(f filter) ([]neutron.NetworkV2, error) {
   393  	return n.matchNetworks(f)
   394  }
   395  
   396  // network retrieves the network by ID.
   397  func (n *Neutron) network(networkId string) (*neutron.NetworkV2, error) {
   398  	if err := n.ProcessFunctionHook(n, networkId); err != nil {
   399  		return nil, err
   400  	}
   401  	return n.neutronModel.Network(networkId)
   402  }
   403  
   404  // addNetwork creates a new network.
   405  func (n *Neutron) addNetwork(network neutron.NetworkV2) error {
   406  	if err := n.ProcessFunctionHook(n, network); err != nil {
   407  		return err
   408  	}
   409  	return n.neutronModel.AddNetwork(network)
   410  }
   411  
   412  // removeNetwork deletes an existing group.
   413  func (n *Neutron) removeNetwork(netId string) error {
   414  	if err := n.ProcessFunctionHook(n, netId); err != nil {
   415  		return err
   416  	}
   417  	return n.neutronModel.RemoveNetwork(netId)
   418  }
   419  
   420  // allSubnets returns a list of all existing subnets.
   421  func (n *Neutron) allSubnets() (subnets []neutron.SubnetV2) {
   422  	for _, sub := range n.subnets {
   423  		subnets = append(subnets, sub)
   424  	}
   425  	return subnets
   426  }
   427  
   428  // subnet retrieves the subnet by ID.
   429  func (n *Neutron) subnet(subnetId string) (*neutron.SubnetV2, error) {
   430  	if err := n.ProcessFunctionHook(n, subnetId); err != nil {
   431  		return nil, err
   432  	}
   433  	subnet, ok := n.subnets[subnetId]
   434  	if !ok {
   435  		return nil, testservices.NewSubnetNotFoundError(subnetId)
   436  	}
   437  	return &subnet, nil
   438  }
   439  
   440  // addSubnet creates a new subnet.
   441  func (n *Neutron) addSubnet(subnet neutron.SubnetV2) error {
   442  	if err := n.ProcessFunctionHook(n, subnet); err != nil {
   443  		return err
   444  	}
   445  	if _, err := n.subnet(subnet.Id); err == nil {
   446  		return testservices.NewSubnetAlreadyExistsError(subnet.Id)
   447  	}
   448  	subnet.TenantId = n.TenantId
   449  	n.subnets[subnet.Id] = subnet
   450  	return nil
   451  }
   452  
   453  // removeSubnet deletes an existing subnet.
   454  func (n *Neutron) removeSubnet(subnetId string) error {
   455  	if err := n.ProcessFunctionHook(n, subnetId); err != nil {
   456  		return err
   457  	}
   458  	if _, err := n.subnet(subnetId); err != nil {
   459  		return err
   460  	}
   461  	delete(n.subnets, subnetId)
   462  	return nil
   463  }