func (c *client) getExecutorChecksum(ctx types.Context) (string, error) {

	if c.isController() {
		return "", utils.NewUnsupportedForClientTypeError(
			c.clientType, "getExecutorChecksum")
	}

	ctx.Debug("getting executor checksum")

	f, err := os.Open(types.LSX.String())
	if err != nil {
		return "", err
	}
	defer f.Close()

	h := md5.New()
	buf := make([]byte, 1024)
	for {
		n, err := f.Read(buf)
		if err == io.EOF {
			break
		}
		if err != nil {
			return "", err
		}
		if _, err := h.Write(buf[:n]); err != nil {
			return "", err
		}
	}

	sum := fmt.Sprintf("%x", h.Sum(nil))
	ctx.WithField("localChecksum", sum).Debug("got local executor checksum")
	return sum, nil
}
Beispiel #2
0
// initDefaultLibStorageServices initializes the config object with a default
// libStorage service if one is not present.
//
// TODO Move this into libStorage in libStorage 0.1.2
func initDefaultLibStorageServices(
	ctx apitypes.Context, config gofig.Config) error {

	if config.IsSet(apitypes.ConfigServices) {
		ctx.Debug(
			"libStorage auto service mode disabled; services defined")
		return nil
	}

	serviceName := config.GetString(apitypes.ConfigService)
	if serviceName == "" {
		ctx.Debug(
			"libStorage auto service mode disabled; service name empty")
		return nil
	}

	ctx.WithField("driver", serviceName).Info(
		"libStorage auto service mode enabled")

	buf := &bytes.Buffer{}
	fmt.Fprintf(buf, defaultServiceConfigFormat, serviceName)

	if err := config.ReadConfig(buf); err != nil {
		return err
	}

	return nil
}
func (c *client) downloadExecutor(ctx types.Context) error {

	if c.isController() {
		return utils.NewUnsupportedForClientTypeError(
			c.clientType, "downloadExecutor")
	}

	ctx.Debug("downloading executor")

	f, err := os.OpenFile(
		types.LSX.String(),
		os.O_CREATE|os.O_RDWR|os.O_TRUNC,
		0755)
	if err != nil {
		return err
	}

	defer f.Close()

	rdr, err := c.APIClient.ExecutorGet(ctx, types.LSX.Name())
	n, err := io.Copy(f, rdr)
	if err != nil {
		return err
	}

	if err := f.Sync(); err != nil {
		return err
	}

	ctx.WithField("bytes", n).Debug("downloaded executor")
	return nil
}
func (c *client) NextDevice(
	ctx types.Context,
	opts types.Store) (string, error) {

	if c.isController() {
		return "", utils.NewUnsupportedForClientTypeError(
			c.clientType, "NextDevice")
	}

	if supported, _ := c.Supported(ctx, opts); !supported {
		return "", errExecutorNotSupported
	}

	ctx = context.RequireTX(ctx.Join(c.ctx))

	serviceName, ok := context.ServiceName(ctx)
	if !ok {
		return "", goof.New("missing service name")
	}

	si, err := c.getServiceInfo(serviceName)
	if err != nil {
		return "", err
	}
	driverName := si.Driver.Name

	out, err := c.runExecutor(ctx, driverName, types.LSXCmdNextDevice)
	if err != nil {
		return "", err
	}

	ctx.Debug("xli nextdevice success")
	return gotil.Trim(string(out)), nil
}
func (c *client) runExecutor(
	ctx types.Context, args ...string) ([]byte, error) {

	if c.isController() {
		return nil, utils.NewUnsupportedForClientTypeError(
			c.clientType, "runExecutor")
	}

	ctx.Debug("waiting on executor lock")
	if err := c.lsxMutexWait(); err != nil {
		return nil, err
	}

	defer func() {
		ctx.Debug("signalling executor lock")
		if err := c.lsxMutexSignal(); err != nil {
			panic(err)
		}
	}()

	lsxBin := types.LSX.String()
	cmd := exec.Command(lsxBin, args...)
	cmd.Env = os.Environ()

	configEnvVars := c.config.EnvVars()
	for _, cev := range configEnvVars {
		// ctx.WithField("value", cev).Debug("set executor env var")
		cmd.Env = append(cmd.Env, cev)
	}

	out, err := cmd.Output()

	if exitError, ok := err.(*exec.ExitError); ok {
		exitCode := exitError.Sys().(syscall.WaitStatus).ExitStatus()
		switch exitCode {
		case types.LSXExitCodeNotImplemented:
			return nil, types.ErrNotImplemented
		case types.LSXExitCodeTimedOut:
			return nil, types.ErrTimedOut
		}
		return nil, goof.WithFieldsE(
			map[string]interface{}{
				"lsx":  lsxBin,
				"args": args,
			},
			"error executing xcli",
			err)
	}

	return out, err
}
Beispiel #6
0
func waitUntilLibStorageStopped(ctx apitypes.Context, errs <-chan error) {
	ctx.Debug("waiting until libStorage is stopped")

	// if there is no err channel then do not wait until libStorage is stopped
	// as the absence of the err channel means libStorage was not started in
	// embedded mode
	if errs == nil {
		ctx.Debug("done waiting on err chan; err chan is nil")
		return
	}

	// in a goroutine, range over the apiserver.Close channel until it's closed
	for range apiserver.Close() {
	}
	ctx.Debug("done sending close signals to libStorage")

	// block until the err channel is closed
	for err := range errs {
		if err == nil {
			continue
		}
		ctx.WithError(err).Error("error on closing libStorage server")
	}
	ctx.Debug("done waiting on err chan")
}
func (d *idm) Remove(
	ctx types.Context,
	volumeName string,
	opts types.Store) error {

	fields := log.Fields{
		"volumeName": volumeName,
		"opts":       opts}
	ctx.WithFields(fields).Debug("removing volume")

	if d.disableRemove() {
		ctx.Debug("disableRemove skipped deletion")
		return nil
	}
	return d.IntegrationDriver.Remove(ctx.Join(d.ctx), volumeName, opts)
}
func (d *idm) Create(
	ctx types.Context,
	volumeName string,
	opts *types.VolumeCreateOpts) (*types.Volume, error) {

	fields := log.Fields{
		"volumeName": volumeName,
		"opts":       opts}
	ctx.WithFields(fields).Debug("creating volume")

	if d.disableCreate() {
		ctx.Debug("disableRemove skipped creation")
		return nil, nil
	}
	return d.IntegrationDriver.Create(ctx.Join(d.ctx), volumeName, opts)
}
func (c *client) updateExecutor(ctx types.Context) error {

	if c.isController() {
		return utils.NewUnsupportedForClientTypeError(
			c.clientType, "updateExecutor")
	}

	ctx.Debug("updating executor")

	lsxi := c.lsxCache.GetExecutorInfo(types.LSX.Name())
	if lsxi == nil {
		return goof.WithField("lsx", types.LSX, "unknown executor")
	}

	ctx.Debug("waiting on executor lock")
	if err := c.lsxMutexWait(); err != nil {
		return err
	}
	defer func() {
		ctx.Debug("signalling executor lock")
		if err := c.lsxMutexSignal(); err != nil {
			panic(err)
		}
	}()

	if !types.LSX.Exists() {
		ctx.Debug("executor does not exist, download executor")
		return c.downloadExecutor(ctx)
	}

	ctx.Debug("executor exists, getting local checksum")

	checksum, err := c.getExecutorChecksum(ctx)
	if err != nil {
		return err
	}

	if lsxi.MD5Checksum != checksum {
		ctx.WithFields(log.Fields{
			"remoteChecksum": lsxi.MD5Checksum,
			"localChecksum":  checksum,
		}).Debug("executor checksums do not match, download executor")
		return c.downloadExecutor(ctx)
	}

	return nil
}
func (c *client) WaitForDevice(
	ctx types.Context,
	opts *types.WaitForDeviceOpts) (bool, *types.LocalDevices, error) {

	if c.isController() {
		return false, nil, utils.NewUnsupportedForClientTypeError(
			c.clientType, "WaitForDevice")
	}

	if supported, _ := c.Supported(ctx, opts.Opts); !supported {
		return false, nil, errExecutorNotSupported
	}

	ctx = context.RequireTX(ctx.Join(c.ctx))

	serviceName, ok := context.ServiceName(ctx)
	if !ok {
		return false, nil, goof.New("missing service name")
	}

	si, err := c.getServiceInfo(serviceName)
	if err != nil {
		return false, nil, err
	}
	driverName := si.Driver.Name

	out, err := c.runExecutor(
		ctx, driverName, types.LSXCmdWaitForDevice,
		opts.ScanType.String(), opts.Token, opts.Timeout.String())

	if err != types.ErrTimedOut {
		return false, nil, err
	}

	matched := err == nil

	ld, err := unmarshalLocalDevices(ctx, out)
	if err != nil {
		return false, nil, err
	}

	ctx.Debug("xli waitfordevice success")
	return matched, ld, nil
}
func (c *client) LocalDevices(
	ctx types.Context,
	opts *types.LocalDevicesOpts) (*types.LocalDevices, error) {

	if c.isController() {
		return nil, utils.NewUnsupportedForClientTypeError(
			c.clientType, "LocalDevices")
	}

	if supported, _ := c.Supported(ctx, opts.Opts); !supported {
		return nil, errExecutorNotSupported
	}

	ctx = context.RequireTX(ctx.Join(c.ctx))

	serviceName, ok := context.ServiceName(ctx)
	if !ok {
		return nil, goof.New("missing service name")
	}

	si, err := c.getServiceInfo(serviceName)
	if err != nil {
		return nil, err
	}
	driverName := si.Driver.Name

	out, err := c.runExecutor(
		ctx, driverName, types.LSXCmdLocalDevices, opts.ScanType.String())
	if err != nil {
		return nil, err
	}

	ld, err := unmarshalLocalDevices(ctx, out)
	if err != nil {
		return nil, err
	}

	ctx.Debug("xli localdevices success")
	return ld, nil
}
Beispiel #12
0
// Mount will return a mount point path when specifying either a volumeName
// or volumeID.  If a overwriteFs boolean is specified it will overwrite
// the FS based on newFsType if it is detected that there is no FS present.
func (d *driver) Mount(
	ctx types.Context,
	volumeID, volumeName string,
	opts *types.VolumeMountOpts) (string, *types.Volume, error) {

	ctx.WithFields(log.Fields{
		"volumeName": volumeName,
		"volumeID":   volumeID,
		"opts":       opts}).Info("mounting volume")

	vol, err := d.volumeInspectByIDOrName(
		ctx, volumeID, volumeName,
		types.VolAttReqWithDevMapOnlyVolsAttachedToInstanceOrUnattachedVols,
		opts.Opts)
	if isErrNotFound(err) && d.volumeCreateImplicit() {
		var err error
		if vol, err = d.Create(ctx, volumeName, &types.VolumeCreateOpts{
			Opts: utils.NewStore(),
		}); err != nil {
			return "", nil, goof.WithError(
				"problem creating volume implicitly", err)
		}
	} else if err != nil {
		return "", nil, err
	}

	if vol == nil {
		return "", nil, goof.New("no volume returned or created")
	}

	client := context.MustClient(ctx)
	if len(vol.Attachments) == 0 || opts.Preempt {
		mp, err := d.getVolumeMountPath(vol.Name)
		if err != nil {
			return "", nil, err
		}

		ctx.Debug("performing precautionary unmount")
		_ = client.OS().Unmount(ctx, mp, opts.Opts)

		var token string
		vol, token, err = client.Storage().VolumeAttach(
			ctx, vol.ID, &types.VolumeAttachOpts{
				Force: opts.Preempt,
				Opts:  utils.NewStore(),
			})
		if err != nil {
			return "", nil, err
		}

		if token != "" {
			opts := &types.WaitForDeviceOpts{
				LocalDevicesOpts: types.LocalDevicesOpts{
					ScanType: apiconfig.DeviceScanType(d.config),
					Opts:     opts.Opts,
				},
				Token:   token,
				Timeout: apiconfig.DeviceAttachTimeout(d.config),
			}

			_, _, err = client.Executor().WaitForDevice(ctx, opts)
			if err != nil {
				return "", nil, goof.WithError(
					"problem with device discovery", err)
			}
		}

		vol, err = d.volumeInspectByIDOrName(
			ctx, vol.ID, "", types.VolAttReqTrue, opts.Opts)
		if err != nil {
			return "", nil, err
		}

	}

	if len(vol.Attachments) == 0 {
		return "", nil, goof.New("volume did not attach")
	}

	inst, err := client.Storage().InstanceInspect(ctx, utils.NewStore())
	if err != nil {
		return "", nil, goof.New("problem getting instance ID")
	}
	var ma *types.VolumeAttachment
	for _, att := range vol.Attachments {
		if att.InstanceID.ID == inst.InstanceID.ID {
			ma = att
			break
		}
	}

	if ma == nil {
		return "", nil, goof.New("no local attachment found")
	}

	if ma.DeviceName == "" {
		return "", nil, goof.New("no device name returned")
	}

	mounts, err := client.OS().Mounts(
		ctx, ma.DeviceName, "", opts.Opts)
	if err != nil {
		return "", nil, err
	}

	if len(mounts) > 0 {
		return d.volumeMountPath(mounts[0].MountPoint), vol, nil
	}

	if opts.NewFSType == "" {
		opts.NewFSType = d.fsType()
	}

	if err := client.OS().Format(
		ctx,
		ma.DeviceName,
		&types.DeviceFormatOpts{
			NewFSType:   opts.NewFSType,
			OverwriteFS: opts.OverwriteFS,
		}); err != nil {
		return "", nil, err
	}

	mountPath, err := d.getVolumeMountPath(vol.Name)
	if err != nil {
		return "", nil, err
	}

	if err := os.MkdirAll(mountPath, 0755); err != nil {
		return "", nil, err
	}

	if err := client.OS().Mount(
		ctx,
		ma.DeviceName,
		mountPath,
		&types.DeviceMountOpts{}); err != nil {
		return "", nil, err
	}

	mntPath := d.volumeMountPath(mountPath)

	fields := log.Fields{
		"vol":     vol,
		"mntPath": mntPath,
	}
	ctx.WithFields(fields).Info("volume mounted")

	return mntPath, vol, nil
}
Beispiel #13
0
// Used in VolumeCreate
func (d *driver) createVolume(
	ctx types.Context,
	volumeName, snapshotID string,
	opts *types.VolumeCreateOpts) (*awsec2.Volume, error) {

	var (
		err    error
		server awsec2.Instance
	)
	// Create volume using EC2 API call
	if server, err = d.getInstance(ctx); err != nil {
		return &awsec2.Volume{}, goof.WithError(
			"error creating volume with EC2 API call", err)
	}

	// Fill in Availability Zone if needed
	d.createVolumeEnsureAvailabilityZone(opts.AvailabilityZone, &server)

	options := &awsec2.CreateVolumeInput{
		Size:             opts.Size,
		AvailabilityZone: opts.AvailabilityZone,
		Encrypted:        opts.Encrypted,
		VolumeType:       opts.Type,
	}
	if snapshotID != "" {
		options.SnapshotId = &snapshotID
	}
	if opts.IOPS != nil && *opts.IOPS > 0 {
		options.Iops = opts.IOPS
	}
	if opts.Encrypted != nil && *opts.Encrypted {
		if opts.EncryptionKey != nil && len(*opts.EncryptionKey) > 0 {
			ctx.Debug("creating encrypted volume w client enc key")
			options.KmsKeyId = opts.EncryptionKey
		} else if len(d.kmsKeyID) > 0 {
			ctx.Debug("creating encrypted volume w server enc key")
			options.KmsKeyId = aws.String(d.kmsKeyID)
		} else {
			ctx.Debug("creating encrypted volume w default enc key")
		}
	}

	var resp *awsec2.Volume

	if resp, err = mustSession(ctx).CreateVolume(options); err != nil {
		return &awsec2.Volume{}, goof.WithError(
			"error creating volume", err)
	}

	// Add tags to created volume
	if err = d.createTags(ctx, *resp.VolumeId, volumeName); err != nil {
		return &awsec2.Volume{}, goof.WithError(
			"error creating tags", err)
	}

	// Wait for volume status to change
	if err = d.waitVolumeComplete(
		ctx, *resp.VolumeId, waitVolumeCreate); err != nil {
		return &awsec2.Volume{}, goof.WithError(
			"error waiting for volume creation", err)
	}

	return resp, nil
}
func (c *client) InstanceID(
	ctx types.Context,
	opts types.Store) (*types.InstanceID, error) {

	if c.isController() {
		return nil, utils.NewUnsupportedForClientTypeError(
			c.clientType, "InstanceID")
	}

	if supported, _ := c.Supported(ctx, opts); !supported {
		return nil, errExecutorNotSupported
	}

	ctx = context.RequireTX(ctx.Join(c.ctx))

	serviceName, ok := context.ServiceName(ctx)
	if !ok {
		return nil, goof.New("missing service name")
	}

	si, err := c.getServiceInfo(serviceName)
	if err != nil {
		return nil, err
	}
	driverName := strings.ToLower(si.Driver.Name)

	// check to see if the driver's instance ID is cached
	if iid := c.instanceIDCache.GetInstanceID(driverName); iid != nil {
		return iid, nil
	}

	out, err := c.runExecutor(ctx, driverName, types.LSXCmdInstanceID)
	if err != nil {
		return nil, err
	}

	iid := &types.InstanceID{}
	if err := iid.UnmarshalText(out); err != nil {
		return nil, err
	}

	ctx = ctx.WithValue(context.InstanceIDKey, iid)

	if iid.HasMetadata() {
		ctx.Debug("sending instanceID in API.InstanceInspect call")
		instance, err := c.InstanceInspect(ctx, serviceName)
		if err != nil {
			return nil, err
		}
		ctx.Debug("received instanceID from API.InstanceInspect call")
		iid.ID = instance.InstanceID.ID
		iid.Fields = instance.InstanceID.Fields
		iid.DeleteMetadata()
	}

	c.instanceIDCache.Set(driverName, iid)
	ctx.Debug("cached instanceID")

	ctx.Debug("xli instanceID success")
	return iid, nil
}
Beispiel #15
0
func activateLibStorage(
	ctx apitypes.Context,
	config gofig.Config) (apitypes.Context, gofig.Config, <-chan error, error) {

	apiserver.DisableStartupInfo = true

	var (
		host          string
		err           error
		isRunning     bool
		errs          chan error
		serverErrChan <-chan error
		server        apitypes.Server
	)

	if host = config.GetString(apitypes.ConfigHost); host != "" {
		if !config.GetBool(apitypes.ConfigEmbedded) {
			ctx.WithField(
				"host", host,
			).Debug("not starting embedded server; embedded mode disabled")
			return ctx, config, nil, nil
		}
	}

	if host, isRunning = IsLocalServerActive(ctx, config); isRunning {
		ctx = setHost(ctx, config, host)
		ctx.WithField("host", host).Debug(
			"not starting embedded server; already running")
		return ctx, config, nil, nil
	}

	// if no host was specified then see if a set of default services need to
	// be initialized
	if host == "" {
		ctx.Debug("host is empty; initiliazing default services")
		if err = initDefaultLibStorageServices(ctx, config); err != nil {
			ctx.WithError(err).Error("error initializing default services")
			return ctx, config, nil, err
		}
	}

	ctx.Debug("starting embedded libStorage server")

	if server, serverErrChan, err = apiserver.Serve(ctx, config); err != nil {
		ctx.WithError(err).Error("error starting libStorage server")
		return ctx, config, nil, err
	}

	if host == "" {
		host = server.Addrs()[0]
		ctx.WithField("host", host).Debug("got host from new server address")
	}
	ctx = setHost(ctx, config, host)

	errs = make(chan error)
	go func() {
		for err := range serverErrChan {
			if err != nil {
				errs <- err
			}
		}
		if err := os.RemoveAll(SpecFilePath()); err == nil {
			logHostSpec(ctx, host, "removed spec file")
		}
		close(errs)
	}()

	// write the host to the spec file so that other rex-ray invocations can
	// find it, even if running as an embedded libStorage server
	if err := WriteSpecFile(host); err != nil {
		specFile := SpecFilePath()
		if os.IsPermission(err) {
			ctx.WithError(err).Errorf(
				"user does not have write permissions for %s", specFile)
		} else {
			ctx.WithError(err).Errorf(
				"error writing spec file at %s", specFile)
		}
		//WaitUntilLibStorageStopped(ctx, serverErrChan)
		return ctx, config, errs, err
	}
	logHostSpec(ctx, host, "created spec file")

	return ctx, config, errs, nil
}