github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/provider/cloudsigma/client.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package cloudsigma
     5  
     6  import (
     7  	"github.com/altoros/gosigma"
     8  	"github.com/juju/errors"
     9  	"github.com/juju/loggo"
    10  	"github.com/juju/utils"
    11  	"github.com/juju/utils/arch"
    12  
    13  	"github.com/juju/juju/core/instance"
    14  	"github.com/juju/juju/environs"
    15  	"github.com/juju/juju/environs/imagemetadata"
    16  	"github.com/juju/juju/state/multiwatcher"
    17  )
    18  
    19  type environClient struct {
    20  	conn *gosigma.Client
    21  	uuid string
    22  }
    23  
    24  type tracer struct{}
    25  
    26  func (tracer) Logf(format string, args ...interface{}) {
    27  	logger.Tracef(format, args...)
    28  }
    29  
    30  // newClient returns an instance of the CloudSigma client.
    31  var newClient = func(cloud environs.CloudSpec, uuid string) (client *environClient, err error) {
    32  	logger.Debugf("creating CloudSigma client: id=%q", uuid)
    33  
    34  	credAttrs := cloud.Credential.Attributes()
    35  	username := credAttrs[credAttrUsername]
    36  	password := credAttrs[credAttrPassword]
    37  
    38  	// create connection to CloudSigma
    39  	conn, err := gosigma.NewClient(cloud.Endpoint, username, password, nil)
    40  	if err != nil {
    41  		return nil, err
    42  	}
    43  
    44  	// configure trace logger
    45  	if logger.LogLevel() <= loggo.TRACE {
    46  		conn.Logger(&tracer{})
    47  	}
    48  
    49  	client = &environClient{
    50  		conn: conn,
    51  		uuid: uuid,
    52  	}
    53  
    54  	return client, nil
    55  }
    56  
    57  const (
    58  	jujuMetaInstance           = "juju-instance"
    59  	jujuMetaInstanceController = "controller"
    60  	jujuMetaInstanceServer     = "server"
    61  
    62  	jujuMetaEnvironment = "juju-model"
    63  	jujuMetaCoudInit    = "cloudinit-user-data"
    64  	jujuMetaBase64      = "base64_fields"
    65  )
    66  
    67  func (c *environClient) isMyEnvironment(s gosigma.Server) bool {
    68  	if v, ok := s.Get(jujuMetaEnvironment); ok && c.uuid == v {
    69  		return true
    70  	}
    71  	return false
    72  }
    73  
    74  func (c *environClient) isMyServer(s gosigma.Server) bool {
    75  	if _, ok := s.Get(jujuMetaInstance); ok {
    76  		return c.isMyEnvironment(s)
    77  	}
    78  	return false
    79  }
    80  
    81  // isMyController is used to filter servers in the CloudSigma account
    82  func (c environClient) isMyController(s gosigma.Server) bool {
    83  	if v, ok := s.Get(jujuMetaInstance); ok && v == jujuMetaInstanceController {
    84  		return c.isMyEnvironment(s)
    85  	}
    86  	return false
    87  }
    88  
    89  // instances returns a list of CloudSigma servers for this environment
    90  func (c *environClient) instances() ([]gosigma.Server, error) {
    91  	return c.conn.ServersFiltered(gosigma.RequestDetail, c.isMyServer)
    92  }
    93  
    94  // instanceMap of server ids to servers at CloudSigma account
    95  func (c *environClient) instanceMap() (map[string]gosigma.Server, error) {
    96  	servers, err := c.conn.ServersFiltered(gosigma.RequestDetail, c.isMyServer)
    97  	if err != nil {
    98  		return nil, errors.Trace(err)
    99  	}
   100  
   101  	m := make(map[string]gosigma.Server, len(servers))
   102  	for _, s := range servers {
   103  		m[s.UUID()] = s
   104  	}
   105  
   106  	return m, nil
   107  }
   108  
   109  //getControllerIds get list of ids for all controller instances
   110  func (c *environClient) getControllerIds() (ids []instance.Id, err error) {
   111  	logger.Tracef("query state...")
   112  
   113  	servers, err := c.conn.ServersFiltered(gosigma.RequestDetail, c.isMyController)
   114  	if err != nil {
   115  		return []instance.Id{}, errors.Trace(err)
   116  	}
   117  
   118  	if len(servers) == 0 {
   119  		return []instance.Id{}, environs.ErrNotBootstrapped
   120  	}
   121  
   122  	ids = make([]instance.Id, len(servers))
   123  
   124  	for i, server := range servers {
   125  		logger.Tracef("controller id: %s", server.UUID())
   126  		ids[i] = instance.Id(server.UUID())
   127  	}
   128  
   129  	return ids, nil
   130  }
   131  
   132  //stopInstance stops the CloudSigma server corresponding to the given instance ID.
   133  func (c *environClient) stopInstance(id instance.Id) error {
   134  	uuid := string(id)
   135  	if uuid == "" {
   136  		return errors.New("invalid instance id")
   137  	}
   138  
   139  	s, err := c.conn.Server(uuid)
   140  	if err != nil {
   141  		return errors.Trace(err)
   142  	}
   143  
   144  	err = s.StopWait()
   145  	logger.Tracef("environClient.StopInstance - stop server, %q = %v", uuid, err)
   146  
   147  	err = s.Remove(gosigma.RecurseAllDrives)
   148  	logger.Tracef("environClient.StopInstance - remove server, %q = %v", uuid, err)
   149  
   150  	return nil
   151  }
   152  
   153  //newInstance creates and starts new instance.
   154  func (c *environClient) newInstance(
   155  	args environs.StartInstanceParams,
   156  	img *imagemetadata.ImageMetadata,
   157  	userData []byte,
   158  	authorizedKeys string,
   159  ) (srv gosigma.Server, drv gosigma.Drive, ar string, err error) {
   160  
   161  	defer func() {
   162  		if err == nil {
   163  			return
   164  		}
   165  		if srv != nil {
   166  			srv.Remove(gosigma.RecurseAllDrives)
   167  		} else if drv != nil {
   168  			drv.Remove()
   169  		}
   170  		srv = nil
   171  		drv = nil
   172  	}()
   173  
   174  	if args.InstanceConfig == nil {
   175  		err = errors.New("invalid configuration for new instance: InstanceConfig is nil")
   176  		return nil, nil, "", err
   177  	}
   178  
   179  	logger.Debugf("Tools: %v", args.Tools.URLs())
   180  	logger.Debugf("Juju Constraints:" + args.Constraints.String())
   181  	logger.Debugf("InstanceConfig: %#v", args.InstanceConfig)
   182  
   183  	constraints := newConstraints(args.InstanceConfig.Bootstrap != nil, args.Constraints, img)
   184  	logger.Debugf("CloudSigma Constraints: %v", constraints)
   185  
   186  	originalDrive, err := c.conn.Drive(constraints.driveTemplate, gosigma.LibraryMedia)
   187  	if err != nil {
   188  		err = errors.Annotatef(err, "Failed to query drive template")
   189  		return nil, nil, "", err
   190  	}
   191  
   192  	baseName := "juju-" + c.uuid + "-" + args.InstanceConfig.MachineId
   193  
   194  	cloneParams := gosigma.CloneParams{Name: baseName}
   195  	if drv, err = originalDrive.CloneWait(cloneParams, nil); err != nil {
   196  		err = errors.Errorf("error cloning drive: %v", err)
   197  		return nil, nil, "", err
   198  	}
   199  
   200  	if drv.Size() < constraints.driveSize {
   201  		if err = drv.ResizeWait(constraints.driveSize); err != nil {
   202  			err = errors.Errorf("error resizing drive: %v", err)
   203  			return nil, nil, "", err
   204  		}
   205  	}
   206  
   207  	cc, err := c.generateSigmaComponents(
   208  		baseName, constraints, args, drv, userData, authorizedKeys,
   209  	)
   210  	if err != nil {
   211  		return nil, nil, "", errors.Trace(err)
   212  	}
   213  
   214  	if srv, err = c.conn.CreateServer(cc); err != nil {
   215  		err = errors.Annotatef(err, "error creating new instance")
   216  		return nil, nil, "", err
   217  	}
   218  
   219  	if err = srv.Start(); err != nil {
   220  		err = errors.Annotatef(err, "error booting new instance")
   221  		return nil, nil, "", err
   222  	}
   223  
   224  	// populate root drive hardware characteristics
   225  	switch originalDrive.Arch() {
   226  	case "64":
   227  		ar = arch.AMD64
   228  	case "32":
   229  		ar = arch.I386
   230  	default:
   231  		err = errors.Errorf("unknown arch: %v", ar)
   232  		return nil, nil, "", err
   233  	}
   234  
   235  	return srv, drv, ar, nil
   236  }
   237  
   238  func (c *environClient) generateSigmaComponents(
   239  	baseName string,
   240  	constraints *sigmaConstraints,
   241  	args environs.StartInstanceParams,
   242  	drv gosigma.Drive,
   243  	userData []byte,
   244  	authorizedKeys string,
   245  ) (cc gosigma.Components, err error) {
   246  	cc.SetName(baseName)
   247  	cc.SetDescription(baseName)
   248  	cc.SetSMP(constraints.cores)
   249  	cc.SetCPU(constraints.power)
   250  	cc.SetMem(constraints.mem)
   251  
   252  	vncpass, err := utils.RandomPassword()
   253  	if err != nil {
   254  		err = errors.Errorf("error generating password: %v", err)
   255  		return
   256  	}
   257  	cc.SetVNCPassword(vncpass)
   258  	logger.Debugf("Setting ssh key: %s end", authorizedKeys)
   259  	cc.SetSSHPublicKey(authorizedKeys)
   260  	cc.AttachDrive(1, "0:0", "virtio", drv.UUID())
   261  	cc.NetworkDHCP4(gosigma.ModelVirtio)
   262  
   263  	if multiwatcher.AnyJobNeedsState(args.InstanceConfig.Jobs...) {
   264  		cc.SetMeta(jujuMetaInstance, jujuMetaInstanceController)
   265  	} else {
   266  		cc.SetMeta(jujuMetaInstance, jujuMetaInstanceServer)
   267  	}
   268  
   269  	cc.SetMeta(jujuMetaEnvironment, c.uuid)
   270  	cc.SetMeta(jujuMetaCoudInit, string(userData))
   271  	cc.SetMeta(jujuMetaBase64, jujuMetaCoudInit)
   272  
   273  	return cc, nil
   274  }