github.com/cloud-green/juju@v0.0.0-20151002100041-a00291338d3d/apiserver/spaces/spaces.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package spaces
     5  
     6  import (
     7  	"github.com/juju/errors"
     8  	"github.com/juju/loggo"
     9  	"github.com/juju/names"
    10  
    11  	"github.com/juju/juju/apiserver/common"
    12  	"github.com/juju/juju/apiserver/params"
    13  	"github.com/juju/juju/environs"
    14  	"github.com/juju/juju/environs/config"
    15  	"github.com/juju/juju/state"
    16  )
    17  
    18  var logger = loggo.GetLogger("juju.apiserver.spaces")
    19  
    20  func init() {
    21  	common.RegisterStandardFacade("Spaces", 1, NewAPI)
    22  }
    23  
    24  // API defines the methods the Spaces API facade implements.
    25  type API interface {
    26  	CreateSpaces(params.CreateSpacesParams) (params.ErrorResults, error)
    27  	ListSpaces() (params.ListSpacesResults, error)
    28  }
    29  
    30  // Backing defines the state methods this facede needs, so they can be
    31  // mocked for testing.
    32  type Backing interface {
    33  	// EnvironConfig returns the configuration of the environment.
    34  	EnvironConfig() (*config.Config, error)
    35  
    36  	// AddSpace creates a space.
    37  	AddSpace(name string, subnetIds []string, public bool) error
    38  
    39  	// AllSpaces returns all known Juju network spaces.
    40  	AllSpaces() ([]common.BackingSpace, error)
    41  }
    42  
    43  // spacesAPI implements the API interface.
    44  type spacesAPI struct {
    45  	backing    Backing
    46  	resources  *common.Resources
    47  	authorizer common.Authorizer
    48  }
    49  
    50  // NewAPI creates a new Space API server-side facade with a
    51  // state.State backing.
    52  func NewAPI(st *state.State, res *common.Resources, auth common.Authorizer) (API, error) {
    53  	return newAPIWithBacking(&stateShim{st: st}, res, auth)
    54  }
    55  
    56  // newAPIWithBacking creates a new server-side Spaces API facade with
    57  // the given Backing.
    58  func newAPIWithBacking(backing Backing, resources *common.Resources, authorizer common.Authorizer) (API, error) {
    59  	// Only clients can access the Spaces facade.
    60  	if !authorizer.AuthClient() {
    61  		return nil, common.ErrPerm
    62  	}
    63  	return &spacesAPI{
    64  		backing:    backing,
    65  		resources:  resources,
    66  		authorizer: authorizer,
    67  	}, nil
    68  }
    69  
    70  // CreateSpaces creates a new Juju network space, associating the
    71  // specified subnets with it (optional; can be empty).
    72  func (api *spacesAPI) CreateSpaces(args params.CreateSpacesParams) (results params.ErrorResults, err error) {
    73  	err = api.supportsSpaces()
    74  	if err != nil {
    75  		return results, common.ServerError(errors.Trace(err))
    76  	}
    77  
    78  	results.Results = make([]params.ErrorResult, len(args.Spaces))
    79  
    80  	for i, space := range args.Spaces {
    81  		err := api.createOneSpace(space)
    82  		if err == nil {
    83  			continue
    84  		}
    85  		results.Results[i].Error = common.ServerError(errors.Trace(err))
    86  	}
    87  
    88  	return results, nil
    89  }
    90  
    91  func (api *spacesAPI) createOneSpace(args params.CreateSpaceParams) error {
    92  	// Validate the args, assemble information for api.backing.AddSpaces
    93  	var subnets []string
    94  
    95  	spaceTag, err := names.ParseSpaceTag(args.SpaceTag)
    96  	if err != nil {
    97  		return errors.Trace(err)
    98  	}
    99  
   100  	for _, tag := range args.SubnetTags {
   101  		subnetTag, err := names.ParseSubnetTag(tag)
   102  		if err != nil {
   103  			return errors.Trace(err)
   104  		}
   105  		subnets = append(subnets, subnetTag.Id())
   106  	}
   107  
   108  	// Add the validated space
   109  	err = api.backing.AddSpace(spaceTag.Id(), subnets, args.Public)
   110  	if err != nil {
   111  		return errors.Trace(err)
   112  	}
   113  	return nil
   114  }
   115  
   116  func backingSubnetToParamsSubnet(subnet common.BackingSubnet) params.Subnet {
   117  	cidr := subnet.CIDR()
   118  	vlantag := subnet.VLANTag()
   119  	providerid := subnet.ProviderId()
   120  	zones := subnet.AvailabilityZones()
   121  	status := subnet.Status()
   122  	var spaceTag names.SpaceTag
   123  	if subnet.SpaceName() != "" {
   124  		spaceTag = names.NewSpaceTag(subnet.SpaceName())
   125  	}
   126  
   127  	return params.Subnet{
   128  		CIDR:       cidr,
   129  		VLANTag:    vlantag,
   130  		ProviderId: providerid,
   131  		Zones:      zones,
   132  		Status:     status,
   133  		SpaceTag:   spaceTag.String(),
   134  		Life:       subnet.Life(),
   135  	}
   136  }
   137  
   138  // ListSpaces lists all the available spaces and their associated subnets.
   139  func (api *spacesAPI) ListSpaces() (results params.ListSpacesResults, err error) {
   140  	err = api.supportsSpaces()
   141  	if err != nil {
   142  		return results, common.ServerError(errors.Trace(err))
   143  	}
   144  
   145  	spaces, err := api.backing.AllSpaces()
   146  	if err != nil {
   147  		return results, errors.Trace(err)
   148  	}
   149  
   150  	results.Results = make([]params.Space, len(spaces))
   151  	for i, space := range spaces {
   152  		result := params.Space{}
   153  		result.Name = space.Name()
   154  
   155  		subnets, err := space.Subnets()
   156  		if err != nil {
   157  			err = errors.Annotatef(err, "fetching subnets")
   158  			result.Error = common.ServerError(err)
   159  			results.Results[i] = result
   160  			continue
   161  		}
   162  
   163  		result.Subnets = make([]params.Subnet, len(subnets))
   164  		for i, subnet := range subnets {
   165  			result.Subnets[i] = backingSubnetToParamsSubnet(subnet)
   166  		}
   167  		results.Results[i] = result
   168  	}
   169  	return results, nil
   170  }
   171  
   172  // supportsSpaces checks if the environment implements NetworkingEnviron
   173  // and also if it supports spaces.
   174  func (api *spacesAPI) supportsSpaces() error {
   175  	config, err := api.backing.EnvironConfig()
   176  	if err != nil {
   177  		return errors.Annotate(err, "getting environment config")
   178  	}
   179  	env, err := environs.New(config)
   180  	if err != nil {
   181  		return errors.Annotate(err, "validating environment config")
   182  	}
   183  	netEnv, ok := environs.SupportsNetworking(env)
   184  	if !ok {
   185  		return errors.NotSupportedf("networking")
   186  	}
   187  	ok, err = netEnv.SupportsSpaces()
   188  	if err != nil {
   189  		logger.Warningf("environment does not support spaces: %v", err)
   190  	}
   191  	return err
   192  }