github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/provider/openstack/local_test.go (about)

     1  // Copyright 2012, 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package openstack_test
     5  
     6  import (
     7  	"bytes"
     8  	stdcontext "context"
     9  	"encoding/pem"
    10  	"fmt"
    11  	"io"
    12  	"net/http"
    13  	"net/http/httptest"
    14  	"net/url"
    15  	"os"
    16  	"path/filepath"
    17  	"regexp"
    18  	"sort"
    19  	"strings"
    20  	"time"
    21  
    22  	"github.com/go-goose/goose/v5/cinder"
    23  	"github.com/go-goose/goose/v5/client"
    24  	"github.com/go-goose/goose/v5/identity"
    25  	"github.com/go-goose/goose/v5/neutron"
    26  	"github.com/go-goose/goose/v5/nova"
    27  	"github.com/go-goose/goose/v5/testservices/hook"
    28  	"github.com/go-goose/goose/v5/testservices/identityservice"
    29  	"github.com/go-goose/goose/v5/testservices/neutronmodel"
    30  	"github.com/go-goose/goose/v5/testservices/neutronservice"
    31  	"github.com/go-goose/goose/v5/testservices/novaservice"
    32  	"github.com/go-goose/goose/v5/testservices/openstackservice"
    33  	"github.com/juju/clock/testclock"
    34  	"github.com/juju/collections/set"
    35  	"github.com/juju/errors"
    36  	"github.com/juju/names/v5"
    37  	gitjujutesting "github.com/juju/testing"
    38  	jc "github.com/juju/testing/checkers"
    39  	"github.com/juju/utils/v3"
    40  	"github.com/juju/utils/v3/ssh"
    41  	"github.com/juju/version/v2"
    42  	gc "gopkg.in/check.v1"
    43  
    44  	"github.com/juju/juju/cloud"
    45  	"github.com/juju/juju/cloudconfig/instancecfg"
    46  	"github.com/juju/juju/core/arch"
    47  	corebase "github.com/juju/juju/core/base"
    48  	"github.com/juju/juju/core/constraints"
    49  	"github.com/juju/juju/core/instance"
    50  	"github.com/juju/juju/core/network"
    51  	corenetwork "github.com/juju/juju/core/network"
    52  	"github.com/juju/juju/core/network/firewall"
    53  	"github.com/juju/juju/core/status"
    54  	"github.com/juju/juju/environs"
    55  	"github.com/juju/juju/environs/bootstrap"
    56  	environscloudspec "github.com/juju/juju/environs/cloudspec"
    57  	"github.com/juju/juju/environs/config"
    58  	"github.com/juju/juju/environs/context"
    59  	"github.com/juju/juju/environs/filestorage"
    60  	"github.com/juju/juju/environs/imagemetadata"
    61  	imagetesting "github.com/juju/juju/environs/imagemetadata/testing"
    62  	"github.com/juju/juju/environs/instances"
    63  	"github.com/juju/juju/environs/jujutest"
    64  	"github.com/juju/juju/environs/simplestreams"
    65  	sstesting "github.com/juju/juju/environs/simplestreams/testing"
    66  	envstorage "github.com/juju/juju/environs/storage"
    67  	"github.com/juju/juju/environs/tags"
    68  	envtesting "github.com/juju/juju/environs/testing"
    69  	"github.com/juju/juju/environs/tools"
    70  	"github.com/juju/juju/juju/keys"
    71  	"github.com/juju/juju/juju/testing"
    72  	"github.com/juju/juju/jujuclient"
    73  	"github.com/juju/juju/provider/common"
    74  	"github.com/juju/juju/provider/openstack"
    75  	"github.com/juju/juju/storage"
    76  	coretesting "github.com/juju/juju/testing"
    77  	jujuversion "github.com/juju/juju/version"
    78  )
    79  
    80  type ProviderSuite struct {
    81  	restoreTimeouts func()
    82  }
    83  
    84  var _ = gc.Suite(&ProviderSuite{})
    85  
    86  func (s *ProviderSuite) SetUpTest(c *gc.C) {
    87  	s.restoreTimeouts = envtesting.PatchAttemptStrategies(openstack.ShortAttempt, openstack.StorageAttempt)
    88  }
    89  
    90  func (s *ProviderSuite) TearDownTest(c *gc.C) {
    91  	s.restoreTimeouts()
    92  }
    93  
    94  // Register tests to run against a test Openstack instance (service doubles).
    95  func registerLocalTests() {
    96  	cred := &identity.Credentials{
    97  		User:       "fred",
    98  		Secrets:    "secret",
    99  		Region:     "some-region",
   100  		TenantName: "some tenant",
   101  	}
   102  	testConfig := makeTestConfig(cred)
   103  	testConfig["agent-version"] = coretesting.FakeVersionNumber.String()
   104  	testConfig["authorized-keys"] = "fakekey"
   105  	testConfig["network"] = "private_999"
   106  	gc.Suite(&localLiveSuite{
   107  		LiveTests: LiveTests{
   108  			cred: cred,
   109  			LiveTests: jujutest.LiveTests{
   110  				TestConfig: testConfig,
   111  			},
   112  		},
   113  	})
   114  	gc.Suite(&localServerSuite{
   115  		cred: cred,
   116  		Tests: jujutest.Tests{
   117  			TestConfig: testConfig,
   118  		},
   119  	})
   120  	gc.Suite(&noNeutronSuite{
   121  		cred: cred,
   122  	})
   123  }
   124  
   125  // localServer is used to spin up a local Openstack service double.
   126  type localServer struct {
   127  	OpenstackSvc    *openstackservice.Openstack
   128  	Nova            *novaservice.Nova
   129  	Neutron         *neutronservice.Neutron
   130  	restoreTimeouts func()
   131  	UseTLS          bool
   132  }
   133  
   134  type newOpenstackFunc func(*identity.Credentials, identity.AuthMode, bool) (*openstackservice.Openstack, []string)
   135  
   136  func (s *localServer) start(
   137  	c *gc.C, cred *identity.Credentials, newOpenstackFunc newOpenstackFunc,
   138  ) {
   139  	var logMsg []string
   140  	s.OpenstackSvc, logMsg = newOpenstackFunc(cred, identity.AuthUserPass, s.UseTLS)
   141  	s.Nova = s.OpenstackSvc.Nova
   142  	s.Neutron = s.OpenstackSvc.Neutron
   143  	for _, msg := range logMsg {
   144  		c.Logf("%v", msg)
   145  	}
   146  	s.restoreTimeouts = envtesting.PatchAttemptStrategies(openstack.ShortAttempt, openstack.StorageAttempt)
   147  	s.Nova.SetAvailabilityZones(
   148  		nova.AvailabilityZone{Name: "test-unavailable"},
   149  		nova.AvailabilityZone{
   150  			Name: "test-available",
   151  			State: nova.AvailabilityZoneState{
   152  				Available: true,
   153  			},
   154  		},
   155  	)
   156  }
   157  
   158  func (s *localServer) stop() {
   159  	if s.OpenstackSvc != nil {
   160  		s.OpenstackSvc.Stop()
   161  	} else if s.Nova != nil {
   162  		s.Nova.Stop()
   163  	}
   164  	s.restoreTimeouts()
   165  }
   166  
   167  func (s *localServer) openstackCertificate(c *gc.C) ([]string, error) {
   168  	certificate, err := s.OpenstackSvc.Certificate(openstackservice.Identity)
   169  	if err != nil {
   170  		return []string{}, err
   171  	}
   172  	if certificate == nil {
   173  		return []string{}, errors.New("No certificate returned from openstack test double")
   174  	}
   175  	buf := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certificate.Raw})
   176  	return []string{string(buf)}, nil
   177  }
   178  
   179  func (s *localHTTPSServerSuite) envUsingCertificate(c *gc.C) environs.Environ {
   180  	newattrs := make(map[string]interface{}, len(s.attrs))
   181  	for k, v := range s.attrs {
   182  		newattrs[k] = v
   183  	}
   184  	newattrs["ssl-hostname-verification"] = true
   185  	cfg, err := config.New(config.NoDefaults, newattrs)
   186  	c.Assert(err, jc.ErrorIsNil)
   187  
   188  	cloudSpec := makeCloudSpec(s.cred)
   189  	cloudSpec.CACertificates, err = s.srv.openstackCertificate(c)
   190  	c.Assert(err, jc.ErrorIsNil)
   191  
   192  	env, err := environs.New(stdcontext.TODO(), environs.OpenParams{
   193  		Cloud:  cloudSpec,
   194  		Config: cfg,
   195  	})
   196  	c.Assert(err, jc.ErrorIsNil)
   197  	return env
   198  }
   199  
   200  // localLiveSuite runs tests from LiveTests using an Openstack service double.
   201  type localLiveSuite struct {
   202  	coretesting.BaseSuite
   203  	LiveTests
   204  	srv localServer
   205  }
   206  
   207  func makeMockAdapter() *mockAdapter {
   208  	volumes := make(map[string]*cinder.Volume)
   209  	return &mockAdapter{
   210  		createVolume: func(args cinder.CreateVolumeVolumeParams) (*cinder.Volume, error) {
   211  			metadata := args.Metadata.(map[string]string)
   212  			volume := cinder.Volume{
   213  				ID:               args.Name,
   214  				Metadata:         metadata,
   215  				Status:           "cool",
   216  				AvailabilityZone: args.AvailabilityZone,
   217  			}
   218  			volumes[volume.ID] = &volume
   219  			return &volume, nil
   220  		},
   221  		getVolumesDetail: func() ([]cinder.Volume, error) {
   222  			var result []cinder.Volume
   223  			for _, volume := range volumes {
   224  				result = append(result, *volume)
   225  			}
   226  			return result, nil
   227  		},
   228  		getVolume: func(volumeId string) (*cinder.Volume, error) {
   229  			if volume, ok := volumes[volumeId]; ok {
   230  				return volume, nil
   231  			}
   232  			return nil, errors.New("not found")
   233  		},
   234  		setVolumeMetadata: func(volumeId string, metadata map[string]string) (map[string]string, error) {
   235  			if volume, ok := volumes[volumeId]; ok {
   236  				for k, v := range metadata {
   237  					volume.Metadata[k] = v
   238  				}
   239  				return volume.Metadata, nil
   240  			}
   241  			return nil, errors.New("not found")
   242  		},
   243  	}
   244  }
   245  
   246  func overrideCinderProvider(s *gitjujutesting.CleanupSuite, adapter *mockAdapter) {
   247  	s.PatchValue(openstack.NewOpenstackStorage, func(*openstack.Environ) (openstack.OpenstackStorage, error) {
   248  		return adapter, nil
   249  	})
   250  }
   251  
   252  func (s *localLiveSuite) SetUpSuite(c *gc.C) {
   253  	s.BaseSuite.SetUpSuite(c)
   254  
   255  	c.Logf("Running live tests using openstack service test double")
   256  	s.srv.start(c, s.cred, newFullOpenstackService)
   257  
   258  	// Set credentials to use when bootstrapping. Must be done after
   259  	// starting server to get the auth URL.
   260  	s.Credential = makeCredential(s.cred)
   261  	s.CloudEndpoint = s.cred.URL
   262  	s.CloudRegion = s.cred.Region
   263  
   264  	s.LiveTests.SetUpSuite(c)
   265  	openstack.UseTestImageData(openstack.ImageMetadataStorage(s.Env), s.cred)
   266  	restoreFinishBootstrap := envtesting.DisableFinishBootstrap()
   267  	s.AddCleanup(func(*gc.C) { restoreFinishBootstrap() })
   268  	overrideCinderProvider(&s.CleanupSuite, &mockAdapter{})
   269  }
   270  
   271  func (s *localLiveSuite) TearDownSuite(c *gc.C) {
   272  	openstack.RemoveTestImageData(openstack.ImageMetadataStorage(s.Env))
   273  	s.LiveTests.TearDownSuite(c)
   274  	s.srv.stop()
   275  	s.BaseSuite.TearDownSuite(c)
   276  }
   277  
   278  func (s *localLiveSuite) SetUpTest(c *gc.C) {
   279  	s.BaseSuite.SetUpTest(c)
   280  	s.LiveTests.SetUpTest(c)
   281  	imagetesting.PatchOfficialDataSources(&s.CleanupSuite, "")
   282  }
   283  
   284  func (s *localLiveSuite) TearDownTest(c *gc.C) {
   285  	s.LiveTests.TearDownTest(c)
   286  	s.BaseSuite.TearDownTest(c)
   287  }
   288  
   289  // localServerSuite contains tests that run against an Openstack service double.
   290  // These tests can test things that would be unreasonably slow or expensive
   291  // to test on a live Openstack server. The service double is started and stopped for
   292  // each test.
   293  type localServerSuite struct {
   294  	coretesting.BaseSuite
   295  	jujutest.Tests
   296  	cred                 *identity.Credentials
   297  	srv                  localServer
   298  	env                  environs.Environ
   299  	toolsMetadataStorage envstorage.Storage
   300  	imageMetadataStorage envstorage.Storage
   301  	storageAdapter       *mockAdapter
   302  	callCtx              context.ProviderCallContext
   303  }
   304  
   305  func (s *localServerSuite) SetUpSuite(c *gc.C) {
   306  	s.BaseSuite.SetUpSuite(c)
   307  	restoreFinishBootstrap := envtesting.DisableFinishBootstrap()
   308  	s.AddCleanup(func(*gc.C) { restoreFinishBootstrap() })
   309  
   310  	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   311  		w.WriteHeader(404)
   312  	}))
   313  	s.AddCleanup(func(c *gc.C) {
   314  		server.Close()
   315  	})
   316  	s.PatchValue(&imagemetadata.DefaultUbuntuBaseURL, server.URL)
   317  
   318  	c.Logf("Running local tests")
   319  }
   320  
   321  func (s *localServerSuite) SetUpTest(c *gc.C) {
   322  	s.BaseSuite.SetUpTest(c)
   323  	s.srv.start(c, s.cred, newFullOpenstackService)
   324  
   325  	// Set credentials to use when bootstrapping. Must be done after
   326  	// starting server to get the auth URL.
   327  	s.Credential = makeCredential(s.cred)
   328  	s.CloudEndpoint = s.cred.URL
   329  	s.CloudRegion = s.cred.Region
   330  	cl := client.NewClient(s.cred, identity.AuthUserPass, nil)
   331  	err := cl.Authenticate()
   332  	c.Assert(err, jc.ErrorIsNil)
   333  	containerURL, err := cl.MakeServiceURL("object-store", "", nil)
   334  	c.Assert(err, jc.ErrorIsNil)
   335  	s.TestConfig = s.TestConfig.Merge(coretesting.Attrs{
   336  		"agent-metadata-url": containerURL + "/juju-dist-test/tools",
   337  		"image-metadata-url": containerURL + "/juju-dist-test",
   338  		"auth-url":           s.cred.URL,
   339  	})
   340  	s.PatchValue(&jujuversion.Current, coretesting.FakeVersionNumber)
   341  	s.Tests.SetUpTest(c)
   342  	// For testing, we create a storage instance to which is uploaded tools and image metadata.
   343  	s.env = s.Prepare(c)
   344  	s.toolsMetadataStorage = openstack.MetadataStorage(s.env)
   345  	// Put some fake metadata in place so that tests that are simply
   346  	// starting instances without any need to check if those instances
   347  	// are running can find the metadata.
   348  	envtesting.UploadFakeTools(c, s.toolsMetadataStorage, s.env.Config().AgentStream(), s.env.Config().AgentStream())
   349  	s.imageMetadataStorage = openstack.ImageMetadataStorage(s.env)
   350  	openstack.UseTestImageData(s.imageMetadataStorage, s.cred)
   351  	s.storageAdapter = makeMockAdapter()
   352  	overrideCinderProvider(&s.CleanupSuite, s.storageAdapter)
   353  	s.callCtx = context.NewEmptyCloudCallContext()
   354  }
   355  
   356  func (s *localServerSuite) TearDownTest(c *gc.C) {
   357  	if s.imageMetadataStorage != nil {
   358  		openstack.RemoveTestImageData(s.imageMetadataStorage)
   359  	}
   360  	if s.toolsMetadataStorage != nil {
   361  		envtesting.RemoveFakeToolsMetadata(c, s.toolsMetadataStorage)
   362  	}
   363  	s.Tests.TearDownTest(c)
   364  	s.srv.stop()
   365  	s.BaseSuite.TearDownTest(c)
   366  }
   367  
   368  func (s *localServerSuite) openEnviron(c *gc.C, attrs coretesting.Attrs) environs.Environ {
   369  	cfg, err := config.New(config.NoDefaults, s.TestConfig.Merge(attrs))
   370  	c.Assert(err, jc.ErrorIsNil)
   371  	env, err := environs.New(stdcontext.TODO(), environs.OpenParams{
   372  		Cloud:  s.CloudSpec(),
   373  		Config: cfg,
   374  	})
   375  	c.Assert(err, jc.ErrorIsNil)
   376  	return env
   377  }
   378  
   379  func (s *localServerSuite) TestBootstrap(c *gc.C) {
   380  	// Tests uses Prepare, so destroy first.
   381  	err := environs.Destroy(s.env.Config().Name(), s.env, s.callCtx, s.ControllerStore)
   382  	c.Assert(err, jc.ErrorIsNil)
   383  	s.Tests.TestBootstrap(c)
   384  }
   385  
   386  func (s *localServerSuite) TestStartStop(c *gc.C) {
   387  	// Tests uses Prepare, so destroy first.
   388  	err := environs.Destroy(s.env.Config().Name(), s.env, s.callCtx, s.ControllerStore)
   389  	c.Assert(err, jc.ErrorIsNil)
   390  	s.Tests.TestStartStop(c)
   391  }
   392  
   393  // If the bootstrap node is configured to require a public IP address,
   394  // bootstrapping fails if an address cannot be allocated.
   395  func (s *localServerSuite) TestBootstrapFailsWhenPublicIPError(c *gc.C) {
   396  	coretesting.SkipIfPPC64EL(c, "lp:1425242")
   397  
   398  	cleanup := s.srv.Neutron.RegisterControlPoint(
   399  		"addFloatingIP",
   400  		func(sc hook.ServiceControl, args ...interface{}) error {
   401  			return fmt.Errorf("failed on purpose")
   402  		},
   403  	)
   404  	defer cleanup()
   405  
   406  	err := environs.Destroy(s.env.Config().Name(), s.env, s.callCtx, s.ControllerStore)
   407  	c.Assert(err, jc.ErrorIsNil)
   408  
   409  	env := s.openEnviron(c, coretesting.Attrs{})
   410  	cons := constraints.MustParse("allocate-public-ip=true")
   411  	err = bootstrapEnvWithConstraints(c, env, cons)
   412  	c.Assert(err, gc.ErrorMatches, "(.|\n)*cannot allocate a public IP as needed(.|\n)*")
   413  }
   414  
   415  func (s *localServerSuite) TestAddressesWithPublicIPConstraints(c *gc.C) {
   416  	// Floating IP address is 10.0.0.1
   417  	bootstrapFinished := false
   418  	s.PatchValue(&common.FinishBootstrap, func(
   419  		ctx environs.BootstrapContext,
   420  		client ssh.Client,
   421  		env environs.Environ,
   422  		callCtx context.ProviderCallContext,
   423  		inst instances.Instance,
   424  		instanceConfig *instancecfg.InstanceConfig,
   425  		_ environs.BootstrapDialOpts,
   426  	) error {
   427  		addr, err := inst.Addresses(s.callCtx)
   428  		c.Assert(err, jc.ErrorIsNil)
   429  		c.Assert(addr, jc.SameContents, network.ProviderAddresses{
   430  			network.NewMachineAddress("10.0.0.1", corenetwork.WithScope(corenetwork.ScopePublic)).AsProviderAddress(),
   431  			network.NewMachineAddress("127.0.0.1", corenetwork.WithScope(corenetwork.ScopeMachineLocal)).AsProviderAddress(),
   432  			network.NewMachineAddress("::face::000f").AsProviderAddress(),
   433  			network.NewMachineAddress("127.10.0.1", corenetwork.WithScope(corenetwork.ScopePublic)).AsProviderAddress(),
   434  			network.NewMachineAddress("::dead:beef:f00d", corenetwork.WithScope(corenetwork.ScopePublic)).AsProviderAddress(),
   435  		})
   436  		bootstrapFinished = true
   437  		return nil
   438  	})
   439  
   440  	env := s.openEnviron(c, coretesting.Attrs{
   441  		"network": "private_999",
   442  	})
   443  	cons := constraints.MustParse("allocate-public-ip=true")
   444  	err := bootstrapEnvWithConstraints(c, env, cons)
   445  	c.Assert(err, jc.ErrorIsNil)
   446  	c.Assert(bootstrapFinished, jc.IsTrue)
   447  }
   448  
   449  func (s *localServerSuite) TestAddressesWithoutPublicIPConstraints(c *gc.C) {
   450  	bootstrapFinished := false
   451  	s.PatchValue(&common.FinishBootstrap, func(
   452  		ctx environs.BootstrapContext,
   453  		client ssh.Client,
   454  		env environs.Environ,
   455  		callCtx context.ProviderCallContext,
   456  		inst instances.Instance,
   457  		instanceConfig *instancecfg.InstanceConfig,
   458  		_ environs.BootstrapDialOpts,
   459  	) error {
   460  		addr, err := inst.Addresses(s.callCtx)
   461  		c.Assert(err, jc.ErrorIsNil)
   462  		c.Assert(addr, jc.SameContents, network.ProviderAddresses{
   463  			network.NewMachineAddress("127.0.0.1", corenetwork.WithScope(corenetwork.ScopeMachineLocal)).AsProviderAddress(),
   464  			network.NewMachineAddress("::face::000f").AsProviderAddress(),
   465  			network.NewMachineAddress("127.10.0.1", corenetwork.WithScope(corenetwork.ScopePublic)).AsProviderAddress(),
   466  			network.NewMachineAddress("::dead:beef:f00d", corenetwork.WithScope(corenetwork.ScopePublic)).AsProviderAddress(),
   467  		})
   468  		bootstrapFinished = true
   469  		return nil
   470  	})
   471  
   472  	env := s.openEnviron(c, coretesting.Attrs{})
   473  	cons := constraints.MustParse("allocate-public-ip=false")
   474  	err := bootstrapEnvWithConstraints(c, env, cons)
   475  	c.Assert(err, jc.ErrorIsNil)
   476  	c.Assert(bootstrapFinished, jc.IsTrue)
   477  }
   478  
   479  // If the environment is configured not to require a public IP address for nodes,
   480  // bootstrapping and starting an instance should occur without any attempt to
   481  // allocate a public address.
   482  func (s *localServerSuite) TestStartInstanceWithoutPublicIP(c *gc.C) {
   483  	cleanup := s.srv.Neutron.RegisterControlPoint(
   484  		"addFloatingIP",
   485  		func(sc hook.ServiceControl, args ...interface{}) error {
   486  			return fmt.Errorf("add floating IP should not have been called")
   487  		},
   488  	)
   489  	defer cleanup()
   490  	cleanup = s.srv.Nova.RegisterControlPoint(
   491  		"addServerFloatingIP",
   492  		func(sc hook.ServiceControl, args ...interface{}) error {
   493  			return fmt.Errorf("add server floating IP should not have been called")
   494  		},
   495  	)
   496  	defer cleanup()
   497  
   498  	err := environs.Destroy(s.env.Config().Name(), s.env, s.callCtx, s.ControllerStore)
   499  	c.Assert(err, jc.ErrorIsNil)
   500  
   501  	env := s.Prepare(c)
   502  	err = bootstrapEnv(c, env)
   503  	c.Assert(err, jc.ErrorIsNil)
   504  	inst, _ := testing.AssertStartInstance(c, env, s.callCtx, s.ControllerUUID, "100")
   505  	err = env.StopInstances(s.callCtx, inst.Id())
   506  	c.Assert(err, jc.ErrorIsNil)
   507  }
   508  
   509  // If we fail to allocate a floating IP when starting an instance, the nova instance
   510  // should be terminated.
   511  func (s *localServerSuite) TestStartInstanceWhenPublicIPError(c *gc.C) {
   512  	var (
   513  		addServerID        string
   514  		removeServerID     string
   515  		removeServerCalled bool
   516  	)
   517  
   518  	cleanup := s.srv.Neutron.RegisterControlPoint(
   519  		"addFloatingIP",
   520  		func(sc hook.ServiceControl, args ...interface{}) error {
   521  			return fmt.Errorf("fail on purpose")
   522  		},
   523  	)
   524  	defer cleanup()
   525  	cleanup = s.srv.Nova.RegisterControlPoint(
   526  		"addServer",
   527  		func(sc hook.ServiceControl, args ...interface{}) error {
   528  			addServerID = args[0].(*nova.ServerDetail).Id
   529  			return nil
   530  		},
   531  	)
   532  	defer cleanup()
   533  	cleanup = s.srv.Nova.RegisterControlPoint(
   534  		"removeServer",
   535  		func(sc hook.ServiceControl, args ...interface{}) error {
   536  			removeServerCalled = true
   537  			removeServerID = args[0].(string)
   538  			return nil
   539  		},
   540  	)
   541  	defer cleanup()
   542  	_, _, _, err := testing.StartInstanceWithConstraints(s.env, s.callCtx, s.ControllerUUID, "100", constraints.MustParse("allocate-public-ip=true"))
   543  	c.Assert(err, gc.ErrorMatches, "(.|\n)*cannot allocate a public IP as needed(.|\n)*")
   544  	c.Assert(removeServerCalled, jc.IsTrue)
   545  	c.Assert(removeServerID, gc.Equals, addServerID)
   546  }
   547  
   548  func (s *localServerSuite) TestStartInstanceHardwareCharacteristics(c *gc.C) {
   549  	// Ensure amd64 tools are available, to ensure an amd64 image.
   550  	env := s.ensureAMDImages(c)
   551  	err := bootstrapEnv(c, env)
   552  	c.Assert(err, jc.ErrorIsNil)
   553  	_, hc := testing.AssertStartInstanceWithConstraints(c, env, s.callCtx, s.ControllerUUID, "100", constraints.MustParse("mem=1024 arch=amd64"))
   554  	c.Check(*hc.Arch, gc.Equals, "amd64")
   555  	c.Check(*hc.Mem, gc.Equals, uint64(2048))
   556  	c.Check(*hc.CpuCores, gc.Equals, uint64(1))
   557  	c.Assert(hc.CpuPower, gc.IsNil)
   558  }
   559  
   560  func (s *localServerSuite) TestInstanceName(c *gc.C) {
   561  	inst, _ := testing.AssertStartInstance(c, s.env, s.callCtx, s.ControllerUUID, "100")
   562  	serverDetail := openstack.InstanceServerDetail(inst)
   563  	envName := s.env.Config().Name()
   564  	c.Assert(serverDetail.Name, gc.Matches, "juju-06f00d-"+envName+"-100")
   565  }
   566  
   567  func (s *localServerSuite) TestStartInstanceNetwork(c *gc.C) {
   568  	cfg, err := s.env.Config().Apply(coretesting.Attrs{
   569  		// A label that corresponds to a neutron test service network
   570  		"network": "net",
   571  	})
   572  	c.Assert(err, jc.ErrorIsNil)
   573  	err = s.env.SetConfig(cfg)
   574  	c.Assert(err, jc.ErrorIsNil)
   575  
   576  	inst, _ := testing.AssertStartInstance(c, s.env, s.callCtx, s.ControllerUUID, "100")
   577  	err = s.env.StopInstances(s.callCtx, inst.Id())
   578  	c.Assert(err, jc.ErrorIsNil)
   579  }
   580  
   581  func (s *localServerSuite) TestStartInstanceMultiNetworkFound(c *gc.C) {
   582  	cfg, err := s.env.Config().Apply(coretesting.Attrs{
   583  		"network": "",
   584  	})
   585  	c.Assert(err, jc.ErrorIsNil)
   586  
   587  	err = s.env.SetConfig(cfg)
   588  	c.Assert(err, jc.ErrorIsNil)
   589  
   590  	inst, _, _, err := testing.StartInstance(s.env, s.callCtx, s.ControllerUUID, "100")
   591  	c.Assert(err, jc.ErrorIsNil)
   592  	c.Check(inst, gc.NotNil)
   593  }
   594  
   595  func (s *localServerSuite) TestStartInstanceExternalNetwork(c *gc.C) {
   596  	cfg, err := s.env.Config().Apply(coretesting.Attrs{
   597  		// A label that corresponds to a neutron test service external network
   598  		"external-network": "ext-net",
   599  	})
   600  	c.Assert(err, jc.ErrorIsNil)
   601  	err = s.env.SetConfig(cfg)
   602  	c.Assert(err, jc.ErrorIsNil)
   603  
   604  	cons := constraints.MustParse("allocate-public-ip=true")
   605  	inst, _ := testing.AssertStartInstanceWithConstraints(c, s.env, s.callCtx, s.ControllerUUID, "100", cons)
   606  	err = s.env.StopInstances(s.callCtx, inst.Id())
   607  	c.Assert(err, jc.ErrorIsNil)
   608  }
   609  
   610  func (s *localServerSuite) TestStartInstanceNetworkUnknownLabel(c *gc.C) {
   611  	cfg, err := s.env.Config().Apply(coretesting.Attrs{
   612  		// A label that has no related network in the neutron test service
   613  		"network": "no-network-with-this-label",
   614  	})
   615  	c.Assert(err, jc.ErrorIsNil)
   616  	err = s.env.SetConfig(cfg)
   617  	c.Assert(err, jc.ErrorIsNil)
   618  
   619  	inst, _, _, err := testing.StartInstance(s.env, s.callCtx, s.ControllerUUID, "100")
   620  	c.Check(inst, gc.IsNil)
   621  	c.Assert(err, gc.ErrorMatches, `unable to determine networks for configured list: \[no-network-with-this-label\]`)
   622  }
   623  
   624  func (s *localServerSuite) TestStartInstanceExternalNetworkUnknownLabel(c *gc.C) {
   625  	cfg, err := s.env.Config().Apply(coretesting.Attrs{
   626  		// A label that has no related network in the neutron test service
   627  		"external-network": "no-network-with-this-label",
   628  	})
   629  	c.Assert(err, jc.ErrorIsNil)
   630  	err = s.env.SetConfig(cfg)
   631  	c.Assert(err, jc.ErrorIsNil)
   632  
   633  	cons := constraints.MustParse("allocate-public-ip=true")
   634  	inst, _, _, err := testing.StartInstanceWithConstraints(s.env, s.callCtx, s.ControllerUUID, "100", cons)
   635  	c.Assert(err, jc.ErrorIsNil)
   636  	err = s.env.StopInstances(s.callCtx, inst.Id())
   637  	c.Assert(err, jc.ErrorIsNil)
   638  }
   639  
   640  func (s *localServerSuite) TestStartInstanceNetworkUnknownID(c *gc.C) {
   641  	cfg, err := s.env.Config().Apply(coretesting.Attrs{
   642  		// A valid UUID but no related network in the nova test service
   643  		"network": "f81d4fae-7dec-11d0-a765-00a0c91e6bf6",
   644  	})
   645  	c.Assert(err, jc.ErrorIsNil)
   646  	err = s.env.SetConfig(cfg)
   647  	c.Assert(err, jc.ErrorIsNil)
   648  
   649  	inst, _, _, err := testing.StartInstance(s.env, s.callCtx, s.ControllerUUID, "100")
   650  	c.Check(inst, gc.IsNil)
   651  	c.Assert(err, gc.ErrorMatches,
   652  		`unable to determine networks for configured list: \[f81d4fae-7dec-11d0-a765-00a0c91e6bf6\]`)
   653  }
   654  
   655  func (s *localServerSuite) TestStartInstanceNoNetworksNetworkNotSetNoError(c *gc.C) {
   656  	// Modify the Openstack service that is created by default,
   657  	// to clear the networks.
   658  	model := neutronmodel.New()
   659  	for _, net := range model.AllNetworks() {
   660  		_ = model.RemoveNetwork(net.Id)
   661  	}
   662  	s.srv.OpenstackSvc.Neutron.AddNeutronModel(model)
   663  	s.srv.OpenstackSvc.Nova.AddNeutronModel(model)
   664  
   665  	cfg, err := s.env.Config().Apply(coretesting.Attrs{
   666  		"network": "",
   667  	})
   668  	c.Assert(err, jc.ErrorIsNil)
   669  	err = s.env.SetConfig(cfg)
   670  	c.Assert(err, jc.ErrorIsNil)
   671  
   672  	inst, _, _, err := testing.StartInstance(s.env, s.callCtx, s.ControllerUUID, "100")
   673  	c.Check(inst, gc.NotNil)
   674  	c.Assert(err, jc.ErrorIsNil)
   675  }
   676  
   677  func (s *localServerSuite) TestStartInstanceOneNetworkNetworkNotSetNoError(c *gc.C) {
   678  	// Modify the Openstack service that is created by default,
   679  	// to remove all but 1 internal network.
   680  	model := neutronmodel.New()
   681  	var foundOne bool
   682  	for _, net := range model.AllNetworks() {
   683  		if !net.External {
   684  			if !foundOne {
   685  				foundOne = true
   686  				continue
   687  			}
   688  			err := model.RemoveNetwork(net.Id)
   689  			c.Assert(err, jc.ErrorIsNil)
   690  		}
   691  	}
   692  	s.srv.OpenstackSvc.Neutron.AddNeutronModel(model)
   693  	s.srv.OpenstackSvc.Nova.AddNeutronModel(model)
   694  
   695  	cfg, err := s.env.Config().Apply(coretesting.Attrs{
   696  		"network": "",
   697  	})
   698  	c.Assert(err, jc.ErrorIsNil)
   699  	err = s.env.SetConfig(cfg)
   700  	c.Assert(err, jc.ErrorIsNil)
   701  
   702  	inst, _, _, err := testing.StartInstance(s.env, s.callCtx, s.ControllerUUID, "100")
   703  	c.Check(inst, gc.NotNil)
   704  	c.Assert(err, jc.ErrorIsNil)
   705  }
   706  
   707  func (s *localServerSuite) TestStartInstanceNetworksDifferentAZ(c *gc.C) {
   708  	// If both the network and external-network config values are
   709  	// specified, there is not check for them being on different
   710  	// network availability zones with allocate-public-ip constraint.
   711  	cfg, err := s.env.Config().Apply(coretesting.Attrs{
   712  		"network":          "net",     // az = nova
   713  		"external-network": "ext-net", // az = test-available
   714  	})
   715  	c.Assert(err, jc.ErrorIsNil)
   716  	err = s.env.SetConfig(cfg)
   717  	c.Assert(err, jc.ErrorIsNil)
   718  
   719  	cons := constraints.MustParse("allocate-public-ip=true")
   720  	inst, _, _, err := testing.StartInstanceWithConstraints(s.env, s.callCtx, s.ControllerUUID, "100", cons)
   721  	c.Assert(err, jc.ErrorIsNil)
   722  	err = s.env.StopInstances(s.callCtx, inst.Id())
   723  	c.Assert(err, jc.ErrorIsNil)
   724  }
   725  
   726  func (s *localServerSuite) TestStartInstanceNetworksEmptyAZ(c *gc.C) {
   727  	// Modify the Openstack service that is created by default,
   728  	// to clear the networks.
   729  	model := neutronmodel.New()
   730  	for _, net := range model.AllNetworks() {
   731  		_ = model.RemoveNetwork(net.Id)
   732  	}
   733  
   734  	// Add 2 networks to the Openstack service, one private,
   735  	// one external without availability zones.  LP: 1891227.
   736  	err := model.AddNetwork(neutron.NetworkV2{
   737  		Id:        "1",
   738  		Name:      "no-az-net",
   739  		SubnetIds: []string{"sub-net"},
   740  		External:  false,
   741  	})
   742  	c.Assert(err, jc.ErrorIsNil)
   743  	err = model.AddNetwork(neutron.NetworkV2{
   744  		Id:        "2",
   745  		Name:      "ext-no-az-net",
   746  		SubnetIds: []string{"ext-sub-net"},
   747  		External:  true,
   748  	})
   749  	c.Assert(err, jc.ErrorIsNil)
   750  	s.srv.OpenstackSvc.Neutron.AddNeutronModel(model)
   751  	s.srv.OpenstackSvc.Nova.AddNeutronModel(model)
   752  
   753  	// Set floating ip to ensure we try to find the external
   754  	// network.
   755  	cfg, err := s.env.Config().Apply(coretesting.Attrs{
   756  		"network": "no-az-net", // az = nova
   757  	})
   758  	c.Assert(err, jc.ErrorIsNil)
   759  	err = s.env.SetConfig(cfg)
   760  	c.Assert(err, jc.ErrorIsNil)
   761  
   762  	cons := constraints.MustParse("allocate-public-ip=true")
   763  	inst, _, _, err := testing.StartInstanceWithConstraints(s.env, s.callCtx, s.ControllerUUID, "100", cons)
   764  	c.Assert(err, jc.ErrorIsNil)
   765  	err = s.env.StopInstances(s.callCtx, inst.Id())
   766  	c.Assert(err, jc.ErrorIsNil)
   767  }
   768  
   769  func (s *localServerSuite) TestStartInstanceNetworkNoExternalNetInAZ(c *gc.C) {
   770  	cfg, err := s.env.Config().Apply(coretesting.Attrs{
   771  		"network": "net", // az = nova
   772  	})
   773  	c.Assert(err, jc.ErrorIsNil)
   774  	err = s.env.SetConfig(cfg)
   775  	c.Assert(err, jc.ErrorIsNil)
   776  
   777  	cons := constraints.MustParse("allocate-public-ip=true")
   778  	_, _, _, err = testing.StartInstanceWithConstraints(s.env, s.callCtx, s.ControllerUUID, "100", cons)
   779  	c.Assert(err, gc.ErrorMatches, "cannot allocate a public IP as needed: could not find an external network in availability zone.*")
   780  }
   781  
   782  func (s *localServerSuite) TestStartInstancePortSecurityEnabled(c *gc.C) {
   783  	cfg, err := s.env.Config().Apply(coretesting.Attrs{
   784  		"network": "net",
   785  	})
   786  	c.Assert(err, jc.ErrorIsNil)
   787  	err = s.env.SetConfig(cfg)
   788  	c.Assert(err, jc.ErrorIsNil)
   789  
   790  	inst, _, _, err := testing.StartInstance(s.env, s.callCtx, s.ControllerUUID, "100")
   791  	c.Assert(err, jc.ErrorIsNil)
   792  	novaClient := openstack.GetNovaClient(s.env)
   793  	detail, err := novaClient.GetServer(string(inst.Id()))
   794  	c.Assert(err, jc.ErrorIsNil)
   795  	c.Assert(detail.Groups, gc.NotNil)
   796  }
   797  
   798  func (s *localServerSuite) TestStartInstancePortSecurityDisabled(c *gc.C) {
   799  	cfg, err := s.env.Config().Apply(coretesting.Attrs{
   800  		"network": "net-disabled",
   801  	})
   802  	c.Assert(err, jc.ErrorIsNil)
   803  	err = s.env.SetConfig(cfg)
   804  	c.Assert(err, jc.ErrorIsNil)
   805  
   806  	inst, _, _, err := testing.StartInstance(s.env, s.callCtx, s.ControllerUUID, "100")
   807  	c.Assert(err, jc.ErrorIsNil)
   808  	novaClient := openstack.GetNovaClient(s.env)
   809  	detail, err := novaClient.GetServer(string(inst.Id()))
   810  	c.Assert(err, jc.ErrorIsNil)
   811  	c.Assert(detail.Groups, gc.IsNil)
   812  }
   813  
   814  func (s *localServerSuite) TestStartInstanceGetServerFail(c *gc.C) {
   815  	// Force an error in waitForActiveServerDetails
   816  	cleanup := s.srv.Nova.RegisterControlPoint(
   817  		"server",
   818  		func(sc hook.ServiceControl, args ...interface{}) error {
   819  			return fmt.Errorf("GetServer failed on purpose")
   820  		},
   821  	)
   822  	defer cleanup()
   823  	inst, _, _, err := testing.StartInstance(s.env, s.callCtx, s.ControllerUUID, "100")
   824  	c.Check(inst, gc.IsNil)
   825  	c.Assert(err, gc.ErrorMatches, "cannot run instance: "+
   826  		"request \\(.*/servers\\) returned unexpected status: "+
   827  		"500; error info: .*GetServer failed on purpose")
   828  	c.Assert(errors.Is(err, environs.ErrAvailabilityZoneIndependent), jc.IsTrue)
   829  }
   830  
   831  func (s *localServerSuite) TestStartInstanceWaitForActiveDetails(c *gc.C) {
   832  	env := s.openEnviron(c, coretesting.Attrs{"firewall-mode": config.FwInstance})
   833  
   834  	s.srv.Nova.SetServerStatus(nova.StatusBuild)
   835  	defer s.srv.Nova.SetServerStatus("")
   836  
   837  	// Make time advance in zero time
   838  	clk := testclock.NewClock(time.Time{})
   839  	clock := testclock.AutoAdvancingClock{Clock: clk, Advance: clk.Advance}
   840  	env.(*openstack.Environ).SetClock(&clock)
   841  
   842  	inst, _, _, err := testing.StartInstance(env, s.callCtx, s.ControllerUUID, "100")
   843  	c.Check(inst, gc.IsNil)
   844  	c.Assert(err, gc.ErrorMatches, "cannot run instance: max duration exceeded: instance .* has status BUILD")
   845  
   846  	// Ensure that the started instance got terminated.
   847  	insts, err := env.AllInstances(s.callCtx)
   848  	c.Assert(err, jc.ErrorIsNil)
   849  	c.Assert(insts, gc.HasLen, 0, gc.Commentf("expected launched instance to be terminated if stuck in BUILD state"))
   850  }
   851  
   852  func (s *localServerSuite) TestStartInstanceDeletesSecurityGroupsOnInstanceCreateFailure(c *gc.C) {
   853  	env := s.openEnviron(c, coretesting.Attrs{"firewall-mode": config.FwInstance})
   854  
   855  	// Force an error in waitForActiveServerDetails
   856  	cleanup := s.srv.Nova.RegisterControlPoint(
   857  		"server",
   858  		func(sc hook.ServiceControl, args ...interface{}) error {
   859  			return fmt.Errorf("GetServer failed on purpose")
   860  		},
   861  	)
   862  	defer cleanup()
   863  	inst, _, _, err := testing.StartInstance(env, s.callCtx, s.ControllerUUID, "100")
   864  	c.Check(inst, gc.IsNil)
   865  	c.Assert(err, gc.NotNil)
   866  
   867  	assertSecurityGroups(c, env, []string{"default"})
   868  }
   869  
   870  func (s *localServerSuite) TestStartInstanceDeletesSecurityGroupsOnFailure(c *gc.C) {
   871  	env := s.openEnviron(c, coretesting.Attrs{"firewall-mode": config.FwInstance})
   872  
   873  	s.srv.Nova.SetServerStatus(nova.StatusBuild)
   874  	defer s.srv.Nova.SetServerStatus("")
   875  
   876  	// Make time advance in zero time
   877  	clk := testclock.NewClock(time.Time{})
   878  	clock := testclock.AutoAdvancingClock{Clock: clk, Advance: clk.Advance}
   879  	env.(*openstack.Environ).SetClock(&clock)
   880  
   881  	_, _, _, err := testing.StartInstance(env, s.callCtx, s.ControllerUUID, "100")
   882  	c.Assert(err, gc.NotNil)
   883  
   884  	assertSecurityGroups(c, env, []string{"default"})
   885  }
   886  
   887  func assertSecurityGroups(c *gc.C, env environs.Environ, expected []string) {
   888  	neutronClient := openstack.GetNeutronClient(env)
   889  	groups, err := neutronClient.ListSecurityGroupsV2()
   890  	c.Assert(err, jc.ErrorIsNil)
   891  	for _, name := range expected {
   892  		found := false
   893  		for _, group := range groups {
   894  			if group.Name == name {
   895  				found = true
   896  				break
   897  			}
   898  		}
   899  		if !found {
   900  			c.Errorf("expected security group %q not found", name)
   901  		}
   902  	}
   903  	for _, group := range groups {
   904  		found := false
   905  		for _, name := range expected {
   906  			if group.Name == name {
   907  				found = true
   908  				break
   909  			}
   910  		}
   911  		if !found {
   912  			c.Errorf("existing security group %q is not expected", group.Name)
   913  		}
   914  	}
   915  }
   916  
   917  type portAssertion struct {
   918  	SubnetIDs  []string
   919  	NamePrefix string
   920  }
   921  
   922  func assertPorts(c *gc.C, env environs.Environ, expected []portAssertion) {
   923  	neutronClient := openstack.GetNeutronClient(env)
   924  	ports, err := neutronClient.ListPortsV2()
   925  	c.Assert(err, jc.ErrorIsNil)
   926  	c.Assert(ports, gc.HasLen, len(expected))
   927  	for k, port := range ports {
   928  		c.Assert(port.Name, jc.HasPrefix, expected[k].NamePrefix)
   929  		c.Assert(port.FixedIPs, gc.HasLen, len(expected[k].SubnetIDs))
   930  		for i, ip := range port.FixedIPs {
   931  			c.Assert(ip.SubnetID, gc.Equals, expected[k].SubnetIDs[i])
   932  		}
   933  	}
   934  }
   935  
   936  func assertInstanceIds(c *gc.C, env environs.Environ, callCtx context.ProviderCallContext, expected ...instance.Id) {
   937  	allInstances, err := env.AllRunningInstances(callCtx)
   938  	c.Assert(err, jc.ErrorIsNil)
   939  	instIds := make([]instance.Id, len(allInstances))
   940  	for i, inst := range allInstances {
   941  		instIds[i] = inst.Id()
   942  	}
   943  	c.Assert(instIds, jc.SameContents, expected)
   944  }
   945  
   946  func (s *localServerSuite) TestStopInstance(c *gc.C) {
   947  	env := s.openEnviron(c, coretesting.Attrs{"firewall-mode": config.FwInstance})
   948  	instanceName := "100"
   949  	inst, _ := testing.AssertStartInstance(c, env, s.callCtx, s.ControllerUUID, instanceName)
   950  	// Openstack now has three security groups for the server, the default
   951  	// group, one group for the entire environment, and another for the
   952  	// new instance.
   953  	modelUUID := env.Config().UUID()
   954  	allSecurityGroups := []string{
   955  		"default", fmt.Sprintf("juju-%v-%v", s.ControllerUUID, modelUUID),
   956  		fmt.Sprintf("juju-%v-%v-%v", s.ControllerUUID, modelUUID, instanceName),
   957  	}
   958  	assertSecurityGroups(c, env, allSecurityGroups)
   959  	err := env.StopInstances(s.callCtx, inst.Id())
   960  	c.Assert(err, jc.ErrorIsNil)
   961  	// The security group for this instance is now removed.
   962  	assertSecurityGroups(c, env, []string{
   963  		"default", fmt.Sprintf("juju-%v-%v", s.ControllerUUID, modelUUID),
   964  	})
   965  }
   966  
   967  // Due to bug #1300755 it can happen that the security group intended for
   968  // an instance is also used as the common security group of another
   969  // environment. If this is the case, the attempt to delete the instance's
   970  // security group fails but StopInstance succeeds.
   971  func (s *localServerSuite) TestStopInstanceSecurityGroupNotDeleted(c *gc.C) {
   972  	coretesting.SkipIfPPC64EL(c, "lp:1425242")
   973  
   974  	// Force an error when a security group is deleted.
   975  	cleanup := s.srv.Neutron.RegisterControlPoint(
   976  		"removeSecurityGroup",
   977  		func(sc hook.ServiceControl, args ...interface{}) error {
   978  			return fmt.Errorf("failed on purpose")
   979  		},
   980  	)
   981  	defer cleanup()
   982  	env := s.openEnviron(c, coretesting.Attrs{"firewall-mode": config.FwInstance})
   983  	instanceName := "100"
   984  	inst, _ := testing.AssertStartInstance(c, env, s.callCtx, s.ControllerUUID, instanceName)
   985  	modelUUID := env.Config().UUID()
   986  	allSecurityGroups := []string{
   987  		"default", fmt.Sprintf("juju-%v-%v", s.ControllerUUID, modelUUID),
   988  		fmt.Sprintf("juju-%v-%v-%v", s.ControllerUUID, modelUUID, instanceName),
   989  	}
   990  	assertSecurityGroups(c, env, allSecurityGroups)
   991  
   992  	// Make time advance in zero time
   993  	clk := testclock.NewClock(time.Time{})
   994  	clock := testclock.AutoAdvancingClock{Clock: clk, Advance: clk.Advance}
   995  	env.(*openstack.Environ).SetClock(&clock)
   996  
   997  	err := env.StopInstances(s.callCtx, inst.Id())
   998  	c.Assert(err, jc.ErrorIsNil)
   999  	assertSecurityGroups(c, env, allSecurityGroups)
  1000  }
  1001  
  1002  func (s *localServerSuite) TestDestroyEnvironmentDeletesSecurityGroupsFWModeInstance(c *gc.C) {
  1003  	env := s.openEnviron(c, coretesting.Attrs{"firewall-mode": config.FwInstance})
  1004  	instanceName := "100"
  1005  	testing.AssertStartInstance(c, env, s.callCtx, s.ControllerUUID, instanceName)
  1006  	modelUUID := env.Config().UUID()
  1007  	allSecurityGroups := []string{
  1008  		"default", fmt.Sprintf("juju-%v-%v", s.ControllerUUID, modelUUID),
  1009  		fmt.Sprintf("juju-%v-%v-%v", s.ControllerUUID, modelUUID, instanceName),
  1010  	}
  1011  	assertSecurityGroups(c, env, allSecurityGroups)
  1012  	err := env.Destroy(s.callCtx)
  1013  	c.Check(err, jc.ErrorIsNil)
  1014  	assertSecurityGroups(c, env, []string{"default"})
  1015  }
  1016  
  1017  func (s *localServerSuite) TestDestroyEnvironmentDeletesSecurityGroupsFWModeGlobal(c *gc.C) {
  1018  	env := s.openEnviron(c, coretesting.Attrs{"firewall-mode": config.FwGlobal})
  1019  	instanceName := "100"
  1020  	testing.AssertStartInstance(c, env, s.callCtx, s.ControllerUUID, instanceName)
  1021  	modelUUID := env.Config().UUID()
  1022  	allSecurityGroups := []string{
  1023  		"default", fmt.Sprintf("juju-%v-%v", s.ControllerUUID, modelUUID),
  1024  		fmt.Sprintf("juju-%v-%v-global", s.ControllerUUID, modelUUID),
  1025  	}
  1026  	assertSecurityGroups(c, env, allSecurityGroups)
  1027  	err := env.Destroy(s.callCtx)
  1028  	c.Check(err, jc.ErrorIsNil)
  1029  	assertSecurityGroups(c, env, []string{"default"})
  1030  }
  1031  
  1032  func (s *localServerSuite) TestDestroyController(c *gc.C) {
  1033  	env := s.openEnviron(c, coretesting.Attrs{"uuid": utils.MustNewUUID().String()})
  1034  	controllerEnv := s.env
  1035  
  1036  	controllerInstanceName := "100"
  1037  	testing.AssertStartInstance(c, controllerEnv, s.callCtx, s.ControllerUUID, controllerInstanceName)
  1038  	hostedModelInstanceName := "200"
  1039  	testing.AssertStartInstance(c, env, s.callCtx, s.ControllerUUID, hostedModelInstanceName)
  1040  	modelUUID := env.Config().UUID()
  1041  	allControllerSecurityGroups := []string{
  1042  		"default", fmt.Sprintf("juju-%v-%v", s.ControllerUUID, controllerEnv.Config().UUID()),
  1043  		fmt.Sprintf("juju-%v-%v-%v", s.ControllerUUID, controllerEnv.Config().UUID(), controllerInstanceName),
  1044  	}
  1045  	allHostedModelSecurityGroups := []string{
  1046  		"default", fmt.Sprintf("juju-%v-%v", s.ControllerUUID, modelUUID),
  1047  		fmt.Sprintf("juju-%v-%v-%v", s.ControllerUUID, modelUUID, hostedModelInstanceName),
  1048  	}
  1049  	assertSecurityGroups(c, controllerEnv, append(
  1050  		allControllerSecurityGroups, allHostedModelSecurityGroups...,
  1051  	))
  1052  
  1053  	err := controllerEnv.DestroyController(s.callCtx, s.ControllerUUID)
  1054  	c.Check(err, jc.ErrorIsNil)
  1055  	assertSecurityGroups(c, controllerEnv, []string{"default"})
  1056  	assertInstanceIds(c, env, s.callCtx)
  1057  	assertInstanceIds(c, controllerEnv, s.callCtx)
  1058  }
  1059  
  1060  func (s *localServerSuite) TestDestroyHostedModel(c *gc.C) {
  1061  	env := s.openEnviron(c, coretesting.Attrs{"uuid": utils.MustNewUUID().String()})
  1062  	controllerEnv := s.env
  1063  
  1064  	controllerInstanceName := "100"
  1065  	controllerInstance, _ := testing.AssertStartInstance(c, controllerEnv, s.callCtx, s.ControllerUUID, controllerInstanceName)
  1066  	hostedModelInstanceName := "200"
  1067  	testing.AssertStartInstance(c, env, s.callCtx, s.ControllerUUID, hostedModelInstanceName)
  1068  	modelUUID := env.Config().UUID()
  1069  	allControllerSecurityGroups := []string{
  1070  		"default", fmt.Sprintf("juju-%v-%v", s.ControllerUUID, controllerEnv.Config().UUID()),
  1071  		fmt.Sprintf("juju-%v-%v-%v", s.ControllerUUID, controllerEnv.Config().UUID(), controllerInstanceName),
  1072  	}
  1073  	allHostedModelSecurityGroups := []string{
  1074  		"default", fmt.Sprintf("juju-%v-%v", s.ControllerUUID, modelUUID),
  1075  		fmt.Sprintf("juju-%v-%v-%v", s.ControllerUUID, modelUUID, hostedModelInstanceName),
  1076  	}
  1077  	assertSecurityGroups(c, controllerEnv, append(
  1078  		allControllerSecurityGroups, allHostedModelSecurityGroups...,
  1079  	))
  1080  
  1081  	err := env.Destroy(s.callCtx)
  1082  	c.Check(err, jc.ErrorIsNil)
  1083  	assertSecurityGroups(c, controllerEnv, allControllerSecurityGroups)
  1084  	assertInstanceIds(c, env, s.callCtx)
  1085  	assertInstanceIds(c, controllerEnv, s.callCtx, controllerInstance.Id())
  1086  }
  1087  
  1088  func (s *localServerSuite) TestDestroyControllerSpaceConstraints(c *gc.C) {
  1089  	uuid := utils.MustNewUUID().String()
  1090  	env := s.openEnviron(c, coretesting.Attrs{"uuid": uuid})
  1091  	controllerEnv := s.env
  1092  
  1093  	s.srv.Nova.SetAvailabilityZones(
  1094  		nova.AvailabilityZone{
  1095  			Name: "zone-0",
  1096  			State: nova.AvailabilityZoneState{
  1097  				Available: true,
  1098  			},
  1099  		},
  1100  	)
  1101  
  1102  	controllerInstanceName := "100"
  1103  	params := environs.StartInstanceParams{
  1104  		ControllerUUID:   s.ControllerUUID,
  1105  		AvailabilityZone: "zone-0",
  1106  		Constraints:      constraints.MustParse("spaces=space-1 zones=zone-0"),
  1107  		SubnetsToZones: []map[corenetwork.Id][]string{
  1108  			{
  1109  				"999-01": {"zone-0"},
  1110  			},
  1111  		},
  1112  	}
  1113  	_, err := testing.StartInstanceWithParams(env, s.callCtx, controllerInstanceName, params)
  1114  	c.Assert(err, jc.ErrorIsNil)
  1115  	assertPorts(c, env, []portAssertion{
  1116  		{NamePrefix: fmt.Sprintf("juju-%s-", uuid), SubnetIDs: []string{"999-01"}},
  1117  	})
  1118  
  1119  	// The openstack runtime would assign a device_id to a port when it's
  1120  	// assigned to an instance. To ensure that all ports are correctly removed
  1121  	// when destroying and so we can exercise all the code paths we have to
  1122  	// replicate that piece of code.
  1123  	// When moving to mocking of providers, this shouldn't be need or required.
  1124  	s.assignDeviceIdToPort(c, "1", "1")
  1125  
  1126  	err = controllerEnv.DestroyController(s.callCtx, s.ControllerUUID)
  1127  	c.Check(err, jc.ErrorIsNil)
  1128  	assertSecurityGroups(c, controllerEnv, []string{"default"})
  1129  	assertInstanceIds(c, env, s.callCtx)
  1130  	assertInstanceIds(c, controllerEnv, s.callCtx)
  1131  	assertPorts(c, env, []portAssertion{})
  1132  }
  1133  
  1134  func (s *localServerSuite) assignDeviceIdToPort(c *gc.C, portId, deviceId string) {
  1135  	err := s.srv.Nova.AddOSInterface(deviceId, nova.OSInterface{
  1136  		FixedIPs: []nova.PortFixedIP{
  1137  			{
  1138  				IPAddress: "10.0.0.1",
  1139  			},
  1140  		},
  1141  		IPAddress: "10.0.0.1",
  1142  	})
  1143  	c.Assert(err, jc.ErrorIsNil)
  1144  
  1145  	model := s.srv.Neutron.NeutronModel()
  1146  	port, err := model.Port("1")
  1147  	c.Assert(err, jc.ErrorIsNil)
  1148  	err = model.RemovePort("1")
  1149  	c.Assert(err, jc.ErrorIsNil)
  1150  	port.DeviceId = "1"
  1151  	err = model.AddPort(*port)
  1152  	c.Assert(err, jc.ErrorIsNil)
  1153  }
  1154  
  1155  var instanceGathering = []struct {
  1156  	ids []instance.Id
  1157  	err error
  1158  }{
  1159  	{ids: []instance.Id{"id0"}},
  1160  	{ids: []instance.Id{"id0", "id0"}},
  1161  	{ids: []instance.Id{"id0", "id1"}},
  1162  	{ids: []instance.Id{"id1", "id0"}},
  1163  	{ids: []instance.Id{"id1", "id0", "id1"}},
  1164  	{
  1165  		ids: []instance.Id{""},
  1166  		err: environs.ErrNoInstances,
  1167  	},
  1168  	{
  1169  		ids: []instance.Id{"", ""},
  1170  		err: environs.ErrNoInstances,
  1171  	},
  1172  	{
  1173  		ids: []instance.Id{"", "", ""},
  1174  		err: environs.ErrNoInstances,
  1175  	},
  1176  	{
  1177  		ids: []instance.Id{"id0", ""},
  1178  		err: environs.ErrPartialInstances,
  1179  	},
  1180  	{
  1181  		ids: []instance.Id{"", "id1"},
  1182  		err: environs.ErrPartialInstances,
  1183  	},
  1184  	{
  1185  		ids: []instance.Id{"id0", "id1", ""},
  1186  		err: environs.ErrPartialInstances,
  1187  	},
  1188  	{
  1189  		ids: []instance.Id{"id0", "", "id0"},
  1190  		err: environs.ErrPartialInstances,
  1191  	},
  1192  	{
  1193  		ids: []instance.Id{"id0", "id0", ""},
  1194  		err: environs.ErrPartialInstances,
  1195  	},
  1196  	{
  1197  		ids: []instance.Id{"", "id0", "id1"},
  1198  		err: environs.ErrPartialInstances,
  1199  	},
  1200  }
  1201  
  1202  func (s *localServerSuite) TestInstanceStatus(c *gc.C) {
  1203  	// goose's test service always returns ACTIVE state.
  1204  	inst, _ := testing.AssertStartInstance(c, s.env, s.callCtx, s.ControllerUUID, "100")
  1205  	c.Assert(inst.Status(s.callCtx).Status, gc.Equals, status.Running)
  1206  	err := s.env.StopInstances(s.callCtx, inst.Id())
  1207  	c.Assert(err, jc.ErrorIsNil)
  1208  }
  1209  
  1210  func (s *localServerSuite) TestAllRunningInstancesFloatingIP(c *gc.C) {
  1211  	env := s.openEnviron(c, coretesting.Attrs{
  1212  		"network": "private_999",
  1213  	})
  1214  	cons := constraints.MustParse("allocate-public-ip=true")
  1215  	inst0, _ := testing.AssertStartInstanceWithConstraints(c, env, s.callCtx, s.ControllerUUID, "100", cons)
  1216  	inst1, _ := testing.AssertStartInstanceWithConstraints(c, env, s.callCtx, s.ControllerUUID, "101", cons)
  1217  	defer func() {
  1218  		err := env.StopInstances(s.callCtx, inst0.Id(), inst1.Id())
  1219  		c.Assert(err, jc.ErrorIsNil)
  1220  	}()
  1221  
  1222  	allInstances, err := env.AllRunningInstances(s.callCtx)
  1223  	c.Assert(err, jc.ErrorIsNil)
  1224  	for _, inst := range allInstances {
  1225  		c.Assert(*openstack.InstanceFloatingIP(inst), gc.Equals, fmt.Sprintf("10.0.0.%v", inst.Id()))
  1226  	}
  1227  }
  1228  
  1229  func (s *localServerSuite) assertInstancesGathering(c *gc.C, withFloatingIP bool) {
  1230  	env := s.openEnviron(c, coretesting.Attrs{})
  1231  
  1232  	var cons constraints.Value
  1233  	if withFloatingIP {
  1234  		cons = constraints.MustParse("allocate-public-ip=true")
  1235  	}
  1236  	inst0, _ := testing.AssertStartInstanceWithConstraints(c, env, s.callCtx, s.ControllerUUID, "100", cons)
  1237  	id0 := inst0.Id()
  1238  	inst1, _ := testing.AssertStartInstanceWithConstraints(c, env, s.callCtx, s.ControllerUUID, "101", cons)
  1239  	id1 := inst1.Id()
  1240  	defer func() {
  1241  		err := env.StopInstances(s.callCtx, inst0.Id(), inst1.Id())
  1242  		c.Assert(err, jc.ErrorIsNil)
  1243  	}()
  1244  
  1245  	for i, test := range instanceGathering {
  1246  		c.Logf("test %d: find %v -> expect len %d, err: %v", i, test.ids, len(test.ids), test.err)
  1247  		ids := make([]instance.Id, len(test.ids))
  1248  		for j, id := range test.ids {
  1249  			switch id {
  1250  			case "id0":
  1251  				ids[j] = id0
  1252  			case "id1":
  1253  				ids[j] = id1
  1254  			}
  1255  		}
  1256  		insts, err := env.Instances(s.callCtx, ids)
  1257  		c.Assert(err, gc.Equals, test.err)
  1258  		if err == environs.ErrNoInstances {
  1259  			c.Assert(insts, gc.HasLen, 0)
  1260  		} else {
  1261  			c.Assert(insts, gc.HasLen, len(test.ids))
  1262  		}
  1263  		for j, inst := range insts {
  1264  			if ids[j] != "" {
  1265  				c.Assert(inst.Id(), gc.Equals, ids[j])
  1266  				if withFloatingIP {
  1267  					c.Assert(*openstack.InstanceFloatingIP(inst), gc.Equals, fmt.Sprintf("10.0.0.%v", inst.Id()))
  1268  				} else {
  1269  					c.Assert(openstack.InstanceFloatingIP(inst), gc.IsNil)
  1270  				}
  1271  			} else {
  1272  				c.Assert(inst, gc.IsNil)
  1273  			}
  1274  		}
  1275  	}
  1276  }
  1277  
  1278  func (s *localServerSuite) TestInstancesGathering(c *gc.C) {
  1279  	s.assertInstancesGathering(c, false)
  1280  }
  1281  
  1282  func (s *localServerSuite) TestInstancesGatheringWithFloatingIP(c *gc.C) {
  1283  	s.assertInstancesGathering(c, true)
  1284  }
  1285  
  1286  func (s *localServerSuite) TestInstancesShutoffSuspended(c *gc.C) {
  1287  	coretesting.SkipIfPPC64EL(c, "lp:1425242")
  1288  
  1289  	cleanup := s.srv.Nova.RegisterControlPoint(
  1290  		"addServer",
  1291  		func(sc hook.ServiceControl, args ...interface{}) error {
  1292  			details := args[0].(*nova.ServerDetail)
  1293  			switch {
  1294  			case strings.HasSuffix(details.Name, "-100"):
  1295  				details.Status = nova.StatusShutoff
  1296  			case strings.HasSuffix(details.Name, "-101"):
  1297  				details.Status = nova.StatusSuspended
  1298  			default:
  1299  				c.Fatalf("unexpected instance details: %#v", details)
  1300  			}
  1301  			return nil
  1302  		},
  1303  	)
  1304  	defer cleanup()
  1305  	stateInst1, _ := testing.AssertStartInstance(c, s.env, s.callCtx, s.ControllerUUID, "100")
  1306  	stateInst2, _ := testing.AssertStartInstance(c, s.env, s.callCtx, s.ControllerUUID, "101")
  1307  	defer func() {
  1308  		err := s.env.StopInstances(s.callCtx, stateInst1.Id(), stateInst2.Id())
  1309  		c.Assert(err, jc.ErrorIsNil)
  1310  	}()
  1311  
  1312  	twoInstances, err := s.env.Instances(s.callCtx, []instance.Id{stateInst1.Id(), stateInst2.Id()})
  1313  
  1314  	c.Assert(err, jc.ErrorIsNil)
  1315  	c.Assert(twoInstances, gc.HasLen, 2)
  1316  	c.Assert(twoInstances[0].Status(s.callCtx).Message, gc.Equals, nova.StatusShutoff)
  1317  	c.Assert(twoInstances[1].Status(s.callCtx).Message, gc.Equals, nova.StatusSuspended)
  1318  }
  1319  
  1320  func (s *localServerSuite) TestInstancesErrorResponse(c *gc.C) {
  1321  	coretesting.SkipIfPPC64EL(c, "lp:1425242")
  1322  
  1323  	cleanup := s.srv.Nova.RegisterControlPoint(
  1324  		"server",
  1325  		func(sc hook.ServiceControl, args ...interface{}) error {
  1326  			return fmt.Errorf("strange error not instance")
  1327  		},
  1328  	)
  1329  	defer cleanup()
  1330  
  1331  	oneInstance, err := s.env.Instances(s.callCtx, []instance.Id{"1"})
  1332  	c.Check(oneInstance, gc.IsNil)
  1333  	c.Assert(err, gc.ErrorMatches, "(?s).*strange error not instance.*")
  1334  }
  1335  
  1336  func (s *localServerSuite) TestInstancesMultiErrorResponse(c *gc.C) {
  1337  	coretesting.SkipIfPPC64EL(c, "lp:1425242")
  1338  
  1339  	cleanup := s.srv.Nova.RegisterControlPoint(
  1340  		"matchServers",
  1341  		func(sc hook.ServiceControl, args ...interface{}) error {
  1342  			return fmt.Errorf("strange error no instances")
  1343  		},
  1344  	)
  1345  	defer cleanup()
  1346  
  1347  	twoInstances, err := s.env.Instances(s.callCtx, []instance.Id{"1", "2"})
  1348  	c.Check(twoInstances, gc.IsNil)
  1349  	c.Assert(err, gc.ErrorMatches, "(?s).*strange error no instances.*")
  1350  }
  1351  
  1352  // TODO (wallyworld) - this test was copied from the ec2 provider.
  1353  // It should be moved to environs.jujutests.Tests.
  1354  func (s *localServerSuite) TestBootstrapInstanceUserDataAndState(c *gc.C) {
  1355  	err := bootstrapEnv(c, s.env)
  1356  	c.Assert(err, jc.ErrorIsNil)
  1357  
  1358  	// Check that ControllerInstances returns the ID of the bootstrap machine.
  1359  	ids, err := s.env.ControllerInstances(s.callCtx, s.ControllerUUID)
  1360  	c.Assert(err, jc.ErrorIsNil)
  1361  	c.Assert(ids, gc.HasLen, 1)
  1362  
  1363  	allInstances, err := s.env.AllRunningInstances(s.callCtx)
  1364  	c.Assert(err, jc.ErrorIsNil)
  1365  	c.Assert(allInstances, gc.HasLen, 1)
  1366  	c.Check(allInstances[0].Id(), gc.Equals, ids[0])
  1367  
  1368  	addresses, err := allInstances[0].Addresses(s.callCtx)
  1369  	c.Assert(err, jc.ErrorIsNil)
  1370  	c.Assert(addresses, gc.Not(gc.HasLen), 0)
  1371  
  1372  	// TODO(wallyworld) - 2013-03-01 bug=1137005
  1373  	// The nova test double needs to be updated to support retrieving instance userData.
  1374  	// Until then, we can't check the cloud init script was generated correctly.
  1375  	// When we can, we should also check cloudinit for a non-manager node (as in the
  1376  	// ec2 tests).
  1377  }
  1378  
  1379  func (s *localServerSuite) TestGetImageMetadataSources(c *gc.C) {
  1380  	ss := simplestreams.NewSimpleStreams(sstesting.TestDataSourceFactory())
  1381  	// Create a config that matches s.TestConfig but with the specified stream.
  1382  	attrs := coretesting.Attrs{}
  1383  	env := s.openEnviron(c, attrs)
  1384  
  1385  	sources, err := environs.ImageMetadataSources(env, ss)
  1386  	c.Assert(err, jc.ErrorIsNil)
  1387  	c.Assert(sources, gc.HasLen, 3)
  1388  	var urls = make([]string, len(sources))
  1389  	for i, source := range sources {
  1390  		imageURL, err := source.URL("")
  1391  		c.Assert(err, jc.ErrorIsNil)
  1392  		urls[i] = imageURL
  1393  	}
  1394  	// The image-metadata-url ends with "/juju-dist-test/".
  1395  	c.Check(strings.HasSuffix(urls[0], "/juju-dist-test/"), jc.IsTrue)
  1396  	// The product-streams URL ends with "/imagemetadata".
  1397  	c.Check(strings.HasSuffix(urls[1], "/imagemetadata/"), jc.IsTrue)
  1398  	c.Assert(urls[2], jc.HasPrefix, imagemetadata.DefaultUbuntuBaseURL)
  1399  }
  1400  
  1401  func (s *localServerSuite) TestGetImageMetadataSourcesNoProductStreams(c *gc.C) {
  1402  	ss := simplestreams.NewSimpleStreams(sstesting.TestDataSourceFactory())
  1403  	s.PatchValue(openstack.MakeServiceURL, func(client.AuthenticatingClient, string, string, []string) (string, error) {
  1404  		return "", errors.New("cannae do it captain")
  1405  	})
  1406  	env := s.Open(c, stdcontext.TODO(), s.env.Config())
  1407  	sources, err := environs.ImageMetadataSources(env, ss)
  1408  	c.Assert(err, jc.ErrorIsNil)
  1409  	c.Assert(sources, gc.HasLen, 2)
  1410  
  1411  	// Check that data sources are in the right order
  1412  	c.Check(sources[0].Description(), gc.Equals, "image-metadata-url")
  1413  	c.Check(sources[1].Description(), gc.Equals, "default ubuntu cloud images")
  1414  }
  1415  
  1416  func (s *localServerSuite) TestGetToolsMetadataSources(c *gc.C) {
  1417  	ss := simplestreams.NewSimpleStreams(sstesting.TestDataSourceFactory())
  1418  	s.PatchValue(&tools.DefaultBaseURL, "")
  1419  
  1420  	env := s.Open(c, stdcontext.TODO(), s.env.Config())
  1421  	sources, err := tools.GetMetadataSources(env, ss)
  1422  	c.Assert(err, jc.ErrorIsNil)
  1423  	c.Assert(sources, gc.HasLen, 2)
  1424  	var urls = make([]string, len(sources))
  1425  	for i, source := range sources {
  1426  		metadataURL, err := source.URL("")
  1427  		c.Assert(err, jc.ErrorIsNil)
  1428  		urls[i] = metadataURL
  1429  	}
  1430  	// The agent-metadata-url ends with "/juju-dist-test/tools/".
  1431  	c.Check(strings.HasSuffix(urls[0], "/juju-dist-test/tools/"), jc.IsTrue)
  1432  	// Check that the URL from keystone parses.
  1433  	_, err = url.Parse(urls[1])
  1434  	c.Assert(err, jc.ErrorIsNil)
  1435  }
  1436  
  1437  func (s *localServerSuite) TestSupportsNetworking(c *gc.C) {
  1438  	env := s.Open(c, stdcontext.TODO(), s.env.Config())
  1439  	_, ok := environs.SupportsNetworking(env)
  1440  	c.Assert(ok, jc.IsTrue)
  1441  }
  1442  
  1443  func (s *localServerSuite) prepareNetworkingEnviron(c *gc.C, cfg *config.Config) environs.NetworkingEnviron {
  1444  	env := s.Open(c, stdcontext.TODO(), cfg)
  1445  	netenv, supported := environs.SupportsNetworking(env)
  1446  	c.Assert(supported, jc.IsTrue)
  1447  	return netenv
  1448  }
  1449  
  1450  func (s *localServerSuite) TestSubnetsFindAll(c *gc.C) {
  1451  	env := s.prepareNetworkingEnviron(c, s.env.Config())
  1452  	// the environ is opened with network:"private_999" which maps to network id "999"
  1453  	obtainedSubnets, err := env.Subnets(s.callCtx, "", []network.Id{})
  1454  	c.Assert(err, jc.ErrorIsNil)
  1455  	neutronClient := openstack.GetNeutronClient(s.env)
  1456  	openstackSubnets, err := neutronClient.ListSubnetsV2()
  1457  	c.Assert(err, jc.ErrorIsNil)
  1458  
  1459  	obtainedSubnetMap := make(map[network.Id]network.SubnetInfo)
  1460  	for _, sub := range obtainedSubnets {
  1461  		obtainedSubnetMap[sub.ProviderId] = sub
  1462  	}
  1463  
  1464  	expectedSubnetMap := make(map[network.Id]network.SubnetInfo)
  1465  	for _, subnet := range openstackSubnets {
  1466  		if subnet.NetworkId != "999" {
  1467  			continue
  1468  		}
  1469  		net, err := neutronClient.GetNetworkV2(subnet.NetworkId)
  1470  		c.Assert(err, jc.ErrorIsNil)
  1471  		expectedSubnetMap[network.Id(subnet.Id)] = network.SubnetInfo{
  1472  			CIDR:              subnet.Cidr,
  1473  			ProviderId:        network.Id(subnet.Id),
  1474  			ProviderNetworkId: network.Id(subnet.NetworkId),
  1475  			VLANTag:           0,
  1476  			AvailabilityZones: net.AvailabilityZones,
  1477  			ProviderSpaceId:   "",
  1478  		}
  1479  	}
  1480  
  1481  	c.Check(obtainedSubnetMap, jc.DeepEquals, expectedSubnetMap)
  1482  }
  1483  
  1484  func (s *localServerSuite) TestSubnetsFindAllWithExternal(c *gc.C) {
  1485  	cfg := s.env.Config()
  1486  	cfg, err := cfg.Apply(map[string]interface{}{"external-network": "ext-net"})
  1487  	c.Assert(err, jc.ErrorIsNil)
  1488  	env := s.prepareNetworkingEnviron(c, cfg)
  1489  	// private_999 is the internal network, 998 is the external network
  1490  	// the environ is opened with network:"private_999" which maps to network id "999"
  1491  	obtainedSubnets, err := env.Subnets(s.callCtx, "", []network.Id{})
  1492  	c.Assert(err, jc.ErrorIsNil)
  1493  	neutronClient := openstack.GetNeutronClient(s.env)
  1494  	openstackSubnets, err := neutronClient.ListSubnetsV2()
  1495  	c.Assert(err, jc.ErrorIsNil)
  1496  
  1497  	obtainedSubnetMap := make(map[network.Id]network.SubnetInfo)
  1498  	for _, sub := range obtainedSubnets {
  1499  		obtainedSubnetMap[sub.ProviderId] = sub
  1500  	}
  1501  
  1502  	expectedSubnetMap := make(map[network.Id]network.SubnetInfo)
  1503  	for _, subnets := range openstackSubnets {
  1504  		if subnets.NetworkId != "999" && subnets.NetworkId != "998" {
  1505  			continue
  1506  		}
  1507  		net, err := neutronClient.GetNetworkV2(subnets.NetworkId)
  1508  		c.Assert(err, jc.ErrorIsNil)
  1509  		expectedSubnetMap[network.Id(subnets.Id)] = network.SubnetInfo{
  1510  			CIDR:              subnets.Cidr,
  1511  			ProviderId:        network.Id(subnets.Id),
  1512  			ProviderNetworkId: network.Id(subnets.NetworkId),
  1513  			VLANTag:           0,
  1514  			AvailabilityZones: net.AvailabilityZones,
  1515  			ProviderSpaceId:   "",
  1516  		}
  1517  	}
  1518  
  1519  	c.Check(obtainedSubnetMap, jc.DeepEquals, expectedSubnetMap)
  1520  }
  1521  
  1522  func (s *localServerSuite) TestFindNetworksInternal(c *gc.C) {
  1523  	s.testFindNetworks(c, true)
  1524  }
  1525  
  1526  func (s *localServerSuite) TestFindNetworksExternal(c *gc.C) {
  1527  	s.testFindNetworks(c, false)
  1528  }
  1529  
  1530  func (s *localServerSuite) testFindNetworks(c *gc.C, internal bool) {
  1531  	env := s.prepareNetworkingEnviron(c, s.env.Config())
  1532  	obtainedNetworks, err := openstack.FindNetworks(env, internal)
  1533  	c.Assert(err, jc.ErrorIsNil)
  1534  	neutronClient := openstack.GetNeutronClient(s.env)
  1535  	openstackNetworks, err := neutronClient.ListNetworksV2()
  1536  	c.Assert(err, jc.ErrorIsNil)
  1537  
  1538  	expectedNetworks := set.NewStrings()
  1539  	for _, oNet := range openstackNetworks {
  1540  		if oNet.External == internal {
  1541  			continue
  1542  		}
  1543  		expectedNetworks.Add(oNet.Name)
  1544  	}
  1545  
  1546  	c.Check(obtainedNetworks.Values(), jc.SameContents, expectedNetworks.Values())
  1547  
  1548  }
  1549  
  1550  func (s *localServerSuite) TestSubnetsWithMissingSubnet(c *gc.C) {
  1551  	env := s.prepareNetworkingEnviron(c, s.env.Config())
  1552  	subnets, err := env.Subnets(s.callCtx, "", []network.Id{"missing"})
  1553  	c.Assert(err, gc.ErrorMatches, `failed to find the following subnet ids: \[missing\]`)
  1554  	c.Assert(subnets, gc.HasLen, 0)
  1555  }
  1556  
  1557  func (s *localServerSuite) TestSuperSubnets(c *gc.C) {
  1558  	env := s.prepareNetworkingEnviron(c, s.env.Config())
  1559  	obtainedSubnets, err := env.SuperSubnets(s.callCtx)
  1560  	c.Assert(err, jc.ErrorIsNil)
  1561  	neutronClient := openstack.GetNeutronClient(s.env)
  1562  	openstackSubnets, err := neutronClient.ListSubnetsV2()
  1563  	c.Assert(err, jc.ErrorIsNil)
  1564  
  1565  	expectedSubnets := make([]string, 0, len(openstackSubnets))
  1566  	for _, subnets := range openstackSubnets {
  1567  		if subnets.NetworkId != "999" {
  1568  			continue
  1569  		}
  1570  		expectedSubnets = append(expectedSubnets, subnets.Cidr)
  1571  	}
  1572  	sort.Strings(obtainedSubnets)
  1573  	sort.Strings(expectedSubnets)
  1574  	c.Check(obtainedSubnets, jc.DeepEquals, expectedSubnets)
  1575  }
  1576  
  1577  func (s *localServerSuite) TestFindImageBadDefaultImage(c *gc.C) {
  1578  	imagetesting.PatchOfficialDataSources(&s.CleanupSuite, "")
  1579  	env := s.Open(c, stdcontext.TODO(), s.env.Config())
  1580  
  1581  	// An error occurs if no suitable image is found.
  1582  	_, err := openstack.FindInstanceSpec(env, corebase.MakeDefaultBase("ubuntu", "15.04"), "amd64", "mem=1G", nil)
  1583  	c.Assert(err, gc.ErrorMatches, `no metadata for "ubuntu@15.04" images in some-region with arch amd64`)
  1584  }
  1585  
  1586  func (s *localServerSuite) TestConstraintsValidator(c *gc.C) {
  1587  	env := s.Open(c, stdcontext.TODO(), s.env.Config())
  1588  	validator, err := env.ConstraintsValidator(s.callCtx)
  1589  	c.Assert(err, jc.ErrorIsNil)
  1590  	cons := constraints.MustParse("arch=amd64 cpu-power=10 virt-type=lxd")
  1591  	unsupported, err := validator.Validate(cons)
  1592  	c.Assert(err, jc.ErrorIsNil)
  1593  	c.Assert(unsupported, jc.SameContents, []string{"cpu-power"})
  1594  }
  1595  
  1596  func (s *localServerSuite) TestConstraintsValidatorVocab(c *gc.C) {
  1597  	env := s.Open(c, stdcontext.TODO(), s.env.Config())
  1598  	validator, err := env.ConstraintsValidator(s.callCtx)
  1599  	c.Assert(err, jc.ErrorIsNil)
  1600  
  1601  	cons := constraints.MustParse("instance-type=foo")
  1602  	_, err = validator.Validate(cons)
  1603  	c.Assert(err, gc.ErrorMatches, "invalid constraint value: instance-type=foo\nvalid values are:.*")
  1604  
  1605  	cons = constraints.MustParse("virt-type=foo")
  1606  	_, err = validator.Validate(cons)
  1607  	c.Assert(err, gc.ErrorMatches, regexp.QuoteMeta("invalid constraint value: virt-type=foo\nvalid values are: [kvm lxd]"))
  1608  }
  1609  
  1610  func (s *localServerSuite) TestConstraintsMerge(c *gc.C) {
  1611  	env := s.Open(c, stdcontext.TODO(), s.env.Config())
  1612  	validator, err := env.ConstraintsValidator(s.callCtx)
  1613  	c.Assert(err, jc.ErrorIsNil)
  1614  	consA := constraints.MustParse("arch=amd64 mem=1G root-disk=10G")
  1615  	consB := constraints.MustParse("instance-type=m1.small")
  1616  	cons, err := validator.Merge(consA, consB)
  1617  	c.Assert(err, jc.ErrorIsNil)
  1618  	// NOTE: root-disk and instance-type constraints are checked by PrecheckInstance.
  1619  	c.Assert(cons, gc.DeepEquals, constraints.MustParse("arch=amd64 instance-type=m1.small root-disk=10G"))
  1620  }
  1621  
  1622  func (s *localServerSuite) TestFindImageInstanceConstraint(c *gc.C) {
  1623  	env := s.Open(c, stdcontext.TODO(), s.env.Config())
  1624  	imageMetadata := []*imagemetadata.ImageMetadata{{
  1625  		Id:   "image-id",
  1626  		Arch: "amd64",
  1627  	}}
  1628  
  1629  	spec, err := openstack.FindInstanceSpec(
  1630  		env, jujuversion.DefaultSupportedLTSBase(), "amd64", "instance-type=m1.tiny",
  1631  		imageMetadata,
  1632  	)
  1633  	c.Assert(err, jc.ErrorIsNil)
  1634  	c.Assert(spec.InstanceType.Name, gc.Equals, "m1.tiny")
  1635  }
  1636  
  1637  func (s *localServerSuite) TestFindInstanceImageConstraintHypervisor(c *gc.C) {
  1638  	testVirtType := "qemu"
  1639  	env := s.Open(c, stdcontext.TODO(), s.env.Config())
  1640  	imageMetadata := []*imagemetadata.ImageMetadata{{
  1641  		Id:       "image-id",
  1642  		Arch:     "amd64",
  1643  		VirtType: testVirtType,
  1644  	}}
  1645  
  1646  	spec, err := openstack.FindInstanceSpec(
  1647  		env, jujuversion.DefaultSupportedLTSBase(), "amd64", "virt-type="+testVirtType,
  1648  		imageMetadata,
  1649  	)
  1650  	c.Assert(err, jc.ErrorIsNil)
  1651  	c.Assert(spec.InstanceType.VirtType, gc.NotNil)
  1652  	c.Assert(*spec.InstanceType.VirtType, gc.Equals, testVirtType)
  1653  	c.Assert(spec.InstanceType.Name, gc.Equals, "m1.small")
  1654  }
  1655  
  1656  func (s *localServerSuite) TestFindInstanceImageWithHypervisorNoConstraint(c *gc.C) {
  1657  	testVirtType := "qemu"
  1658  	env := s.Open(c, stdcontext.TODO(), s.env.Config())
  1659  	imageMetadata := []*imagemetadata.ImageMetadata{{
  1660  		Id:       "image-id",
  1661  		Arch:     "amd64",
  1662  		VirtType: testVirtType,
  1663  	}}
  1664  
  1665  	spec, err := openstack.FindInstanceSpec(
  1666  		env, jujuversion.DefaultSupportedLTSBase(), "amd64", "",
  1667  		imageMetadata,
  1668  	)
  1669  	c.Assert(err, jc.ErrorIsNil)
  1670  	c.Assert(spec.InstanceType.VirtType, gc.NotNil)
  1671  	c.Assert(*spec.InstanceType.VirtType, gc.Equals, testVirtType)
  1672  	c.Assert(spec.InstanceType.Name, gc.Equals, "m1.small")
  1673  }
  1674  
  1675  func (s *localServerSuite) TestFindInstanceNoConstraint(c *gc.C) {
  1676  	env := s.Open(c, stdcontext.TODO(), s.env.Config())
  1677  	imageMetadata := []*imagemetadata.ImageMetadata{{
  1678  		Id:   "image-id",
  1679  		Arch: "amd64",
  1680  	}}
  1681  
  1682  	spec, err := openstack.FindInstanceSpec(
  1683  		env, jujuversion.DefaultSupportedLTSBase(), "amd64", "",
  1684  		imageMetadata,
  1685  	)
  1686  	c.Assert(err, jc.ErrorIsNil)
  1687  	c.Assert(spec.InstanceType.VirtType, gc.IsNil)
  1688  	c.Assert(spec.InstanceType.Name, gc.Equals, "m1.small")
  1689  }
  1690  
  1691  func (s *localServerSuite) TestFindImageInvalidInstanceConstraint(c *gc.C) {
  1692  	env := s.Open(c, stdcontext.TODO(), s.env.Config())
  1693  	imageMetadata := []*imagemetadata.ImageMetadata{{
  1694  		Id:   "image-id",
  1695  		Arch: "amd64",
  1696  	}}
  1697  	_, err := openstack.FindInstanceSpec(
  1698  		env, jujuversion.DefaultSupportedLTSBase(), "amd64", "instance-type=m1.large",
  1699  		imageMetadata,
  1700  	)
  1701  	c.Assert(err, gc.ErrorMatches, `no instance types in some-region matching constraints "arch=amd64 instance-type=m1.large"`)
  1702  }
  1703  
  1704  func (s *localServerSuite) TestPrecheckInstanceValidInstanceType(c *gc.C) {
  1705  	env := s.Open(c, stdcontext.TODO(), s.env.Config())
  1706  	cons := constraints.MustParse("instance-type=m1.small")
  1707  	err := env.PrecheckInstance(s.callCtx, environs.PrecheckInstanceParams{Base: jujuversion.DefaultSupportedLTSBase(), Constraints: cons})
  1708  	c.Assert(err, jc.ErrorIsNil)
  1709  }
  1710  
  1711  func (s *localServerSuite) TestPrecheckInstanceInvalidInstanceType(c *gc.C) {
  1712  	env := s.Open(c, stdcontext.TODO(), s.env.Config())
  1713  	cons := constraints.MustParse("instance-type=m1.large")
  1714  	err := env.PrecheckInstance(s.callCtx, environs.PrecheckInstanceParams{Base: jujuversion.DefaultSupportedLTSBase(), Constraints: cons})
  1715  	c.Assert(err, gc.ErrorMatches, `invalid Openstack flavour "m1.large" specified`)
  1716  }
  1717  
  1718  func (s *localServerSuite) TestPrecheckInstanceInvalidRootDiskConstraint(c *gc.C) {
  1719  	env := s.Open(c, stdcontext.TODO(), s.env.Config())
  1720  	cons := constraints.MustParse("instance-type=m1.small root-disk=10G")
  1721  	err := env.PrecheckInstance(s.callCtx, environs.PrecheckInstanceParams{Base: jujuversion.DefaultSupportedLTSBase(), Constraints: cons})
  1722  	c.Assert(err, gc.ErrorMatches, `constraint root-disk cannot be specified with instance-type unless constraint root-disk-source=volume`)
  1723  }
  1724  
  1725  func (s *localServerSuite) TestPrecheckInstanceAvailZone(c *gc.C) {
  1726  	placement := "zone=test-available"
  1727  	err := s.env.PrecheckInstance(s.callCtx, environs.PrecheckInstanceParams{Base: jujuversion.DefaultSupportedLTSBase(), Placement: placement})
  1728  	c.Assert(err, jc.ErrorIsNil)
  1729  }
  1730  
  1731  func (s *localServerSuite) TestPrecheckInstanceAvailZoneUnavailable(c *gc.C) {
  1732  	placement := "zone=test-unavailable"
  1733  	err := s.env.PrecheckInstance(s.callCtx, environs.PrecheckInstanceParams{Base: jujuversion.DefaultSupportedLTSBase(), Placement: placement})
  1734  	c.Assert(err, gc.ErrorMatches, `zone "test-unavailable" is unavailable`)
  1735  }
  1736  
  1737  func (s *localServerSuite) TestPrecheckInstanceAvailZoneUnknown(c *gc.C) {
  1738  	placement := "zone=test-unknown"
  1739  	err := s.env.PrecheckInstance(s.callCtx, environs.PrecheckInstanceParams{Base: jujuversion.DefaultSupportedLTSBase(), Placement: placement})
  1740  	c.Assert(err, gc.ErrorMatches, `availability zone "test-unknown" not valid`)
  1741  }
  1742  
  1743  func (s *localServerSuite) TestPrecheckInstanceAvailZonesUnsupported(c *gc.C) {
  1744  	s.srv.Nova.SetAvailabilityZones() // no availability zone support
  1745  	placement := "zone=test-unknown"
  1746  	err := s.env.PrecheckInstance(s.callCtx, environs.PrecheckInstanceParams{Base: jujuversion.DefaultSupportedLTSBase(), Placement: placement})
  1747  	c.Assert(err, jc.Satisfies, errors.IsNotImplemented)
  1748  }
  1749  
  1750  func (s *localServerSuite) TestPrecheckInstanceVolumeAvailZonesNoPlacement(c *gc.C) {
  1751  	s.testPrecheckInstanceVolumeAvailZones(c, "")
  1752  }
  1753  
  1754  func (s *localServerSuite) TestPrecheckInstanceVolumeAvailZonesSameZonePlacement(c *gc.C) {
  1755  	s.testPrecheckInstanceVolumeAvailZones(c, "zone=az1")
  1756  }
  1757  
  1758  func (s *localServerSuite) testPrecheckInstanceVolumeAvailZones(c *gc.C, placement string) {
  1759  	s.srv.Nova.SetAvailabilityZones(
  1760  		nova.AvailabilityZone{
  1761  			Name: "az1",
  1762  			State: nova.AvailabilityZoneState{
  1763  				Available: true,
  1764  			},
  1765  		},
  1766  	)
  1767  
  1768  	_, err := s.storageAdapter.CreateVolume(cinder.CreateVolumeVolumeParams{
  1769  		Size:             123,
  1770  		Name:             "foo",
  1771  		AvailabilityZone: "az1",
  1772  		Metadata: map[string]string{
  1773  			"juju-model-uuid":      coretesting.ModelTag.Id(),
  1774  			"juju-controller-uuid": coretesting.ControllerTag.Id(),
  1775  		},
  1776  	})
  1777  	c.Assert(err, jc.ErrorIsNil)
  1778  
  1779  	err = s.env.PrecheckInstance(s.callCtx, environs.PrecheckInstanceParams{
  1780  		Base:              jujuversion.DefaultSupportedLTSBase(),
  1781  		Placement:         placement,
  1782  		VolumeAttachments: []storage.VolumeAttachmentParams{{VolumeId: "foo"}},
  1783  	})
  1784  	c.Assert(err, jc.ErrorIsNil)
  1785  }
  1786  
  1787  func (s *localServerSuite) TestPrecheckInstanceAvailZonesConflictsVolume(c *gc.C) {
  1788  	s.srv.Nova.SetAvailabilityZones(
  1789  		nova.AvailabilityZone{
  1790  			Name: "az1",
  1791  			State: nova.AvailabilityZoneState{
  1792  				Available: true,
  1793  			},
  1794  		},
  1795  		nova.AvailabilityZone{
  1796  			Name: "az2",
  1797  			State: nova.AvailabilityZoneState{
  1798  				Available: true,
  1799  			},
  1800  		},
  1801  	)
  1802  
  1803  	_, err := s.storageAdapter.CreateVolume(cinder.CreateVolumeVolumeParams{
  1804  		Size:             123,
  1805  		Name:             "foo",
  1806  		AvailabilityZone: "az1",
  1807  		Metadata: map[string]string{
  1808  			"juju-model-uuid":      coretesting.ModelTag.Id(),
  1809  			"juju-controller-uuid": coretesting.ControllerTag.Id(),
  1810  		},
  1811  	})
  1812  	c.Assert(err, jc.ErrorIsNil)
  1813  
  1814  	err = s.env.PrecheckInstance(s.callCtx, environs.PrecheckInstanceParams{
  1815  		Base:              jujuversion.DefaultSupportedLTSBase(),
  1816  		Placement:         "zone=az2",
  1817  		VolumeAttachments: []storage.VolumeAttachmentParams{{VolumeId: "foo"}},
  1818  	})
  1819  	c.Assert(err, gc.ErrorMatches, `cannot create instance with placement "zone=az2": cannot create instance in zone "az2", as this will prevent attaching the requested disks in zone "az1"`)
  1820  }
  1821  
  1822  func (s *localServerSuite) TestDeriveAvailabilityZones(c *gc.C) {
  1823  	placement := "zone=test-available"
  1824  	env := s.env.(common.ZonedEnviron)
  1825  	zones, err := env.DeriveAvailabilityZones(
  1826  		s.callCtx,
  1827  		environs.StartInstanceParams{
  1828  			Placement: placement,
  1829  		})
  1830  	c.Assert(err, jc.ErrorIsNil)
  1831  	c.Assert(zones, gc.DeepEquals, []string{"test-available"})
  1832  }
  1833  
  1834  func (s *localServerSuite) TestDeriveAvailabilityZonesUnavailable(c *gc.C) {
  1835  	placement := "zone=test-unavailable"
  1836  	env := s.env.(common.ZonedEnviron)
  1837  	zones, err := env.DeriveAvailabilityZones(
  1838  		s.callCtx,
  1839  		environs.StartInstanceParams{
  1840  			Placement: placement,
  1841  		})
  1842  	c.Assert(err, gc.ErrorMatches, `zone "test-unavailable" is unavailable`)
  1843  	c.Assert(zones, gc.HasLen, 0)
  1844  }
  1845  
  1846  func (s *localServerSuite) TestDeriveAvailabilityZonesUnknown(c *gc.C) {
  1847  	placement := "zone=test-unknown"
  1848  	env := s.env.(common.ZonedEnviron)
  1849  	zones, err := env.DeriveAvailabilityZones(
  1850  		s.callCtx,
  1851  		environs.StartInstanceParams{
  1852  			Placement: placement,
  1853  		})
  1854  	c.Assert(err, gc.ErrorMatches, `availability zone "test-unknown" not valid`)
  1855  	c.Assert(zones, gc.HasLen, 0)
  1856  }
  1857  
  1858  func (s *localServerSuite) TestDeriveAvailabilityZonesVolumeNoPlacement(c *gc.C) {
  1859  	s.srv.Nova.SetAvailabilityZones(
  1860  		nova.AvailabilityZone{
  1861  			Name: "az1",
  1862  			State: nova.AvailabilityZoneState{
  1863  				Available: false,
  1864  			},
  1865  		},
  1866  		nova.AvailabilityZone{
  1867  			Name: "az2",
  1868  			State: nova.AvailabilityZoneState{
  1869  				Available: true,
  1870  			},
  1871  		},
  1872  	)
  1873  
  1874  	_, err := s.storageAdapter.CreateVolume(cinder.CreateVolumeVolumeParams{
  1875  		Size:             123,
  1876  		Name:             "foo",
  1877  		AvailabilityZone: "az2",
  1878  		Metadata: map[string]string{
  1879  			"juju-model-uuid":      coretesting.ModelTag.Id(),
  1880  			"juju-controller-uuid": coretesting.ControllerTag.Id(),
  1881  		},
  1882  	})
  1883  	c.Assert(err, jc.ErrorIsNil)
  1884  
  1885  	env := s.env.(common.ZonedEnviron)
  1886  	zones, err := env.DeriveAvailabilityZones(
  1887  		s.callCtx,
  1888  		environs.StartInstanceParams{
  1889  			VolumeAttachments: []storage.VolumeAttachmentParams{{VolumeId: "foo"}},
  1890  		})
  1891  	c.Assert(err, jc.ErrorIsNil)
  1892  	c.Assert(zones, gc.DeepEquals, []string{"az2"})
  1893  }
  1894  
  1895  func (s *localServerSuite) TestDeriveAvailabilityZonesConflictsVolume(c *gc.C) {
  1896  	s.srv.Nova.SetAvailabilityZones(
  1897  		nova.AvailabilityZone{
  1898  			Name: "az1",
  1899  			State: nova.AvailabilityZoneState{
  1900  				Available: true,
  1901  			},
  1902  		},
  1903  		nova.AvailabilityZone{
  1904  			Name: "az2",
  1905  			State: nova.AvailabilityZoneState{
  1906  				Available: true,
  1907  			},
  1908  		},
  1909  	)
  1910  
  1911  	_, err := s.storageAdapter.CreateVolume(cinder.CreateVolumeVolumeParams{
  1912  		Size:             123,
  1913  		Name:             "foo",
  1914  		AvailabilityZone: "az1",
  1915  		Metadata: map[string]string{
  1916  			"juju-model-uuid":      coretesting.ModelTag.Id(),
  1917  			"juju-controller-uuid": coretesting.ControllerTag.Id(),
  1918  		},
  1919  	})
  1920  	c.Assert(err, jc.ErrorIsNil)
  1921  
  1922  	env := s.env.(common.ZonedEnviron)
  1923  	zones, err := env.DeriveAvailabilityZones(
  1924  		s.callCtx,
  1925  		environs.StartInstanceParams{
  1926  			Placement:         "zone=az2",
  1927  			VolumeAttachments: []storage.VolumeAttachmentParams{{VolumeId: "foo"}},
  1928  		})
  1929  	c.Assert(err, gc.ErrorMatches, `cannot create instance with placement "zone=az2": cannot create instance in zone "az2", as this will prevent attaching the requested disks in zone "az1"`)
  1930  	c.Assert(zones, gc.HasLen, 0)
  1931  }
  1932  
  1933  func (s *localServerSuite) TestValidateImageMetadata(c *gc.C) {
  1934  	ss := simplestreams.NewSimpleStreams(sstesting.TestDataSourceFactory())
  1935  	env := s.Open(c, stdcontext.TODO(), s.env.Config())
  1936  	params, err := env.(simplestreams.ImageMetadataValidator).ImageMetadataLookupParams("some-region")
  1937  	c.Assert(err, jc.ErrorIsNil)
  1938  	params.Sources, err = environs.ImageMetadataSources(env, ss)
  1939  	c.Assert(err, jc.ErrorIsNil)
  1940  	params.Release = "13.04"
  1941  	imageIDs, _, err := imagemetadata.ValidateImageMetadata(ss, params)
  1942  	c.Assert(err, jc.ErrorIsNil)
  1943  	c.Assert(imageIDs, jc.SameContents, []string{"id-y"})
  1944  }
  1945  
  1946  func (s *localServerSuite) TestImageMetadataSourceOrder(c *gc.C) {
  1947  	ss := simplestreams.NewSimpleStreams(sstesting.TestDataSourceFactory())
  1948  	src := func(env environs.Environ) (simplestreams.DataSource, error) {
  1949  		return ss.NewDataSource(simplestreams.Config{
  1950  			Description:          "my datasource",
  1951  			BaseURL:              "file:///bar",
  1952  			HostnameVerification: false,
  1953  			Priority:             simplestreams.CUSTOM_CLOUD_DATA}), nil
  1954  	}
  1955  	environs.RegisterUserImageDataSourceFunc("my func", src)
  1956  	defer environs.UnregisterImageDataSourceFunc("my func")
  1957  	env := s.Open(c, stdcontext.TODO(), s.env.Config())
  1958  	sources, err := environs.ImageMetadataSources(env, ss)
  1959  	c.Assert(err, jc.ErrorIsNil)
  1960  	var sourceIds []string
  1961  	for _, s := range sources {
  1962  		sourceIds = append(sourceIds, s.Description())
  1963  	}
  1964  	c.Assert(sourceIds, jc.DeepEquals, []string{
  1965  		"image-metadata-url", "my datasource", "keystone catalog", "default ubuntu cloud images"})
  1966  }
  1967  
  1968  // TestEnsureGroup checks that when creating a duplicate security group, the existing group is
  1969  // returned.
  1970  func (s *localServerSuite) TestEnsureGroup(c *gc.C) {
  1971  	group, err := openstack.EnsureGroup(s.env, s.callCtx, "test group", false)
  1972  	c.Assert(err, jc.ErrorIsNil)
  1973  	c.Assert(group.Name, gc.Equals, "test group")
  1974  	id := group.Id
  1975  
  1976  	// Do it again and check that the existing group is returned
  1977  	group, err = openstack.EnsureGroup(s.env, s.callCtx, "test group", false)
  1978  	c.Assert(err, jc.ErrorIsNil)
  1979  	c.Check(group.Id, gc.Equals, id)
  1980  	c.Assert(group.Name, gc.Equals, "test group")
  1981  }
  1982  
  1983  // TestEnsureModelGroup checks that when creating a model security group, the
  1984  // group is created with the correct ingress rules
  1985  func (s *localServerSuite) TestEnsureModelGroup(c *gc.C) {
  1986  	group, err := openstack.EnsureGroup(s.env, s.callCtx, "test model group", true)
  1987  	c.Assert(err, jc.ErrorIsNil)
  1988  	c.Assert(group.Name, gc.Equals, "test model group")
  1989  
  1990  	stringRules := make([]string, 0, len(group.Rules))
  1991  	for _, rule := range group.Rules {
  1992  		// Skip the default Security Group Rules created by Neutron
  1993  		if rule.Direction == "egress" {
  1994  			continue
  1995  		}
  1996  		var minInt int
  1997  		if rule.PortRangeMin != nil {
  1998  			minInt = *rule.PortRangeMin
  1999  		}
  2000  		var maxInt int
  2001  		if rule.PortRangeMax != nil {
  2002  			maxInt = *rule.PortRangeMax
  2003  		}
  2004  		ruleStr := fmt.Sprintf("%s %s %d %d %s %s %s",
  2005  			rule.Direction,
  2006  			*rule.IPProtocol,
  2007  			minInt, maxInt,
  2008  			rule.RemoteIPPrefix,
  2009  			rule.EthernetType,
  2010  			rule.ParentGroupId,
  2011  		)
  2012  		stringRules = append(stringRules, ruleStr)
  2013  	}
  2014  	// We don't care about the ordering, so we sort the result, and compare it.
  2015  	expectedRules := []string{
  2016  		fmt.Sprintf(`ingress tcp 1 65535  IPv6 %s`, group.Id),
  2017  		fmt.Sprintf(`ingress tcp 1 65535  IPv4 %s`, group.Id),
  2018  		fmt.Sprintf(`ingress udp 1 65535  IPv6 %s`, group.Id),
  2019  		fmt.Sprintf(`ingress udp 1 65535  IPv4 %s`, group.Id),
  2020  		fmt.Sprintf(`ingress icmp 0 0  IPv6 %s`, group.Id),
  2021  		fmt.Sprintf(`ingress icmp 0 0  IPv4 %s`, group.Id),
  2022  	}
  2023  	sort.Strings(stringRules)
  2024  	sort.Strings(expectedRules)
  2025  	c.Check(stringRules, gc.DeepEquals, expectedRules)
  2026  }
  2027  
  2028  // TestMatchingGroup checks that you receive the group you expected.  matchingGroup()
  2029  // is used by the firewaller when opening and closing ports.  Unit test in response to bug 1675799.
  2030  func (s *localServerSuite) TestMatchingGroup(c *gc.C) {
  2031  	err := bootstrapEnv(c, s.env)
  2032  	c.Assert(err, jc.ErrorIsNil)
  2033  	group1, err := openstack.EnsureGroup(s.env, s.callCtx,
  2034  		openstack.MachineGroupName(s.env, s.ControllerUUID, "1"), false)
  2035  	c.Assert(err, jc.ErrorIsNil)
  2036  	group2, err := openstack.EnsureGroup(s.env, s.callCtx,
  2037  		openstack.MachineGroupName(s.env, s.ControllerUUID, "2"), false)
  2038  	c.Assert(err, jc.ErrorIsNil)
  2039  	_, err = openstack.EnsureGroup(s.env, s.callCtx, openstack.MachineGroupName(s.env, s.ControllerUUID, "11"), false)
  2040  	c.Assert(err, jc.ErrorIsNil)
  2041  	_, err = openstack.EnsureGroup(s.env, s.callCtx, openstack.MachineGroupName(s.env, s.ControllerUUID, "12"), false)
  2042  	c.Assert(err, jc.ErrorIsNil)
  2043  
  2044  	machineNameRegexp := openstack.MachineGroupRegexp(s.env, "1")
  2045  	groupMatched, err := openstack.MatchingGroup(s.env, s.callCtx, machineNameRegexp)
  2046  	c.Assert(err, jc.ErrorIsNil)
  2047  	c.Assert(group1.Id, gc.Equals, groupMatched.Id)
  2048  
  2049  	machineNameRegexp = openstack.MachineGroupRegexp(s.env, "2")
  2050  	groupMatched, err = openstack.MatchingGroup(s.env, s.callCtx, machineNameRegexp)
  2051  	c.Assert(err, jc.ErrorIsNil)
  2052  	c.Assert(group2.Id, gc.Equals, groupMatched.Id)
  2053  }
  2054  
  2055  // localHTTPSServerSuite contains tests that run against an Openstack service
  2056  // double connected on an HTTPS port with a self-signed certificate. This
  2057  // service is set up and torn down for every test.  This should only test
  2058  // things that depend on the HTTPS connection, all other functional tests on a
  2059  // local connection should be in localServerSuite
  2060  type localHTTPSServerSuite struct {
  2061  	coretesting.BaseSuite
  2062  	attrs   map[string]interface{}
  2063  	cred    *identity.Credentials
  2064  	srv     localServer
  2065  	env     environs.Environ
  2066  	callCtx context.ProviderCallContext
  2067  }
  2068  
  2069  var _ = gc.Suite(&localHTTPSServerSuite{})
  2070  
  2071  func (s *localHTTPSServerSuite) SetUpSuite(c *gc.C) {
  2072  	s.BaseSuite.SetUpSuite(c)
  2073  	overrideCinderProvider(&s.CleanupSuite, &mockAdapter{})
  2074  }
  2075  
  2076  func (s *localHTTPSServerSuite) createConfigAttrs(c *gc.C) map[string]interface{} {
  2077  	attrs := makeTestConfig(s.cred)
  2078  	attrs["agent-version"] = coretesting.FakeVersionNumber.String()
  2079  	attrs["authorized-keys"] = "fakekey"
  2080  	attrs["network"] = "net"
  2081  	// In order to set up and tear down the environment properly, we must
  2082  	// disable hostname verification
  2083  	attrs["ssl-hostname-verification"] = false
  2084  	attrs["auth-url"] = s.cred.URL
  2085  	// Now connect and set up test-local tools and image-metadata URLs
  2086  	cl := client.NewNonValidatingClient(s.cred, identity.AuthUserPass, nil)
  2087  	err := cl.Authenticate()
  2088  	c.Assert(err, jc.ErrorIsNil)
  2089  	containerURL, err := cl.MakeServiceURL("object-store", "", nil)
  2090  	c.Assert(err, jc.ErrorIsNil)
  2091  	c.Check(containerURL[:8], gc.Equals, "https://")
  2092  	attrs["agent-metadata-url"] = containerURL + "/juju-dist-test/tools"
  2093  	c.Logf("Set agent-metadata-url=%q", attrs["agent-metadata-url"])
  2094  	attrs["image-metadata-url"] = containerURL + "/juju-dist-test"
  2095  	c.Logf("Set image-metadata-url=%q", attrs["image-metadata-url"])
  2096  	return attrs
  2097  }
  2098  
  2099  func (s *localHTTPSServerSuite) SetUpTest(c *gc.C) {
  2100  	s.BaseSuite.SetUpTest(c)
  2101  	s.PatchValue(&jujuversion.Current, coretesting.FakeVersionNumber)
  2102  	s.srv.UseTLS = true
  2103  	cred := &identity.Credentials{
  2104  		User:       "fred",
  2105  		Secrets:    "secret",
  2106  		Region:     "some-region",
  2107  		TenantName: "some tenant",
  2108  	}
  2109  	// Note: start() will change cred.URL to point to s.srv.Server.URL
  2110  	s.srv.start(c, cred, newFullOpenstackService)
  2111  	s.cred = cred
  2112  	attrs := s.createConfigAttrs(c)
  2113  	c.Assert(attrs["auth-url"].(string)[:8], gc.Equals, "https://")
  2114  	env, err := bootstrap.PrepareController(
  2115  		false,
  2116  		envtesting.BootstrapTODOContext(c),
  2117  		jujuclient.NewMemStore(),
  2118  		prepareParams(attrs, s.cred),
  2119  	)
  2120  	c.Assert(err, jc.ErrorIsNil)
  2121  	s.env = env.(environs.Environ)
  2122  	s.attrs = s.env.Config().AllAttrs()
  2123  	s.callCtx = context.NewEmptyCloudCallContext()
  2124  }
  2125  
  2126  func (s *localHTTPSServerSuite) TearDownTest(c *gc.C) {
  2127  	if s.env != nil {
  2128  		err := s.env.Destroy(s.callCtx)
  2129  		c.Check(err, jc.ErrorIsNil)
  2130  		s.env = nil
  2131  	}
  2132  	s.srv.stop()
  2133  	s.BaseSuite.TearDownTest(c)
  2134  }
  2135  
  2136  func (s *localHTTPSServerSuite) TestSSLVerify(c *gc.C) {
  2137  	// If you don't have ssl-hostname-verification set to false, and do have
  2138  	// a CA Certificate, then we can connect to the environment. Copy the attrs
  2139  	// used by SetUp and force hostname verification.
  2140  	env := s.envUsingCertificate(c)
  2141  	_, err := env.AllRunningInstances(s.callCtx)
  2142  	c.Assert(err, gc.IsNil)
  2143  }
  2144  
  2145  func (s *localHTTPSServerSuite) TestMustDisableSSLVerify(c *gc.C) {
  2146  	coretesting.SkipIfPPC64EL(c, "lp:1425242")
  2147  
  2148  	// If you don't have ssl-hostname-verification set to false, then we
  2149  	// fail to connect to the environment. Copy the attrs used by SetUp and
  2150  	// force hostname verification.
  2151  	newattrs := make(map[string]interface{}, len(s.attrs))
  2152  	for k, v := range s.attrs {
  2153  		newattrs[k] = v
  2154  	}
  2155  	newattrs["ssl-hostname-verification"] = true
  2156  	cfg, err := config.New(config.NoDefaults, newattrs)
  2157  	c.Assert(err, jc.ErrorIsNil)
  2158  	_, err = environs.New(stdcontext.TODO(), environs.OpenParams{
  2159  		Cloud:  makeCloudSpec(s.cred),
  2160  		Config: cfg,
  2161  	})
  2162  	c.Assert(err, gc.ErrorMatches, "(.|\n)*x509: certificate signed by unknown authority")
  2163  }
  2164  
  2165  func (s *localHTTPSServerSuite) TestCanBootstrap(c *gc.C) {
  2166  	restoreFinishBootstrap := envtesting.DisableFinishBootstrap()
  2167  	defer restoreFinishBootstrap()
  2168  
  2169  	// For testing, we create a storage instance to which is uploaded tools and image metadata.
  2170  	toolsMetadataStorage := openstack.MetadataStorage(s.env)
  2171  	agentURL, err := toolsMetadataStorage.URL("")
  2172  	c.Assert(err, jc.ErrorIsNil)
  2173  	c.Logf("Generating fake tools for: %v", agentURL)
  2174  	envtesting.UploadFakeTools(c, toolsMetadataStorage, s.env.Config().AgentStream(), s.env.Config().AgentStream())
  2175  	defer envtesting.RemoveFakeTools(c, toolsMetadataStorage, s.env.Config().AgentStream())
  2176  
  2177  	imageMetadataStorage := openstack.ImageMetadataStorage(s.env)
  2178  	c.Logf("Generating fake images")
  2179  	openstack.UseTestImageData(imageMetadataStorage, s.cred)
  2180  	defer openstack.RemoveTestImageData(imageMetadataStorage)
  2181  
  2182  	err = bootstrapEnv(c, s.env)
  2183  	c.Assert(err, jc.ErrorIsNil)
  2184  }
  2185  
  2186  func (s *localHTTPSServerSuite) TestFetchFromImageMetadataSources(c *gc.C) {
  2187  	ss := simplestreams.NewSimpleStreams(sstesting.TestSkipVerifyDataSourceFactory())
  2188  	// Setup a custom URL for image metadata
  2189  	customStorage := openstack.CreateCustomStorage(s.env, "custom-metadata")
  2190  	customURL, err := customStorage.URL("")
  2191  	c.Assert(err, jc.ErrorIsNil)
  2192  	c.Check(customURL[:8], gc.Equals, "https://")
  2193  
  2194  	envConfig, err := s.env.Config().Apply(
  2195  		map[string]interface{}{"image-metadata-url": customURL},
  2196  	)
  2197  	c.Assert(err, jc.ErrorIsNil)
  2198  	err = s.env.SetConfig(envConfig)
  2199  	c.Assert(err, jc.ErrorIsNil)
  2200  	sources, err := environs.ImageMetadataSources(s.env, ss)
  2201  	c.Assert(err, jc.ErrorIsNil)
  2202  	c.Assert(sources, gc.HasLen, 3)
  2203  
  2204  	// Make sure there is something to download from each location
  2205  	metadata := "metadata-content"
  2206  	metadataStorage := openstack.ImageMetadataStorage(s.env)
  2207  	err = metadataStorage.Put(metadata, bytes.NewBufferString(metadata), int64(len(metadata)))
  2208  	c.Assert(err, jc.ErrorIsNil)
  2209  
  2210  	custom := "custom-content"
  2211  	err = customStorage.Put(custom, bytes.NewBufferString(custom), int64(len(custom)))
  2212  	c.Assert(err, jc.ErrorIsNil)
  2213  
  2214  	// Produce map of data sources keyed on description
  2215  	mappedSources := make(map[string]simplestreams.DataSource, len(sources))
  2216  	for i, s := range sources {
  2217  		c.Logf("datasource %d: %+v", i, s)
  2218  		mappedSources[s.Description()] = s
  2219  	}
  2220  
  2221  	// Read from the Config entry's image-metadata-url
  2222  	contentReader, imageURL, err := mappedSources["image-metadata-url"].Fetch(custom)
  2223  	c.Assert(err, jc.ErrorIsNil)
  2224  	defer func() { _ = contentReader.Close() }()
  2225  	content, err := io.ReadAll(contentReader)
  2226  	c.Assert(err, jc.ErrorIsNil)
  2227  	c.Assert(string(content), gc.Equals, custom)
  2228  	c.Check(imageURL[:8], gc.Equals, "https://")
  2229  
  2230  	// Check the entry we got from keystone
  2231  	contentReader, imageURL, err = mappedSources["keystone catalog"].Fetch(metadata)
  2232  	c.Assert(err, jc.ErrorIsNil)
  2233  	defer func() { _ = contentReader.Close() }()
  2234  	content, err = io.ReadAll(contentReader)
  2235  	c.Assert(err, jc.ErrorIsNil)
  2236  	c.Assert(string(content), gc.Equals, metadata)
  2237  	c.Check(imageURL[:8], gc.Equals, "https://")
  2238  	// Verify that we are pointing at exactly where metadataStorage thinks we are
  2239  	metaURL, err := metadataStorage.URL(metadata)
  2240  	c.Assert(err, jc.ErrorIsNil)
  2241  	c.Check(imageURL, gc.Equals, metaURL)
  2242  }
  2243  
  2244  func (s *localHTTPSServerSuite) TestFetchFromImageMetadataSourcesWithCertificate(c *gc.C) {
  2245  	ss := simplestreams.NewSimpleStreams(sstesting.TestDataSourceFactory())
  2246  	env := s.envUsingCertificate(c)
  2247  
  2248  	// Setup a custom URL for image metadata
  2249  	customStorage := openstack.CreateCustomStorage(env, "custom-metadata")
  2250  	customURL, err := customStorage.URL("")
  2251  	c.Assert(err, jc.ErrorIsNil)
  2252  	c.Check(customURL[:8], gc.Equals, "https://")
  2253  
  2254  	envConfig, err := env.Config().Apply(
  2255  		map[string]interface{}{"image-metadata-url": customURL},
  2256  	)
  2257  	c.Assert(err, jc.ErrorIsNil)
  2258  	err = env.SetConfig(envConfig)
  2259  	c.Assert(err, jc.ErrorIsNil)
  2260  	sources, err := environs.ImageMetadataSources(env, ss)
  2261  	c.Assert(err, jc.ErrorIsNil)
  2262  	c.Assert(sources, gc.HasLen, 3)
  2263  
  2264  	// Make sure there is something to download from each location
  2265  	metadata := "metadata-content"
  2266  	metadataStorage := openstack.ImageMetadataStorage(env)
  2267  	err = metadataStorage.Put(metadata, bytes.NewBufferString(metadata), int64(len(metadata)))
  2268  	c.Assert(err, jc.ErrorIsNil)
  2269  
  2270  	custom := "custom-content"
  2271  	err = customStorage.Put(custom, bytes.NewBufferString(custom), int64(len(custom)))
  2272  	c.Assert(err, jc.ErrorIsNil)
  2273  
  2274  	// Produce map of data sources keyed on description
  2275  	mappedSources := make(map[string]simplestreams.DataSource, len(sources))
  2276  	for i, s := range sources {
  2277  		c.Logf("datasource %d: %+v", i, s)
  2278  		mappedSources[s.Description()] = s
  2279  	}
  2280  
  2281  	// Check the entry we got from keystone
  2282  	contentReader, imageURL, err := mappedSources["keystone catalog"].Fetch(metadata)
  2283  	c.Assert(err, jc.ErrorIsNil)
  2284  	defer func() { _ = contentReader.Close() }()
  2285  	content, err := io.ReadAll(contentReader)
  2286  	c.Assert(err, jc.ErrorIsNil)
  2287  	c.Assert(string(content), gc.Equals, metadata)
  2288  	c.Check(imageURL[:8], gc.Equals, "https://")
  2289  
  2290  	// Verify that we are pointing at exactly where metadataStorage thinks we are
  2291  	metaURL, err := metadataStorage.URL(metadata)
  2292  	c.Assert(err, jc.ErrorIsNil)
  2293  	c.Check(imageURL, gc.Equals, metaURL)
  2294  }
  2295  
  2296  func (s *localHTTPSServerSuite) TestFetchFromToolsMetadataSources(c *gc.C) {
  2297  	ss := simplestreams.NewSimpleStreams(sstesting.TestSkipVerifyDataSourceFactory())
  2298  	// Setup a custom URL for image metadata
  2299  	customStorage := openstack.CreateCustomStorage(s.env, "custom-tools-metadata")
  2300  	customURL, err := customStorage.URL("")
  2301  	c.Assert(err, jc.ErrorIsNil)
  2302  	c.Check(customURL[:8], gc.Equals, "https://")
  2303  
  2304  	envConfig, err := s.env.Config().Apply(
  2305  		map[string]interface{}{"agent-metadata-url": customURL},
  2306  	)
  2307  	c.Assert(err, jc.ErrorIsNil)
  2308  	err = s.env.SetConfig(envConfig)
  2309  	c.Assert(err, jc.ErrorIsNil)
  2310  	sources, err := tools.GetMetadataSources(s.env, ss)
  2311  	c.Assert(err, jc.ErrorIsNil)
  2312  	c.Assert(sources, gc.HasLen, 3)
  2313  
  2314  	// Make sure there is something to download from each location
  2315  
  2316  	keystone := "keystone-tools-content"
  2317  	// The keystone entry just points at the root of the Swift storage, and
  2318  	// we have to create a container to upload any data. So we just point
  2319  	// into a subdirectory for the data we are downloading
  2320  	keystoneContainer := "tools-test"
  2321  	keystoneStorage := openstack.CreateCustomStorage(s.env, "tools-test")
  2322  	err = keystoneStorage.Put(keystone, bytes.NewBufferString(keystone), int64(len(keystone)))
  2323  	c.Assert(err, jc.ErrorIsNil)
  2324  
  2325  	custom := "custom-tools-content"
  2326  	err = customStorage.Put(custom, bytes.NewBufferString(custom), int64(len(custom)))
  2327  	c.Assert(err, jc.ErrorIsNil)
  2328  
  2329  	// Read from the Config entry's agent-metadata-url
  2330  	contentReader, metadataURL, err := sources[0].Fetch(custom)
  2331  	c.Assert(err, jc.ErrorIsNil)
  2332  	defer func() { _ = contentReader.Close() }()
  2333  	content, err := io.ReadAll(contentReader)
  2334  	c.Assert(err, jc.ErrorIsNil)
  2335  	c.Assert(string(content), gc.Equals, custom)
  2336  	c.Check(metadataURL[:8], gc.Equals, "https://")
  2337  
  2338  	// Check the entry we got from keystone
  2339  	// Now fetch the data, and verify the contents.
  2340  	contentReader, metadataURL, err = sources[1].Fetch(keystoneContainer + "/" + keystone)
  2341  	c.Assert(err, jc.ErrorIsNil)
  2342  	defer func() { _ = contentReader.Close() }()
  2343  	content, err = io.ReadAll(contentReader)
  2344  	c.Assert(err, jc.ErrorIsNil)
  2345  	c.Assert(string(content), gc.Equals, keystone)
  2346  	c.Check(metadataURL[:8], gc.Equals, "https://")
  2347  	keystoneURL, err := keystoneStorage.URL(keystone)
  2348  	c.Assert(err, jc.ErrorIsNil)
  2349  	c.Check(metadataURL, gc.Equals, keystoneURL)
  2350  
  2351  	// We *don't* test Fetch for sources[3] because it points to
  2352  	// streams.canonical.com
  2353  }
  2354  
  2355  func (s *localServerSuite) TestRemoveBlankContainer(c *gc.C) {
  2356  	containerStorage := openstack.BlankContainerStorage()
  2357  	err := containerStorage.Remove("some-file")
  2358  	c.Assert(err, gc.ErrorMatches, `cannot remove "some-file": swift container name is empty`)
  2359  }
  2360  
  2361  func (s *localServerSuite) TestAllRunningInstancesIgnoresOtherMachines(c *gc.C) {
  2362  	err := bootstrapEnv(c, s.env)
  2363  	c.Assert(err, jc.ErrorIsNil)
  2364  
  2365  	// Check that we see 1 instance in the environment
  2366  	insts, err := s.env.AllRunningInstances(s.callCtx)
  2367  	c.Assert(err, jc.ErrorIsNil)
  2368  	c.Check(insts, gc.HasLen, 1)
  2369  
  2370  	// Now start a machine 'manually' in the same account, with a similar
  2371  	// but not matching name, and ensure it isn't seen by AllRunningInstances
  2372  	// See bug #1257481, for how similar names were causing them to get
  2373  	// listed (and thus destroyed) at the wrong time
  2374  	existingModelName := s.TestConfig["name"]
  2375  	newMachineName := fmt.Sprintf("juju-%s-2-machine-0", existingModelName)
  2376  
  2377  	// We grab the Nova client directly from the env, just to save time
  2378  	// looking all the stuff up
  2379  	novaClient := openstack.GetNovaClient(s.env)
  2380  	entity, err := novaClient.RunServer(nova.RunServerOpts{
  2381  		Name:     newMachineName,
  2382  		FlavorId: "1", // test service has 1,2,3 for flavor ids
  2383  		ImageId:  "1", // UseTestImageData sets up images 1 and 2
  2384  		Networks: []nova.ServerNetworks{{NetworkId: "1"}},
  2385  	})
  2386  	c.Assert(err, jc.ErrorIsNil)
  2387  	c.Assert(entity, gc.NotNil)
  2388  
  2389  	// List all servers with no filter, we should see both instances
  2390  	servers, err := novaClient.ListServersDetail(nova.NewFilter())
  2391  	c.Assert(err, jc.ErrorIsNil)
  2392  	c.Assert(servers, gc.HasLen, 2)
  2393  
  2394  	insts, err = s.env.AllRunningInstances(s.callCtx)
  2395  	c.Assert(err, jc.ErrorIsNil)
  2396  	c.Check(insts, gc.HasLen, 1)
  2397  }
  2398  
  2399  func (s *localServerSuite) TestResolveNetworkUUID(c *gc.C) {
  2400  	var sampleUUID = "f81d4fae-7dec-11d0-a765-00a0c91e6bf6"
  2401  
  2402  	err := s.srv.Neutron.NeutronModel().AddNetwork(neutron.NetworkV2{Id: sampleUUID})
  2403  	c.Assert(err, jc.ErrorIsNil)
  2404  
  2405  	networkIDs, err := openstack.ResolveNetworkIDs(s.env, sampleUUID, false)
  2406  	c.Assert(err, jc.ErrorIsNil)
  2407  	c.Assert(networkIDs, gc.DeepEquals, []string{sampleUUID})
  2408  }
  2409  
  2410  func (s *localServerSuite) TestResolveNetworkLabel(c *gc.C) {
  2411  	// For now this test has to cheat and use knowledge of goose internals
  2412  	var networkLabel = "net"
  2413  	var expectNetworkIDs = []string{"1"}
  2414  	networkIDs, err := openstack.ResolveNetworkIDs(s.env, networkLabel, false)
  2415  	c.Assert(err, jc.ErrorIsNil)
  2416  	c.Assert(networkIDs, gc.DeepEquals, expectNetworkIDs)
  2417  }
  2418  
  2419  func (s *localServerSuite) TestResolveNetworkLabelMultiple(c *gc.C) {
  2420  	var networkLabel = "multi"
  2421  
  2422  	err := s.srv.Neutron.NeutronModel().AddNetwork(neutron.NetworkV2{
  2423  		Id:   "multi-666",
  2424  		Name: networkLabel,
  2425  	})
  2426  	c.Assert(err, jc.ErrorIsNil)
  2427  
  2428  	err = s.srv.Neutron.NeutronModel().AddNetwork(neutron.NetworkV2{
  2429  		Id:   "multi-999",
  2430  		Name: networkLabel,
  2431  	})
  2432  	c.Assert(err, jc.ErrorIsNil)
  2433  
  2434  	var expectNetworkIDs = []string{"multi-666", "multi-999"}
  2435  	networkIDs, err := openstack.ResolveNetworkIDs(s.env, networkLabel, false)
  2436  	c.Assert(err, jc.ErrorIsNil)
  2437  	c.Assert(networkIDs, jc.SameContents, expectNetworkIDs)
  2438  }
  2439  
  2440  func (s *localServerSuite) TestResolveNetworkNotPresent(c *gc.C) {
  2441  	networkIDs, err := openstack.ResolveNetworkIDs(s.env, "no-network-with-this-label", false)
  2442  	c.Assert(err, jc.ErrorIsNil)
  2443  	c.Check(networkIDs, gc.HasLen, 0)
  2444  }
  2445  
  2446  func (s *localServerSuite) TestStartInstanceAvailZone(c *gc.C) {
  2447  	inst, err := s.testStartInstanceAvailZone(c, "test-available")
  2448  	c.Assert(err, jc.ErrorIsNil)
  2449  	c.Assert(openstack.InstanceServerDetail(inst).AvailabilityZone, gc.Equals, "test-available")
  2450  }
  2451  
  2452  func (s *localServerSuite) TestStartInstanceAvailZoneUnavailable(c *gc.C) {
  2453  	_, err := s.testStartInstanceAvailZone(c, "test-unavailable")
  2454  	c.Assert(errors.Is(err, environs.ErrAvailabilityZoneIndependent), jc.IsFalse)
  2455  }
  2456  
  2457  func (s *localServerSuite) TestStartInstanceAvailZoneUnknown(c *gc.C) {
  2458  	_, err := s.testStartInstanceAvailZone(c, "test-unknown")
  2459  	c.Assert(errors.Is(err, environs.ErrAvailabilityZoneIndependent), jc.IsFalse)
  2460  }
  2461  
  2462  func (s *localServerSuite) testStartInstanceAvailZone(c *gc.C, zone string) (instances.Instance, error) {
  2463  	err := bootstrapEnv(c, s.env)
  2464  	c.Assert(err, jc.ErrorIsNil)
  2465  
  2466  	params := environs.StartInstanceParams{
  2467  		ControllerUUID:   s.ControllerUUID,
  2468  		AvailabilityZone: zone,
  2469  	}
  2470  	result, err := testing.StartInstanceWithParams(s.env, s.callCtx, "1", params)
  2471  	if err != nil {
  2472  		return nil, err
  2473  	}
  2474  	return result.Instance, nil
  2475  }
  2476  
  2477  func (s *localServerSuite) TestGetAvailabilityZones(c *gc.C) {
  2478  	var resultZones []nova.AvailabilityZone
  2479  	var resultErr error
  2480  	s.PatchValue(openstack.NovaListAvailabilityZones, func(c *nova.Client) ([]nova.AvailabilityZone, error) {
  2481  		return append([]nova.AvailabilityZone{}, resultZones...), resultErr
  2482  	})
  2483  	env := s.env.(common.ZonedEnviron)
  2484  
  2485  	resultErr = fmt.Errorf("failed to get availability zones")
  2486  	zones, err := env.AvailabilityZones(s.callCtx)
  2487  	c.Assert(err, gc.Equals, resultErr)
  2488  	c.Assert(zones, gc.IsNil)
  2489  
  2490  	resultErr = nil
  2491  	resultZones = make([]nova.AvailabilityZone, 1)
  2492  	resultZones[0].Name = "whatever"
  2493  	zones, err = env.AvailabilityZones(s.callCtx)
  2494  	c.Assert(err, jc.ErrorIsNil)
  2495  	c.Assert(zones, gc.HasLen, 1)
  2496  	c.Assert(zones[0].Name(), gc.Equals, "whatever")
  2497  }
  2498  
  2499  func (s *localServerSuite) TestGetAvailabilityZonesCommon(c *gc.C) {
  2500  	var resultZones []nova.AvailabilityZone
  2501  	s.PatchValue(openstack.NovaListAvailabilityZones, func(c *nova.Client) ([]nova.AvailabilityZone, error) {
  2502  		return append([]nova.AvailabilityZone{}, resultZones...), nil
  2503  	})
  2504  	env := s.env.(common.ZonedEnviron)
  2505  	resultZones = make([]nova.AvailabilityZone, 2)
  2506  	resultZones[0].Name = "az1"
  2507  	resultZones[1].Name = "az2"
  2508  	resultZones[0].State.Available = true
  2509  	resultZones[1].State.Available = false
  2510  	zones, err := env.AvailabilityZones(s.callCtx)
  2511  	c.Assert(err, jc.ErrorIsNil)
  2512  	c.Assert(zones, gc.HasLen, 2)
  2513  	c.Assert(zones[0].Name(), gc.Equals, resultZones[0].Name)
  2514  	c.Assert(zones[1].Name(), gc.Equals, resultZones[1].Name)
  2515  	c.Assert(zones[0].Available(), jc.IsTrue)
  2516  	c.Assert(zones[1].Available(), jc.IsFalse)
  2517  }
  2518  
  2519  func (s *localServerSuite) TestStartInstanceWithUnknownAZError(c *gc.C) {
  2520  	coretesting.SkipIfPPC64EL(c, "lp:1425242")
  2521  
  2522  	s.srv.Nova.SetAvailabilityZones(
  2523  		// bootstrap node will be on az1.
  2524  		nova.AvailabilityZone{
  2525  			Name: "az1",
  2526  			State: nova.AvailabilityZoneState{
  2527  				Available: true,
  2528  			},
  2529  		},
  2530  		// az2 will be made to return an unknown error.
  2531  		nova.AvailabilityZone{
  2532  			Name: "az2",
  2533  			State: nova.AvailabilityZoneState{
  2534  				Available: true,
  2535  			},
  2536  		},
  2537  	)
  2538  
  2539  	err := bootstrapEnv(c, s.env)
  2540  	c.Assert(err, jc.ErrorIsNil)
  2541  
  2542  	cleanup := s.srv.Nova.RegisterControlPoint(
  2543  		"addServer",
  2544  		func(sc hook.ServiceControl, args ...interface{}) error {
  2545  			serverDetail := args[0].(*nova.ServerDetail)
  2546  			if serverDetail.AvailabilityZone == "az2" {
  2547  				return fmt.Errorf("some unknown error")
  2548  			}
  2549  			return nil
  2550  		},
  2551  	)
  2552  	defer cleanup()
  2553  	_, err = testing.StartInstanceWithParams(s.env, s.callCtx, "1", environs.StartInstanceParams{
  2554  		ControllerUUID:   s.ControllerUUID,
  2555  		AvailabilityZone: "az2",
  2556  	})
  2557  	c.Assert(err, gc.ErrorMatches, "(?s).*some unknown error.*")
  2558  }
  2559  
  2560  func (s *localServerSuite) testStartInstanceWithParamsDeriveAZ(
  2561  	machineId string,
  2562  	params environs.StartInstanceParams,
  2563  ) (*environs.StartInstanceResult, error) {
  2564  	zonedEnv := s.env.(common.ZonedEnviron)
  2565  	zones, err := zonedEnv.DeriveAvailabilityZones(s.callCtx, params)
  2566  	if err != nil {
  2567  		return nil, err
  2568  	}
  2569  	if len(zones) < 1 {
  2570  		return nil, errors.New("no zones found")
  2571  	}
  2572  	params.AvailabilityZone = zones[0]
  2573  	return testing.StartInstanceWithParams(s.env, s.callCtx, "1", params)
  2574  }
  2575  
  2576  func (s *localServerSuite) TestStartInstanceVolumeAttachmentsAvailZone(c *gc.C) {
  2577  	s.srv.Nova.SetAvailabilityZones(
  2578  		nova.AvailabilityZone{
  2579  			Name: "az1",
  2580  			State: nova.AvailabilityZoneState{
  2581  				Available: true,
  2582  			},
  2583  		},
  2584  		nova.AvailabilityZone{
  2585  			Name: "az2",
  2586  			State: nova.AvailabilityZoneState{
  2587  				Available: true,
  2588  			},
  2589  		},
  2590  		nova.AvailabilityZone{
  2591  			Name: "test-available",
  2592  			State: nova.AvailabilityZoneState{
  2593  				Available: true,
  2594  			},
  2595  		},
  2596  	)
  2597  	err := bootstrapEnv(c, s.env)
  2598  	c.Assert(err, jc.ErrorIsNil)
  2599  
  2600  	_, err = s.storageAdapter.CreateVolume(cinder.CreateVolumeVolumeParams{
  2601  		Size:             123,
  2602  		Name:             "foo",
  2603  		AvailabilityZone: "az2",
  2604  		Metadata: map[string]string{
  2605  			"juju-model-uuid":      coretesting.ModelTag.Id(),
  2606  			"juju-controller-uuid": coretesting.ControllerTag.Id(),
  2607  		},
  2608  	})
  2609  	c.Assert(err, jc.ErrorIsNil)
  2610  	result, err := s.testStartInstanceWithParamsDeriveAZ("1", environs.StartInstanceParams{
  2611  		ControllerUUID: s.ControllerUUID,
  2612  		VolumeAttachments: []storage.VolumeAttachmentParams{
  2613  			{VolumeId: "foo"},
  2614  		},
  2615  	})
  2616  	c.Assert(err, jc.ErrorIsNil)
  2617  	c.Assert(openstack.InstanceServerDetail(result.Instance).AvailabilityZone, gc.Equals, "az2")
  2618  }
  2619  
  2620  func (s *localServerSuite) TestStartInstanceVolumeAttachmentsMultipleAvailZones(c *gc.C) {
  2621  	err := bootstrapEnv(c, s.env)
  2622  	c.Assert(err, jc.ErrorIsNil)
  2623  
  2624  	for _, az := range []string{"az1", "az2"} {
  2625  		_, err := s.storageAdapter.CreateVolume(cinder.CreateVolumeVolumeParams{
  2626  			Size:             123,
  2627  			Name:             "vol-" + az,
  2628  			AvailabilityZone: az,
  2629  			Metadata: map[string]string{
  2630  				"juju-model-uuid":      coretesting.ModelTag.Id(),
  2631  				"juju-controller-uuid": coretesting.ControllerTag.Id(),
  2632  			},
  2633  		})
  2634  		c.Assert(err, jc.ErrorIsNil)
  2635  	}
  2636  
  2637  	_, err = s.testStartInstanceWithParamsDeriveAZ("1", environs.StartInstanceParams{
  2638  		ControllerUUID: s.ControllerUUID,
  2639  		VolumeAttachments: []storage.VolumeAttachmentParams{
  2640  			{VolumeId: "vol-az1"},
  2641  			{VolumeId: "vol-az2"},
  2642  		},
  2643  	})
  2644  	c.Assert(err, gc.ErrorMatches, `cannot attach volumes from multiple availability zones: vol-az1 is in az1, vol-az2 is in az2`)
  2645  }
  2646  
  2647  func (s *localServerSuite) TestStartInstanceVolumeAttachmentsAvailZoneConflictsPlacement(c *gc.C) {
  2648  	err := bootstrapEnv(c, s.env)
  2649  	c.Assert(err, jc.ErrorIsNil)
  2650  
  2651  	s.srv.Nova.SetAvailabilityZones(
  2652  		nova.AvailabilityZone{
  2653  			Name: "az1",
  2654  			State: nova.AvailabilityZoneState{
  2655  				Available: true,
  2656  			},
  2657  		},
  2658  		nova.AvailabilityZone{
  2659  			Name: "az2",
  2660  			State: nova.AvailabilityZoneState{
  2661  				Available: true,
  2662  			},
  2663  		},
  2664  	)
  2665  	_, err = s.storageAdapter.CreateVolume(cinder.CreateVolumeVolumeParams{
  2666  		Size:             123,
  2667  		Name:             "foo",
  2668  		AvailabilityZone: "az1",
  2669  		Metadata: map[string]string{
  2670  			"juju-model-uuid":      coretesting.ModelTag.Id(),
  2671  			"juju-controller-uuid": coretesting.ControllerTag.Id(),
  2672  		},
  2673  	})
  2674  	c.Assert(err, jc.ErrorIsNil)
  2675  
  2676  	_, err = testing.StartInstanceWithParams(s.env, s.callCtx, "1", environs.StartInstanceParams{
  2677  		ControllerUUID:    s.ControllerUUID,
  2678  		VolumeAttachments: []storage.VolumeAttachmentParams{{VolumeId: "foo"}},
  2679  		AvailabilityZone:  "az2",
  2680  	})
  2681  	c.Assert(err, gc.ErrorMatches, `cannot create instance in zone "az2", as this will prevent attaching the requested disks in zone "az1"`)
  2682  }
  2683  
  2684  // novaInstaceStartedWithOpts exposes run server options used to start an instance.
  2685  type novaInstaceStartedWithOpts interface {
  2686  	NovaInstanceStartedWithOpts() *nova.RunServerOpts
  2687  }
  2688  
  2689  func (s *localServerSuite) TestStartInstanceWithImageIDConstraint(c *gc.C) {
  2690  	env := s.ensureAMDImages(c)
  2691  
  2692  	err := bootstrapEnv(c, env)
  2693  	c.Assert(err, jc.ErrorIsNil)
  2694  
  2695  	cons, err := constraints.Parse("image-id=ubuntu-bf2")
  2696  	c.Assert(err, jc.ErrorIsNil)
  2697  
  2698  	res, err := testing.StartInstanceWithParams(env, s.callCtx, "1", environs.StartInstanceParams{
  2699  		ControllerUUID: s.ControllerUUID,
  2700  		Constraints:    cons,
  2701  	})
  2702  	c.Assert(err, jc.ErrorIsNil)
  2703  	c.Assert(res, gc.NotNil)
  2704  
  2705  	runOpts := res.Instance.(novaInstaceStartedWithOpts).NovaInstanceStartedWithOpts()
  2706  	c.Assert(runOpts, gc.NotNil)
  2707  	c.Assert(runOpts.ImageId, gc.NotNil)
  2708  	c.Assert(runOpts.ImageId, gc.Equals, "ubuntu-bf2")
  2709  }
  2710  
  2711  func (s *localServerSuite) TestStartInstanceVolumeRootBlockDevice(c *gc.C) {
  2712  	// diskSizeGiB should be equal to the openstack.defaultRootDiskSize
  2713  	diskSizeGiB := 30
  2714  	env := s.ensureAMDImages(c)
  2715  
  2716  	err := bootstrapEnv(c, env)
  2717  	c.Assert(err, jc.ErrorIsNil)
  2718  
  2719  	cons, err := constraints.Parse("root-disk-source=volume arch=amd64")
  2720  	c.Assert(err, jc.ErrorIsNil)
  2721  
  2722  	res, err := testing.StartInstanceWithParams(env, s.callCtx, "1", environs.StartInstanceParams{
  2723  		ControllerUUID: s.ControllerUUID,
  2724  		Constraints:    cons,
  2725  	})
  2726  	c.Assert(err, jc.ErrorIsNil)
  2727  	c.Assert(res, gc.NotNil)
  2728  
  2729  	runOpts := res.Instance.(novaInstaceStartedWithOpts).NovaInstanceStartedWithOpts()
  2730  	c.Assert(runOpts, gc.NotNil)
  2731  	c.Assert(runOpts.BlockDeviceMappings, gc.NotNil)
  2732  	deviceMapping := runOpts.BlockDeviceMappings[0]
  2733  	c.Assert(deviceMapping, jc.DeepEquals, nova.BlockDeviceMapping{
  2734  		BootIndex:           0,
  2735  		UUID:                "1",
  2736  		SourceType:          "image",
  2737  		DestinationType:     "volume",
  2738  		DeleteOnTermination: true,
  2739  		VolumeSize:          diskSizeGiB,
  2740  	})
  2741  }
  2742  
  2743  func (s *localServerSuite) TestStartInstanceVolumeRootBlockDeviceSized(c *gc.C) {
  2744  	env := s.ensureAMDImages(c)
  2745  
  2746  	diskSizeGiB := 10
  2747  
  2748  	err := bootstrapEnv(c, env)
  2749  	c.Assert(err, jc.ErrorIsNil)
  2750  
  2751  	cons, err := constraints.Parse("root-disk-source=volume root-disk=10G arch=amd64")
  2752  	c.Assert(err, jc.ErrorIsNil)
  2753  
  2754  	res, err := testing.StartInstanceWithParams(env, s.callCtx, "1", environs.StartInstanceParams{
  2755  		ControllerUUID: s.ControllerUUID,
  2756  		Constraints:    cons,
  2757  	})
  2758  	c.Assert(err, jc.ErrorIsNil)
  2759  	c.Assert(res, gc.NotNil)
  2760  
  2761  	c.Assert(res.Hardware.RootDisk, gc.NotNil)
  2762  	c.Assert(*res.Hardware.RootDisk, gc.Equals, uint64(diskSizeGiB*1024))
  2763  
  2764  	runOpts := res.Instance.(novaInstaceStartedWithOpts).NovaInstanceStartedWithOpts()
  2765  	c.Assert(runOpts, gc.NotNil)
  2766  	c.Assert(runOpts.BlockDeviceMappings, gc.NotNil)
  2767  	deviceMapping := runOpts.BlockDeviceMappings[0]
  2768  	c.Assert(deviceMapping, jc.DeepEquals, nova.BlockDeviceMapping{
  2769  		BootIndex:           0,
  2770  		UUID:                "1",
  2771  		SourceType:          "image",
  2772  		DestinationType:     "volume",
  2773  		DeleteOnTermination: true,
  2774  		VolumeSize:          diskSizeGiB,
  2775  	})
  2776  }
  2777  
  2778  func (s *localServerSuite) TestStartInstanceLocalRootBlockDeviceConstraint(c *gc.C) {
  2779  	env := s.ensureAMDImages(c)
  2780  
  2781  	err := bootstrapEnv(c, env)
  2782  	c.Assert(err, jc.ErrorIsNil)
  2783  
  2784  	cons, err := constraints.Parse("root-disk-source=local root-disk=1G arch=amd64")
  2785  	c.Assert(err, jc.ErrorIsNil)
  2786  	c.Assert(cons.HasRootDisk(), jc.IsTrue)
  2787  	c.Assert(*cons.RootDisk, gc.Equals, uint64(1024))
  2788  
  2789  	res, err := testing.StartInstanceWithParams(env, s.callCtx, "1", environs.StartInstanceParams{
  2790  		ControllerUUID: s.ControllerUUID,
  2791  		Constraints:    cons,
  2792  	})
  2793  	c.Assert(err, jc.ErrorIsNil)
  2794  	c.Assert(res, gc.NotNil)
  2795  
  2796  	c.Assert(res.Hardware.RootDisk, gc.NotNil)
  2797  	// Check local disk requirements are met.
  2798  	c.Assert(*res.Hardware.RootDisk, jc.GreaterThan, uint64(1024-1))
  2799  
  2800  	runOpts := res.Instance.(novaInstaceStartedWithOpts).NovaInstanceStartedWithOpts()
  2801  	c.Assert(runOpts, gc.NotNil)
  2802  	c.Assert(runOpts.BlockDeviceMappings, gc.NotNil)
  2803  	deviceMapping := runOpts.BlockDeviceMappings[0]
  2804  	c.Assert(deviceMapping, jc.DeepEquals, nova.BlockDeviceMapping{
  2805  		BootIndex:           0,
  2806  		UUID:                "1",
  2807  		SourceType:          "image",
  2808  		DestinationType:     "local",
  2809  		DeleteOnTermination: true,
  2810  		// VolumeSize is 0 when a local disk is used.
  2811  		VolumeSize: 0,
  2812  	})
  2813  }
  2814  
  2815  func (s *localServerSuite) TestStartInstanceLocalRootBlockDevice(c *gc.C) {
  2816  	env := s.ensureAMDImages(c)
  2817  
  2818  	err := bootstrapEnv(c, env)
  2819  	c.Assert(err, jc.ErrorIsNil)
  2820  
  2821  	cons, err := constraints.Parse("root-disk=1G arch=amd64")
  2822  	c.Assert(err, jc.ErrorIsNil)
  2823  	c.Assert(cons.HasRootDisk(), jc.IsTrue)
  2824  	c.Assert(*cons.RootDisk, gc.Equals, uint64(1024))
  2825  
  2826  	res, err := testing.StartInstanceWithParams(env, s.callCtx, "1", environs.StartInstanceParams{
  2827  		ControllerUUID: s.ControllerUUID,
  2828  		Constraints:    cons,
  2829  	})
  2830  	c.Assert(err, jc.ErrorIsNil)
  2831  	c.Assert(res, gc.NotNil)
  2832  
  2833  	c.Assert(res.Hardware.RootDisk, gc.NotNil)
  2834  	// Check local disk requirements are met.
  2835  	c.Assert(*res.Hardware.RootDisk, jc.GreaterThan, uint64(1024-1))
  2836  
  2837  	runOpts := res.Instance.(novaInstaceStartedWithOpts).NovaInstanceStartedWithOpts()
  2838  	c.Assert(runOpts, gc.NotNil)
  2839  	c.Assert(runOpts.BlockDeviceMappings, gc.NotNil)
  2840  	deviceMapping := runOpts.BlockDeviceMappings[0]
  2841  	c.Assert(deviceMapping, jc.DeepEquals, nova.BlockDeviceMapping{
  2842  		BootIndex:           0,
  2843  		UUID:                "1",
  2844  		SourceType:          "image",
  2845  		DestinationType:     "local",
  2846  		DeleteOnTermination: true,
  2847  		// VolumeSize is 0 when a local disk is used.
  2848  		VolumeSize: 0,
  2849  	})
  2850  }
  2851  
  2852  func (s *localServerSuite) TestInstanceTags(c *gc.C) {
  2853  	err := bootstrapEnv(c, s.env)
  2854  	c.Assert(err, jc.ErrorIsNil)
  2855  
  2856  	allInstances, err := s.env.AllRunningInstances(s.callCtx)
  2857  	c.Assert(err, jc.ErrorIsNil)
  2858  	c.Assert(allInstances, gc.HasLen, 1)
  2859  
  2860  	c.Assert(
  2861  		openstack.InstanceServerDetail(allInstances[0]).Metadata,
  2862  		jc.DeepEquals,
  2863  		map[string]string{
  2864  			"juju-model-uuid":      coretesting.ModelTag.Id(),
  2865  			"juju-controller-uuid": coretesting.ControllerTag.Id(),
  2866  			"juju-is-controller":   "true",
  2867  		},
  2868  	)
  2869  }
  2870  
  2871  func (s *localServerSuite) TestTagInstance(c *gc.C) {
  2872  	err := bootstrapEnv(c, s.env)
  2873  	c.Assert(err, jc.ErrorIsNil)
  2874  
  2875  	assertMetadata := func(extraKey, extraValue string) {
  2876  		// Refresh instance
  2877  		allInstances, err := s.env.AllRunningInstances(s.callCtx)
  2878  		c.Assert(err, jc.ErrorIsNil)
  2879  		c.Assert(allInstances, gc.HasLen, 1)
  2880  		c.Assert(
  2881  			openstack.InstanceServerDetail(allInstances[0]).Metadata,
  2882  			jc.DeepEquals,
  2883  			map[string]string{
  2884  				"juju-model-uuid":      coretesting.ModelTag.Id(),
  2885  				"juju-controller-uuid": coretesting.ControllerTag.Id(),
  2886  				"juju-is-controller":   "true",
  2887  				extraKey:               extraValue,
  2888  			},
  2889  		)
  2890  	}
  2891  
  2892  	allInstances, err := s.env.AllRunningInstances(s.callCtx)
  2893  	c.Assert(err, jc.ErrorIsNil)
  2894  	c.Assert(allInstances, gc.HasLen, 1)
  2895  
  2896  	extraKey := "extra-k"
  2897  	extraValue := "extra-v"
  2898  	err = s.env.(environs.InstanceTagger).TagInstance(
  2899  		s.callCtx,
  2900  		allInstances[0].Id(),
  2901  		map[string]string{extraKey: extraValue},
  2902  	)
  2903  	c.Assert(err, jc.ErrorIsNil)
  2904  	assertMetadata(extraKey, extraValue)
  2905  
  2906  	// Ensure that a second call updates existing tags.
  2907  	extraValue = "extra-v2"
  2908  	err = s.env.(environs.InstanceTagger).TagInstance(
  2909  		s.callCtx,
  2910  		allInstances[0].Id(),
  2911  		map[string]string{extraKey: extraValue},
  2912  	)
  2913  	c.Assert(err, jc.ErrorIsNil)
  2914  	assertMetadata(extraKey, extraValue)
  2915  }
  2916  
  2917  func (s *localServerSuite) TestAdoptResources(c *gc.C) {
  2918  	err := bootstrapEnv(c, s.env)
  2919  	c.Assert(err, jc.ErrorIsNil)
  2920  
  2921  	hostedModelUUID := "7e386e08-cba7-44a4-a76e-7c1633584210"
  2922  	cfg, err := s.env.Config().Apply(map[string]interface{}{
  2923  		"uuid": hostedModelUUID,
  2924  	})
  2925  	c.Assert(err, jc.ErrorIsNil)
  2926  	env, err := environs.New(stdcontext.TODO(), environs.OpenParams{
  2927  		Cloud:  makeCloudSpec(s.cred),
  2928  		Config: cfg,
  2929  	})
  2930  	c.Assert(err, jc.ErrorIsNil)
  2931  	originalController := coretesting.ControllerTag.Id()
  2932  	_, _, _, err = testing.StartInstance(env, s.callCtx, originalController, "0")
  2933  	c.Assert(err, jc.ErrorIsNil)
  2934  
  2935  	addVolume(c, s.env, s.callCtx, originalController, "99/9")
  2936  	addVolume(c, env, s.callCtx, originalController, "23/9")
  2937  
  2938  	s.checkInstanceTags(c, s.env, originalController)
  2939  	s.checkInstanceTags(c, env, originalController)
  2940  	s.checkVolumeTags(c, s.env, originalController)
  2941  	s.checkVolumeTags(c, env, originalController)
  2942  	s.checkGroupController(c, s.env, originalController)
  2943  	s.checkGroupController(c, env, originalController)
  2944  
  2945  	// Needs to be a correctly formatted uuid so we can get it out of
  2946  	// group names.
  2947  	newController := "aaaaaaaa-bbbb-cccc-dddd-0123456789ab"
  2948  	err = env.AdoptResources(s.callCtx, newController, version.MustParse("1.2.3"))
  2949  	c.Assert(err, jc.ErrorIsNil)
  2950  
  2951  	s.checkInstanceTags(c, s.env, originalController)
  2952  	s.checkInstanceTags(c, env, newController)
  2953  	s.checkVolumeTags(c, s.env, originalController)
  2954  	s.checkVolumeTags(c, env, newController)
  2955  	s.checkGroupController(c, s.env, originalController)
  2956  	s.checkGroupController(c, env, newController)
  2957  }
  2958  
  2959  func (s *localServerSuite) TestAdoptResourcesNoStorage(c *gc.C) {
  2960  	// Nova-lxd doesn't support storage. lp:1677225
  2961  	s.PatchValue(openstack.NewOpenstackStorage, func(*openstack.Environ) (openstack.OpenstackStorage, error) {
  2962  		return nil, errors.NotSupportedf("volumes")
  2963  	})
  2964  	err := bootstrapEnv(c, s.env)
  2965  	c.Assert(err, jc.ErrorIsNil)
  2966  
  2967  	hostedModelUUID := "7e386e08-cba7-44a4-a76e-7c1633584210"
  2968  	cfg, err := s.env.Config().Apply(map[string]interface{}{
  2969  		"uuid": hostedModelUUID,
  2970  	})
  2971  	c.Assert(err, jc.ErrorIsNil)
  2972  	env, err := environs.New(stdcontext.TODO(), environs.OpenParams{
  2973  		Cloud:  makeCloudSpec(s.cred),
  2974  		Config: cfg,
  2975  	})
  2976  	c.Assert(err, jc.ErrorIsNil)
  2977  	originalController := coretesting.ControllerTag.Id()
  2978  	_, _, _, err = testing.StartInstance(env, s.callCtx, originalController, "0")
  2979  	c.Assert(err, jc.ErrorIsNil)
  2980  
  2981  	s.checkInstanceTags(c, s.env, originalController)
  2982  	s.checkInstanceTags(c, env, originalController)
  2983  	s.checkGroupController(c, s.env, originalController)
  2984  	s.checkGroupController(c, env, originalController)
  2985  
  2986  	// Needs to be a correctly formatted uuid so we can get it out of
  2987  	// group names.
  2988  	newController := "aaaaaaaa-bbbb-cccc-dddd-0123456789ab"
  2989  	err = env.AdoptResources(s.callCtx, newController, version.MustParse("1.2.3"))
  2990  	c.Assert(err, jc.ErrorIsNil)
  2991  
  2992  	s.checkInstanceTags(c, s.env, originalController)
  2993  	s.checkInstanceTags(c, env, newController)
  2994  	s.checkGroupController(c, s.env, originalController)
  2995  	s.checkGroupController(c, env, newController)
  2996  }
  2997  
  2998  func addVolume(
  2999  	c *gc.C, env environs.Environ, callCtx context.ProviderCallContext, controllerUUID, name string,
  3000  ) *storage.Volume {
  3001  	storageAdapter, err := (*openstack.NewOpenstackStorage)(env.(*openstack.Environ))
  3002  	c.Assert(err, jc.ErrorIsNil)
  3003  	modelUUID := env.Config().UUID()
  3004  	source := openstack.NewCinderVolumeSourceForModel(storageAdapter, modelUUID, env.(common.ZonedEnviron))
  3005  	result, err := source.CreateVolumes(callCtx, []storage.VolumeParams{{
  3006  		Tag: names.NewVolumeTag(name),
  3007  		ResourceTags: tags.ResourceTags(
  3008  			names.NewModelTag(modelUUID),
  3009  			names.NewControllerTag(controllerUUID),
  3010  		),
  3011  	}})
  3012  	c.Assert(err, jc.ErrorIsNil)
  3013  	c.Assert(result, gc.HasLen, 1)
  3014  	c.Assert(result[0].Error, jc.ErrorIsNil)
  3015  	return result[0].Volume
  3016  }
  3017  
  3018  func (s *localServerSuite) checkInstanceTags(c *gc.C, env environs.Environ, expectedController string) {
  3019  	allInstances, err := env.AllRunningInstances(s.callCtx)
  3020  	c.Assert(err, jc.ErrorIsNil)
  3021  	c.Assert(allInstances, gc.Not(gc.HasLen), 0)
  3022  	for _, inst := range allInstances {
  3023  		server := openstack.InstanceServerDetail(inst)
  3024  		c.Logf(string(inst.Id()))
  3025  		c.Check(server.Metadata[tags.JujuController], gc.Equals, expectedController)
  3026  	}
  3027  }
  3028  
  3029  func (s *localServerSuite) checkVolumeTags(c *gc.C, env environs.Environ, expectedController string) {
  3030  	stor, err := (*openstack.NewOpenstackStorage)(env.(*openstack.Environ))
  3031  	c.Assert(err, jc.ErrorIsNil)
  3032  	source := openstack.NewCinderVolumeSourceForModel(stor, env.Config().UUID(), s.env.(common.ZonedEnviron))
  3033  	volumeIds, err := source.ListVolumes(s.callCtx)
  3034  	c.Assert(err, jc.ErrorIsNil)
  3035  	c.Assert(volumeIds, gc.Not(gc.HasLen), 0)
  3036  	for _, volumeId := range volumeIds {
  3037  		c.Logf(volumeId)
  3038  		volume, err := stor.GetVolume(volumeId)
  3039  		c.Assert(err, jc.ErrorIsNil)
  3040  		c.Check(volume.Metadata[tags.JujuController], gc.Equals, expectedController)
  3041  	}
  3042  }
  3043  
  3044  func (s *localServerSuite) checkGroupController(c *gc.C, env environs.Environ, expectedController string) {
  3045  	groupNames, err := openstack.GetModelGroupNames(env)
  3046  	c.Assert(err, jc.ErrorIsNil)
  3047  	c.Assert(groupNames, gc.Not(gc.HasLen), 0)
  3048  	extractControllerRe, err := regexp.Compile(openstack.GroupControllerPattern)
  3049  	c.Assert(err, jc.ErrorIsNil)
  3050  	for _, group := range groupNames {
  3051  		c.Logf(group)
  3052  		controller := extractControllerRe.ReplaceAllString(group, "$controllerUUID")
  3053  		c.Check(controller, gc.Equals, expectedController)
  3054  	}
  3055  }
  3056  
  3057  func (s *localServerSuite) TestUpdateGroupController(c *gc.C) {
  3058  	err := bootstrapEnv(c, s.env)
  3059  	c.Assert(err, jc.ErrorIsNil)
  3060  
  3061  	groupNames, err := openstack.GetModelGroupNames(s.env)
  3062  	c.Assert(err, jc.ErrorIsNil)
  3063  	groupNamesBefore := set.NewStrings(groupNames...)
  3064  	c.Assert(groupNamesBefore, gc.DeepEquals, set.NewStrings(
  3065  		"juju-deadbeef-1bad-500d-9000-4b1d0d06f00d-deadbeef-0bad-400d-8000-4b1d0d06f00d",
  3066  		"juju-deadbeef-1bad-500d-9000-4b1d0d06f00d-deadbeef-0bad-400d-8000-4b1d0d06f00d-0",
  3067  	))
  3068  
  3069  	firewaller := openstack.GetFirewaller(s.env)
  3070  	err = firewaller.UpdateGroupController(s.callCtx, "aabbccdd-eeee-ffff-0000-0123456789ab")
  3071  	c.Assert(err, jc.ErrorIsNil)
  3072  
  3073  	groupNames, err = openstack.GetModelGroupNames(s.env)
  3074  	c.Assert(err, jc.ErrorIsNil)
  3075  	groupNamesAfter := set.NewStrings(groupNames...)
  3076  	c.Assert(groupNamesAfter, gc.DeepEquals, set.NewStrings(
  3077  		"juju-aabbccdd-eeee-ffff-0000-0123456789ab-deadbeef-0bad-400d-8000-4b1d0d06f00d",
  3078  		"juju-aabbccdd-eeee-ffff-0000-0123456789ab-deadbeef-0bad-400d-8000-4b1d0d06f00d-0",
  3079  	))
  3080  }
  3081  
  3082  func (s *localServerSuite) TestICMPFirewallRules(c *gc.C) {
  3083  	err := bootstrapEnv(c, s.env)
  3084  	c.Assert(err, jc.ErrorIsNil)
  3085  
  3086  	inst, _ := testing.AssertStartInstance(c, s.env, s.callCtx, s.ControllerUUID, "100")
  3087  	firewaller := openstack.GetFirewaller(s.env)
  3088  	err = firewaller.OpenInstancePorts(s.callCtx, inst, "100", firewall.IngressRules{
  3089  		{
  3090  			PortRange: network.PortRange{
  3091  				FromPort: -1,
  3092  				ToPort:   -1,
  3093  				Protocol: "icmp",
  3094  			},
  3095  			SourceCIDRs: set.NewStrings("0.0.0.0/0"),
  3096  		},
  3097  		{
  3098  			PortRange: network.PortRange{
  3099  				FromPort: -1,
  3100  				ToPort:   -1,
  3101  				Protocol: "ipv6-icmp",
  3102  			},
  3103  			SourceCIDRs: set.NewStrings("::/0"),
  3104  		},
  3105  	})
  3106  	c.Assert(err, jc.ErrorIsNil)
  3107  
  3108  	rules, err := firewaller.InstanceIngressRules(s.callCtx, inst, "100")
  3109  	c.Assert(err, jc.ErrorIsNil)
  3110  
  3111  	c.Assert(len(rules), gc.Equals, 1)
  3112  	c.Assert(rules[0].PortRange.FromPort, gc.Equals, -1)
  3113  	c.Assert(rules[0].PortRange.ToPort, gc.Equals, -1)
  3114  	c.Assert(rules[0].PortRange.Protocol, gc.Equals, "icmp")
  3115  	c.Assert(rules[0].SourceCIDRs.Size(), gc.Equals, 2)
  3116  	c.Assert(rules[0].SourceCIDRs.Contains("0.0.0.0/0"), jc.IsTrue)
  3117  	c.Assert(rules[0].SourceCIDRs.Contains("::/0"), jc.IsTrue)
  3118  }
  3119  
  3120  // TestIPv6RuleCreationForEmptyCIDR is a regression test for lp1709312
  3121  func (s *localServerSuite) TestIPv6RuleCreationForEmptyCIDR(c *gc.C) {
  3122  	err := bootstrapEnv(c, s.env)
  3123  	c.Assert(err, jc.ErrorIsNil)
  3124  
  3125  	inst, _ := testing.AssertStartInstance(c, s.env, s.callCtx, s.ControllerUUID, "100")
  3126  	firewaller := openstack.GetFirewaller(s.env)
  3127  	err = firewaller.OpenInstancePorts(s.callCtx, inst, "100", firewall.IngressRules{
  3128  		{
  3129  			PortRange: network.PortRange{
  3130  				FromPort: 443,
  3131  				ToPort:   443,
  3132  				Protocol: "tcp",
  3133  			},
  3134  			SourceCIDRs: set.NewStrings(),
  3135  		},
  3136  	})
  3137  	c.Assert(err, jc.ErrorIsNil)
  3138  
  3139  	rules, err := firewaller.InstanceIngressRules(s.callCtx, inst, "100")
  3140  	c.Assert(err, jc.ErrorIsNil)
  3141  
  3142  	c.Assert(len(rules), gc.Equals, 1)
  3143  	c.Assert(rules[0].PortRange.FromPort, gc.Equals, 443)
  3144  	c.Assert(rules[0].PortRange.ToPort, gc.Equals, 443)
  3145  	c.Assert(rules[0].PortRange.Protocol, gc.Equals, "tcp")
  3146  	c.Assert(rules[0].SourceCIDRs.Size(), gc.Equals, 2)
  3147  	c.Assert(rules[0].SourceCIDRs.Contains("0.0.0.0/0"), jc.IsTrue)
  3148  	c.Assert(rules[0].SourceCIDRs.Contains("::/0"), jc.IsTrue)
  3149  }
  3150  
  3151  func (s *localServerSuite) ensureAMDImages(c *gc.C) environs.Environ {
  3152  	// Ensure amd64 tools are available, to ensure an amd64 image.
  3153  	amd64Version := version.Binary{
  3154  		Number: jujuversion.Current,
  3155  		Arch:   arch.AMD64,
  3156  	}
  3157  	workloadOSList, err := corebase.AllWorkloadOSTypes()
  3158  	c.Assert(err, jc.ErrorIsNil)
  3159  	for _, workloadOS := range workloadOSList.Values() {
  3160  		amd64Version.Release = workloadOS
  3161  		envtesting.AssertUploadFakeToolsVersions(
  3162  			c, s.toolsMetadataStorage, s.env.Config().AgentStream(), s.env.Config().AgentStream(), amd64Version)
  3163  	}
  3164  
  3165  	// Destroy the old Environ
  3166  	err = environs.Destroy(s.env.Config().Name(), s.env, s.callCtx, s.ControllerStore)
  3167  	c.Assert(err, jc.ErrorIsNil)
  3168  
  3169  	// Prepare a new Environ
  3170  	return s.Prepare(c)
  3171  }
  3172  
  3173  // noNeutronSuite is a clone of localServerSuite which hacks the local
  3174  // openstack to remove the neutron service from the auth response -
  3175  // this causes the client to switch to nova networking.
  3176  type noNeutronSuite struct {
  3177  	coretesting.BaseSuite
  3178  	cred *identity.Credentials
  3179  	srv  localServer
  3180  }
  3181  
  3182  func (s *noNeutronSuite) SetUpSuite(c *gc.C) {
  3183  	s.BaseSuite.SetUpSuite(c)
  3184  	c.Logf("Running local tests")
  3185  }
  3186  
  3187  func (s *noNeutronSuite) SetUpTest(c *gc.C) {
  3188  	s.BaseSuite.SetUpTest(c)
  3189  	s.srv.start(c, s.cred, newNovaNetworkingOpenstackService)
  3190  
  3191  	userPass, ok := s.srv.OpenstackSvc.Identity.(*identityservice.UserPass)
  3192  	c.Assert(ok, jc.IsTrue)
  3193  	// Ensure that there's nothing returned with a type of "network",
  3194  	// so that we switch over to nova networking.
  3195  	cleanup := userPass.RegisterControlPoint("authorisation", func(sc hook.ServiceControl, args ...interface{}) error {
  3196  		res, ok := args[0].(*identityservice.AccessResponse)
  3197  		c.Assert(ok, jc.IsTrue)
  3198  		var filtered []identityservice.V2Service
  3199  		for _, service := range res.Access.ServiceCatalog {
  3200  			if service.Type != "network" {
  3201  				filtered = append(filtered, service)
  3202  			}
  3203  		}
  3204  		res.Access.ServiceCatalog = filtered
  3205  		return nil
  3206  	})
  3207  	s.AddCleanup(func(c *gc.C) { cleanup() })
  3208  }
  3209  
  3210  func (s *noNeutronSuite) TearDownTest(c *gc.C) {
  3211  	s.srv.stop()
  3212  	s.BaseSuite.TearDownTest(c)
  3213  }
  3214  
  3215  func (s *noNeutronSuite) TestSupport(c *gc.C) {
  3216  	cl := client.NewClient(s.cred, identity.AuthUserPass, nil)
  3217  	err := cl.Authenticate()
  3218  	c.Assert(err, jc.ErrorIsNil)
  3219  	containerURL, err := cl.MakeServiceURL("object-store", "", nil)
  3220  	c.Assert(err, jc.ErrorIsNil)
  3221  	attrs := coretesting.FakeConfig().Merge(coretesting.Attrs{
  3222  		"name":               "sample-no-neutron",
  3223  		"type":               "openstack",
  3224  		"auth-mode":          "userpass",
  3225  		"agent-version":      coretesting.FakeVersionNumber.String(),
  3226  		"agent-metadata-url": containerURL + "/juju-dist-test/tools",
  3227  		"image-metadata-url": containerURL + "/juju-dist-test",
  3228  		"authorized-keys":    "fakekey",
  3229  	})
  3230  	s.PatchValue(&jujuversion.Current, coretesting.FakeVersionNumber)
  3231  	// For testing, we create a storage instance to which is uploaded tools and image metadata.
  3232  	_, err = bootstrap.PrepareController(
  3233  		false,
  3234  		envtesting.BootstrapTODOContext(c),
  3235  		jujuclient.NewMemStore(),
  3236  		prepareParams(attrs, s.cred),
  3237  	)
  3238  	c.Check(err, jc.Satisfies, errors.IsNotFound)
  3239  	c.Assert(err, gc.ErrorMatches, `OpenStack Neutron service`)
  3240  }
  3241  
  3242  func prepareParams(attrs map[string]interface{}, cred *identity.Credentials) bootstrap.PrepareParams {
  3243  	return bootstrap.PrepareParams{
  3244  		ControllerConfig: coretesting.FakeControllerConfig(),
  3245  		ModelConfig:      attrs,
  3246  		ControllerName:   attrs["name"].(string),
  3247  		Cloud:            makeCloudSpec(cred),
  3248  		AdminSecret:      testing.AdminSecret,
  3249  	}
  3250  }
  3251  
  3252  func makeCloudSpec(cred *identity.Credentials) environscloudspec.CloudSpec {
  3253  	credential := makeCredential(cred)
  3254  	return environscloudspec.CloudSpec{
  3255  		Type:       "openstack",
  3256  		Name:       "openstack",
  3257  		Endpoint:   cred.URL,
  3258  		Region:     cred.Region,
  3259  		Credential: &credential,
  3260  	}
  3261  }
  3262  
  3263  func makeCredential(cred *identity.Credentials) cloud.Credential {
  3264  	return cloud.NewCredential(
  3265  		cloud.UserPassAuthType,
  3266  		map[string]string{
  3267  			"username":    cred.User,
  3268  			"password":    cred.Secrets,
  3269  			"tenant-name": cred.TenantName,
  3270  		},
  3271  	)
  3272  }
  3273  
  3274  // noSwiftSuite contains tests that run against an OpenStack service double
  3275  // that lacks Swift.
  3276  type noSwiftSuite struct {
  3277  	coretesting.BaseSuite
  3278  	cred *identity.Credentials
  3279  	srv  localServer
  3280  	env  environs.Environ
  3281  }
  3282  
  3283  var _ = gc.Suite(&noSwiftSuite{})
  3284  
  3285  func (s *noSwiftSuite) SetUpSuite(c *gc.C) {
  3286  	s.BaseSuite.SetUpSuite(c)
  3287  	restoreFinishBootstrap := envtesting.DisableFinishBootstrap()
  3288  	s.AddCleanup(func(*gc.C) { restoreFinishBootstrap() })
  3289  
  3290  	s.PatchValue(&imagemetadata.SimplestreamsImagesPublicKey, sstesting.SignedMetadataPublicKey)
  3291  	s.PatchValue(&keys.JujuPublicKey, sstesting.SignedMetadataPublicKey)
  3292  }
  3293  
  3294  func (s *noSwiftSuite) SetUpTest(c *gc.C) {
  3295  	s.BaseSuite.SetUpTest(c)
  3296  	s.cred = &identity.Credentials{
  3297  		Version:    2,
  3298  		User:       "fred",
  3299  		Secrets:    "secret",
  3300  		Region:     "some-region",
  3301  		TenantName: "some tenant",
  3302  	}
  3303  	s.srv.start(c, s.cred, newNovaOnlyOpenstackService)
  3304  
  3305  	attrs := coretesting.FakeConfig().Merge(coretesting.Attrs{
  3306  		"name":            "sample-no-swift",
  3307  		"type":            "openstack",
  3308  		"auth-mode":       "userpass",
  3309  		"agent-version":   coretesting.FakeVersionNumber.String(),
  3310  		"authorized-keys": "fakekey",
  3311  	})
  3312  	s.PatchValue(&jujuversion.Current, coretesting.FakeVersionNumber)
  3313  	// Serve fake tools and image metadata using "filestorage",
  3314  	// rather than Swift as the rest of the tests do.
  3315  	storageDir := c.MkDir()
  3316  	imagesDir := filepath.Join(storageDir, "images")
  3317  	toolsDir := filepath.Join(storageDir, "tools")
  3318  	for _, dir := range []string{imagesDir, toolsDir} {
  3319  		err := os.MkdirAll(dir, 0755)
  3320  		c.Assert(err, jc.ErrorIsNil)
  3321  	}
  3322  	toolsStorage, err := filestorage.NewFileStorageWriter(storageDir)
  3323  	c.Assert(err, jc.ErrorIsNil)
  3324  	envtesting.UploadFakeTools(c, toolsStorage, "released", "released")
  3325  	s.PatchValue(&tools.DefaultBaseURL, storageDir)
  3326  	imageStorage, err := filestorage.NewFileStorageWriter(imagesDir)
  3327  	c.Assert(err, jc.ErrorIsNil)
  3328  	openstack.UseTestImageData(imageStorage, s.cred)
  3329  	imagetesting.PatchOfficialDataSources(&s.CleanupSuite, storageDir)
  3330  
  3331  	env, err := bootstrap.PrepareController(
  3332  		false,
  3333  		envtesting.BootstrapTODOContext(c),
  3334  		jujuclient.NewMemStore(),
  3335  		prepareParams(attrs, s.cred),
  3336  	)
  3337  	c.Assert(err, jc.ErrorIsNil)
  3338  	s.env = env.(environs.Environ)
  3339  }
  3340  
  3341  func (s *noSwiftSuite) TearDownTest(c *gc.C) {
  3342  	s.srv.stop()
  3343  	s.BaseSuite.TearDownTest(c)
  3344  }
  3345  
  3346  func (s *noSwiftSuite) TestBootstrap(c *gc.C) {
  3347  	cfg, err := s.env.Config().Apply(coretesting.Attrs{
  3348  		// A label that corresponds to a neutron test service network
  3349  		"network": "net",
  3350  	})
  3351  	c.Assert(err, jc.ErrorIsNil)
  3352  
  3353  	err = s.env.SetConfig(cfg)
  3354  	c.Assert(err, jc.ErrorIsNil)
  3355  
  3356  	c.Assert(bootstrapEnv(c, s.env), jc.ErrorIsNil)
  3357  }
  3358  
  3359  func newFullOpenstackService(cred *identity.Credentials, auth identity.AuthMode, useTSL bool) (
  3360  	*openstackservice.Openstack, []string,
  3361  ) {
  3362  	service, logMsg := openstackservice.New(cred, auth, useTSL)
  3363  	service.UseNeutronNetworking()
  3364  	service.SetupHTTP(nil)
  3365  	return service, logMsg
  3366  }
  3367  
  3368  func newNovaOnlyOpenstackService(cred *identity.Credentials, auth identity.AuthMode, useTSL bool) (*openstackservice.Openstack, []string) {
  3369  	service, logMsg := openstackservice.NewNoSwift(cred, auth, useTSL)
  3370  	service.UseNeutronNetworking()
  3371  	service.SetupHTTP(nil)
  3372  	return service, logMsg
  3373  }
  3374  
  3375  func newNovaNetworkingOpenstackService(cred *identity.Credentials, auth identity.AuthMode, useTSL bool) (*openstackservice.Openstack, []string) {
  3376  	service, logMsg := openstackservice.New(cred, auth, useTSL)
  3377  	service.SetupHTTP(nil)
  3378  	return service, logMsg
  3379  }
  3380  func bootstrapEnv(c *gc.C, env environs.Environ) error {
  3381  	return bootstrapEnvWithConstraints(c, env, constraints.Value{})
  3382  }
  3383  
  3384  func bootstrapEnvWithConstraints(c *gc.C, env environs.Environ, cons constraints.Value) error {
  3385  	return bootstrap.Bootstrap(envtesting.BootstrapTODOContext(c), env,
  3386  		context.NewEmptyCloudCallContext(),
  3387  		bootstrap.BootstrapParams{
  3388  			ControllerConfig:        coretesting.FakeControllerConfig(),
  3389  			AdminSecret:             testing.AdminSecret,
  3390  			CAPrivateKey:            coretesting.CAKey,
  3391  			SupportedBootstrapBases: coretesting.FakeSupportedJujuBases,
  3392  			BootstrapConstraints:    cons,
  3393  		})
  3394  }