<commit_before>package openstack

import (
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path"
	"strings"

	log "github.com/Sirupsen/logrus"
	flag "github.com/docker/docker/pkg/mflag"
	"github.com/docker/docker/utils"
	"github.com/docker/machine/drivers"
	"github.com/docker/machine/ssh"
	"github.com/docker/machine/state"
)

type Driver struct {
	AuthUrl        string
	Username       string
	Password       string
	TenantName     string
	TenantId       string
	Region         string
	EndpointType   string
	MachineName    string
	MachineId      string
	FlavorId       string
	ImageId        string
	KeyPairName    string
	NetworkId      string
	SecurityGroups []string
	FloatingIpPool string
	SSHUser        string
	SSHPort        int
	storePath      string
	client         *Client
}

type CreateFlags struct {
	AuthUrl        *string
	Username       *string
	Password       *string
	TenantName     *string
	TenantId       *string
	Region         *string
	EndpointType   *string
	FlavorId       *string
	ImageId        *string
	NetworkId      *string
	SecurityGroups *string
	FloatingIpPool *string
	SSHUser        *string
	SSHPort        *int
}

func init() {
	drivers.Register("openstack", &drivers.RegisteredDriver{
		New:                 NewDriver,
		RegisterCreateFlags: RegisterCreateFlags,
	})
}

func RegisterCreateFlags(cmd *flag.FlagSet) interface{} {
	createFlags := new(CreateFlags)
	createFlags.AuthUrl = cmd.String(
		[]string{"-openstack-auth-url"},
		os.Getenv("OS_AUTH_URL"),
		"OpenStack authentication URL",
	)
	createFlags.Username = cmd.String(
		[]string{"-openstack-username"},
		os.Getenv("OS_USERNAME"),
		"OpenStack username",
	)
	createFlags.Password = cmd.String(
		[]string{"-openstack-password"},
		os.Getenv("OS_PASSWORD"),
		"OpenStack password",
	)
	createFlags.TenantName = cmd.String(
		[]string{"-openstack-tenant-name"},
		os.Getenv("OS_TENANT_NAME"),
		"OpenStack tenant name",
	)
	createFlags.TenantId = cmd.String(
		[]string{"-openstack-tenant-id"},
		os.Getenv("OS_TENANT_ID"),
		"OpenStack tenant id",
	)
	createFlags.Region = cmd.String(
		[]string{"-openstack-region"},
		os.Getenv("OS_REGION_NAME"),
		"OpenStack region name",
	)
	createFlags.EndpointType = cmd.String(
		[]string{"-openstack-endpoint-type"},
		os.Getenv("OS_ENDPOINT_TYPE"),
		"OpenStack endpoint type (adminURL, internalURL or the default publicURL)",
	)
	createFlags.FlavorId = cmd.String(
		[]string{"-openstack-flavor-id"},
		"",
		"OpenStack flavor id to use for the instance",
	)
	createFlags.ImageId = cmd.String(
		[]string{"-openstack-image-id"},
		"",
		"OpenStack image id to use for the instance",
	)
	createFlags.NetworkId = cmd.String(
		[]string{"-openstack-net-id"},
		"",
		"OpenStack network id the machine will be connected on",
	)
	createFlags.SecurityGroups = cmd.String(
		[]string{"-openstack-sec-groups"},
		"",
		"OpenStack comma separated security groups for the machine",
	)
	createFlags.FloatingIpPool = cmd.String(
		[]string{"-openstack-floatingip-pool"},
		"",
		"OpenStack floating IP pool to get an IP from to assign to the instance",
	)
	createFlags.SSHUser = cmd.String(
		[]string{"-openstack-ssh-user"},
		"root",
		"OpenStack SSH user. Set to root by default",
	)
	createFlags.SSHPort = cmd.Int(
		[]string{"-openstack-ssh-port"},
		22,
		"OpenStack SSH port. Set to 22 by default",
	)
	return createFlags
}

func NewDriver(storePath string) (drivers.Driver, error) {
	log.WithFields(log.Fields{
		"storePath": storePath,
	}).Info("Instanciate OpenStack driver...")
	return &Driver{
		storePath: storePath,
		client:    &Client{},
	}, nil
}

func (d *Driver) DriverName() string {
	return "openstack"
}

func (d *Driver) SetConfigFromFlags(flagsInterface interface{}) error {
	flags := flagsInterface.(*CreateFlags)
	d.AuthUrl = *flags.AuthUrl
	d.Username = *flags.Username
	d.Password = *<PASSWORD>
	d.TenantName = *flags.TenantName
	d.TenantId = *flags.TenantId
	d.Region = *flags.Region
	d.EndpointType = *flags.EndpointType
	d.FlavorId = *flags.FlavorId
	d.ImageId = *flags.ImageId
	d.NetworkId = *flags.NetworkId
	if *flags.SecurityGroups != "" {
		d.SecurityGroups = strings.Split(*flags.SecurityGroups, ",")
	}
	d.FloatingIpPool = *flags.FloatingIpPool
	d.SSHUser = *flags.SSHUser
	d.SSHPort = *flags.SSHPort
	return d.checkConfig()
}

func (d *Driver) GetURL() (string, error) {
	ip, err := d.GetIP()
	if err != nil {
		return "", err
	}
	if ip == "" {
		return "", nil
	}
	return fmt.Sprintf("tcp://%s:2376", ip), nil
}

func (d *Driver) GetIP() (string, error) {
	addresses, err := d.client.GetInstanceIpAddresses(d)
	if err != nil {
		return "", err
	}

	floating := []string{}
	fixed := []string{}

	for _, address := range addresses {
		if address.AddressType == Floating {
			floating = append(floating, address.Address)
			continue
		}
		if address.AddressType == Fixed {
			fixed = append(fixed, address.Address)
			continue
		}
		log.Warnf("Unknown IP address type : %s", address)
	}

	if len(floating) == 1 {
		return d.foundIP(floating[0]), nil
	} else if len(floating) > 1 {
		log.Warnf("Multiple floating IP found. Take the first one of %s", floating)
		return d.foundIP(floating[0]), nil
	}

	if len(fixed) == 1 {
		return d.foundIP(fixed[0]), nil
	} else if len(fixed) > 1 {
		log.Warnf("Multiple fixed IP found. Take the first one of %s", floating)
		return d.foundIP(fixed[0]), nil
	}

	return "", fmt.Errorf("No IP found for the machine")
}

func (d *Driver) GetState() (state.State, error) {

	log.WithField("MachineId", d.MachineId).Info("Get status for OpenStack instance...")

	s, err := d.client.GetInstanceState(d)
	if err != nil {
		return state.None, err
	}

	log.WithFields(log.Fields{
		"MachineId": d.MachineId,
		"State":     s,
	}).Info("State for OpenStack instance")

	switch s {
	case "ACTIVE":
		return state.Running, nil
	case "PAUSED":
		return state.Paused, nil
	case "SUSPENDED":
		return state.Saved, nil
	case "SHUTOFF":
		return state.Stopped, nil
	case "BUILDING":
		return state.Starting, nil
	case "ERROR":
		return state.Error, nil
	}
	return state.None, nil
}

func (d *Driver) Create() error {

	d.setMachineNameIfNotSet()
	d.KeyPairName = d.MachineName

	if err := d.createSSHKey(); err != nil {
		return err
	}
	if err := d.createMachine(); err != nil {
		return err
	}
	if err := d.waitForInstanceToStart(); err != nil {
		return err
	}
	if err := d.installDocker(); err != nil {
		return err
	}
	return nil
}

func (d *Driver) Start() error {
	log.WithField("MachineId", d.MachineId).Info("Starting OpenStack instance...")
	if err := d.client.StartInstance(d); err != nil {
		return err
	}
	return d.waitForInstanceToStart()
}

func (d *Driver) Stop() error {

	log.WithField("MachineId", d.MachineId).Info("Stopping OpenStack instance...")
	if err := d.client.StopInstance(d); err != nil {
		return err
	}

	log.WithField("MachineId", d.MachineId).Info("Waiting for the OpenStack instance to stop...")
	if err := d.client.WaitForInstanceStatus(d, "SHUTOFF", 200); err != nil {
		return err
	}
	return nil
}

func (d *Driver) Remove() error {
	log.WithField("MachineId", d.MachineId).Info("Deleting OpenStack instance...")
	if err := d.client.DeleteInstance(d); err != nil {
		return err
	}
	log.WithField("Name", d.KeyPairName).Info("Deleting Key Pair...")
	if err := d.client.DeleteKeyPair(d, d.KeyPairName); err != nil {
		return err
	}
	return nil
}

func (d *Driver) Restart() error {
	log.WithField("MachineId", d.MachineId).Info("Restarting OpenStack instance...")
	if err := d.client.RestartInstance(d); err != nil {
		return err
	}
	return d.waitForInstanceToStart()
}

func (d *Driver) Kill() error {
	return d.Stop()
}

func (d *Driver) Upgrade() error {
	return fmt.Errorf("Upgrate is currently not available for the OpenStack driver")
}

func (d *Driver) GetSSHCommand(args ...string) (*exec.Cmd, error) {
	ip, err := d.GetIP()
	if err != nil {
		return nil, err
	}
	return ssh.GetSSHCommand(ip, d.SSHPort, d.SSHUser, d.sshKeyPath(), args...), nil
}

const (
	errorMandatoryEnvOrOption    string = "%s must be specified either using the environment variable %s or the CLI option %s"
	errorMandatoryOption         string = "%s must be specified using the CLI option %s"
	errorExclusiveOptions        string = "Either %s or %s must be specified, not both"
	errorMandatoryTenantNameOrId string = "Tenant id or name must be provided either using one of the environment variables OS_TENANT_ID and OS_TENANT_NAME or one of the CLI options --openstack-tenant-id and --openstack-tenant-name"
	errorWrongEndpointType       string = "Endpoint type must be 'publicURL', 'adminURL' or 'internalURL'"
)

func (d *Driver) checkConfig() error {
	if d.AuthUrl == "" {
		return fmt.Errorf(errorMandatoryEnvOrOption, "Autentication URL", "OS_AUTH_URL", "--openstack-auth-url")
	}
	if d.Username == "" {
		return fmt.Errorf(errorMandatoryEnvOrOption, "Username", "OS_USERNAME", "--openstack-username")
	}
	if d.Password == "" {
		return fmt.Errorf(errorMandatoryEnvOrOption, "Password", "OS_PASSWORD", "--openstack-password")
	}
	if d.TenantName == "" && d.TenantId == "" {
		return fmt.Errorf(errorMandatoryTenantNameOrId)
	}
	if d.TenantName != "" && d.TenantId != "" {
		return fmt.Errorf(errorExclusiveOptions, "tenant id", "tenant name")<commit_msg>OpenStack: Allow to provide both tenant name and id<commit_after>package openstack

import (
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path"
	"strings"

	log "github.com/Sirupsen/logrus"
	flag "github.com/docker/docker/pkg/mflag"
	"github.com/docker/docker/utils"
	"github.com/docker/machine/drivers"
	"github.com/docker/machine/ssh"
	"github.com/docker/machine/state"
)

type Driver struct {
	AuthUrl        string
	Username       string
	Password       string
	TenantName     string
	TenantId       string
	Region         string
	EndpointType   string
	MachineName    string
	MachineId      string
	FlavorId       string
	ImageId        string
	KeyPairName    string
	NetworkId      string
	SecurityGroups []string
	FloatingIpPool string
	SSHUser        string
	SSHPort        int
	storePath      string
	client         *Client
}

type CreateFlags struct {
	AuthUrl        *string
	Username       *string
	Password       *string
	TenantName     *string
	TenantId       *string
	Region         *string
	EndpointType   *string
	FlavorId       *string
	ImageId        *string
	NetworkId      *string
	SecurityGroups *string
	FloatingIpPool *string
	SSHUser        *string
	SSHPort        *int
}

func init() {
	drivers.Register("openstack", &drivers.RegisteredDriver{
		New:                 NewDriver,
		RegisterCreateFlags: RegisterCreateFlags,
	})
}

func RegisterCreateFlags(cmd *flag.FlagSet) interface{} {
	createFlags := new(CreateFlags)
	createFlags.AuthUrl = cmd.String(
		[]string{"-openstack-auth-url"},
		os.Getenv("OS_AUTH_URL"),
		"OpenStack authentication URL",
	)
	createFlags.Username = cmd.String(
		[]string{"-openstack-username"},
		os.Getenv("OS_USERNAME"),
		"OpenStack username",
	)
	createFlags.Password = cmd.String(
		[]string{"-openstack-password"},
		os.Getenv("OS_PASSWORD"),
		"OpenStack password",
	)
	createFlags.TenantName = cmd.String(
		[]string{"-openstack-tenant-name"},
		os.Getenv("OS_TENANT_NAME"),
		"OpenStack tenant name",
	)
	createFlags.TenantId = cmd.String(
		[]string{"-openstack-tenant-id"},
		os.Getenv("OS_TENANT_ID"),
		"OpenStack tenant id",
	)
	createFlags.Region = cmd.String(
		[]string{"-openstack-region"},
		os.Getenv("OS_REGION_NAME"),
		"OpenStack region name",
	)
	createFlags.EndpointType = cmd.String(
		[]string{"-openstack-endpoint-type"},
		os.Getenv("OS_ENDPOINT_TYPE"),
		"OpenStack endpoint type (adminURL, internalURL or the default publicURL)",
	)
	createFlags.FlavorId = cmd.String(
		[]string{"-openstack-flavor-id"},
		"",
		"OpenStack flavor id to use for the instance",
	)
	createFlags.ImageId = cmd.String(
		[]string{"-openstack-image-id"},
		"",
		"OpenStack image id to use for the instance",
	)
	createFlags.NetworkId = cmd.String(
		[]string{"-openstack-net-id"},
		"",
		"OpenStack network id the machine will be connected on",
	)
	createFlags.SecurityGroups = cmd.String(
		[]string{"-openstack-sec-groups"},
		"",
		"OpenStack comma separated security groups for the machine",
	)
	createFlags.FloatingIpPool = cmd.String(
		[]string{"-openstack-floatingip-pool"},
		"",
		"OpenStack floating IP pool to get an IP from to assign to the instance",
	)
	createFlags.SSHUser = cmd.String(
		[]string{"-openstack-ssh-user"},
		"root",
		"OpenStack SSH user. Set to root by default",
	)
	createFlags.SSHPort = cmd.Int(
		[]string{"-openstack-ssh-port"},
		22,
		"OpenStack SSH port. Set to 22 by default",
	)
	return createFlags
}

func NewDriver(storePath string) (drivers.Driver, error) {
	log.WithFields(log.Fields{
		"storePath": storePath,
	}).Info("Instanciate OpenStack driver...")
	return &Driver{
		storePath: storePath,
		client:    &Client{},
	}, nil
}

func (d *Driver) DriverName() string {
	return "openstack"
}

func (d *Driver) SetConfigFromFlags(flagsInterface interface{}) error {
	flags := flagsInterface.(*CreateFlags)
	d.AuthUrl = *flags.AuthUrl
	d.Username = *flags.Username
	d.Password = *flags.Password
	d.TenantName = *flags.TenantName
	d.TenantId = *flags.TenantId
	d.Region = *flags.Region
	d.EndpointType = *flags.EndpointType
	d.FlavorId = *flags.FlavorId
	d.ImageId = *flags.ImageId
	d.NetworkId = *flags.NetworkId
	if *flags.SecurityGroups != "" {
		d.SecurityGroups = strings.Split(*flags.SecurityGroups, ",")
	}
	d.FloatingIpPool = *flags.FloatingIpPool
	d.SSHUser = *flags.SSHUser
	d.SSHPort = *flags.SSHPort
	return d.checkConfig()
}

func (d *Driver) GetURL() (string, error) {
	ip, err := d.GetIP()
	if err != nil {
		return "", err
	}
	if ip == "" {
		return "", nil
	}
	return fmt.Sprintf("tcp://%s:2376", ip), nil
}

func (d *Driver) GetIP() (string, error) {
	addresses, err := d.client.GetInstanceIpAddresses(d)
	if err != nil {
		return "", err
	}

	floating := []string{}
	fixed := []string{}

	for _, address := range addresses {
		if address.AddressType == Floating {
			floating = append(floating, address.Address)
			continue
		}
		if address.AddressType == Fixed {
			fixed = append(fixed, address.Address)
			continue
		}
		log.Warnf("Unknown IP address type : %s", address)
	}

	if len(floating) == 1 {
		return d.foundIP(floating[0]), nil
	} else if len(floating) > 1 {
		log.Warnf("Multiple floating IP found. Take the first one of %s", floating)
		return d.foundIP(floating[0]), nil
	}

	if len(fixed) == 1 {
		return d.foundIP(fixed[0]), nil
	} else if len(fixed) > 1 {
		log.Warnf("Multiple fixed IP found. Take the first one of %s", floating)
		return d.foundIP(fixed[0]), nil
	}

	return "", fmt.Errorf("No IP found for the machine")
}

func (d *Driver) GetState() (state.State, error) {

	log.WithField("MachineId", d.MachineId).Info("Get status for OpenStack instance...")

	s, err := d.client.GetInstanceState(d)
	if err != nil {
		return state.None, err
	}

	log.WithFields(log.Fields{
		"MachineId": d.MachineId,
		"State":     s,
	}).Info("State for OpenStack instance")

	switch s {
	case "ACTIVE":
		return state.Running, nil
	case "PAUSED":
		return state.Paused, nil
	case "SUSPENDED":
		return state.Saved, nil
	case "SHUTOFF":
		return state.Stopped, nil
	case "BUILDING":
		return state.Starting, nil
	case "ERROR":
		return state.Error, nil
	}
	return state.None, nil
}

func (d *Driver) Create() error {

	d.setMachineNameIfNotSet()
	d.KeyPairName = d.MachineName

	if err := d.createSSHKey(); err != nil {
		return err
	}
	if err := d.createMachine(); err != nil {
		return err
	}
	if err := d.waitForInstanceToStart(); err != nil {
		return err
	}
	if err := d.installDocker(); err != nil {
		return err
	}
	return nil
}

func (d *Driver) Start() error {
	log.WithField("MachineId", d.MachineId).Info("Starting OpenStack instance...")
	if err := d.client.StartInstance(d); err != nil {
		return err
	}
	return d.waitForInstanceToStart()
}

func (d *Driver) Stop() error {

	log.WithField("MachineId", d.MachineId).Info("Stopping OpenStack instance...")
	if err := d.client.StopInstance(d); err != nil {
		return err
	}

	log.WithField("MachineId", d.MachineId).Info("Waiting for the OpenStack instance to stop...")
	if err := d.client.WaitForInstanceStatus(d, "SHUTOFF", 200); err != nil {
		return err
	}
	return nil
}

func (d *Driver) Remove() error {
	log.WithField("MachineId", d.MachineId).Info("Deleting OpenStack instance...")
	if err := d.client.DeleteInstance(d); err != nil {
		return err
	}
	log.WithField("Name", d.KeyPairName).Info("Deleting Key Pair...")
	if err := d.client.DeleteKeyPair(d, d.KeyPairName); err != nil {
		return err
	}
	return nil
}

func (d *Driver) Restart() error {
	log.WithField("MachineId", d.MachineId).Info("Restarting OpenStack instance...")
	if err := d.client.RestartInstance(d); err != nil {
		return err
	}
	return d.waitForInstanceToStart()
}

func (d *Driver) Kill() error {
	return d.Stop()
}

func (d *Driver) Upgrade() error {
	return fmt.Errorf("Upgrate is currently not available for the OpenStack driver")
}

func (d *Driver) GetSSHCommand(args ...string) (*exec.Cmd, error) {
	ip, err := d.GetIP()
	if err != nil {
		return nil, err
	}
	return ssh.GetSSHCommand(ip, d.SSHPort, d.SSHUser, d.sshKeyPath(), args...), nil
}

const (
	errorMandatoryEnvOrOption    string = "%s must be specified either using the environment variable %s or the CLI option %s"
	errorMandatoryOption         string = "%s must be specified using the CLI option %s"
	errorMandatoryTenantNameOrId string = "Tenant id or name must be provided either using one of the environment variables OS_TENANT_ID and OS_TENANT_NAME or one of the CLI options --openstack-tenant-id and --openstack-tenant-name"
	errorWrongEndpointType       string = "Endpoint type must be 'publicURL', 'adminURL' or 'internalURL'"
)

func (d *Driver) checkConfig() error {
	if d.AuthUrl == "" {
		return fmt.Errorf(errorMandatoryEnvOrOption, "Autentication URL", "OS_AUTH_URL", "--openstack-auth-url")
	}
	if d.Username == "" {
		return fmt.Errorf(errorMandatoryEnvOrOption, "Username", "OS_USERNAME", "--openstack-username")
	}
	if d.Password == "" {
		return fmt.Errorf(errorMandatoryEnvOrOption, "Password", "OS_PASSWORD", "--openstack-password")
	}
	if d.TenantName == "" && d.TenantId == "" {
		return fmt.Errorf(errorMandatoryTenantNameOrId)
{'fraction_non_alphanumeric': 0.13990625681273164, 'fraction_numerical': 0.0020710704163941574, 'mean_word_length': 3.8122213480199316, 'pattern_counts': {'":': 18, '<': 4, '<?xml version=': 0, '>': 8, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1439722', 'n_tokens_mistral': 6633, 'n_tokens_neox': 6336, 'n_words': 1579}