Add --openstack-key-pair and --openstack-private-file-name so to

import and reuse existing nova keypairs.

Signed-off-by: Fabrizio Soppelsa <fsoppelsa@mirantis.com>
This commit is contained in:
Fabrizio Soppelsa 2015-12-15 18:22:38 -08:00
parent 2f482ac085
commit 7400e6f965
3 changed files with 70 additions and 4 deletions

View File

@ -28,10 +28,12 @@ Options:
- `--openstack-floatingip-pool`: The IP pool that will be used to get a public IP can assign it to the machine. If there is an
IP address already allocated but not assigned to any machine, this IP will be chosen and assigned to the machine. If
there is no IP address already allocated a new IP will be allocated and assigned to the machine.
- `--openstack-keypair-name`: Specify the existing Nova keypair to use.
- `--openstack-insecure`: Explicitly allow openstack driver to perform "insecure" SSL (https) requests. The server's certificate will not be verified against any certificate authorities. This option should be used with caution.
- `--openstack-ip-version`: If the instance has both IPv4 and IPv6 address, you can select IP version. If not provided `4` will be used.
- `--openstack-net-name` or `--openstack-net-id`: Identify the private network the machine will be connected on. If your OpenStack project project contains only one private network it will be use automatically.
- `--openstack-password`: User password. It can be omitted if the standard environment variable `OS_PASSWORD` is set.
- `--openstack-private-key-file`: Used with `--openstack-keypair-name`, associates the private key to the keypair.
- `--openstack-region`: The region to work on. Can be omitted if there is only one region on the OpenStack.
- `--openstack-sec-groups`: If security groups are available on your OpenStack you can specify a comma separated list
to use for the machine (e.g. `secgrp001,secgrp002`).
@ -57,9 +59,11 @@ Environment variables and default values:
| `--openstack-image-name` | `OS_IMAGE_NAME` | - |
| `--openstack-insecure` | `OS_INSECURE` | `false` |
| `--openstack-ip-version` | `OS_IP_VERSION` | `4` |
| `--openstack-keypair-name` | `OS_KEYPAIR_NAME` | - |
| `--openstack-net-id` | `OS_NETWORK_ID` | - |
| `--openstack-net-name` | `OS_NETWORK_NAME` | - |
| `--openstack-password` | `OS_PASSWORD` | - |
| `--openstack-private-key-file` | `OS_PRIVATE_KEY_FILE` | - |
| `--openstack-region` | `OS_REGION_NAME` | - |
| `--openstack-sec-groups` | `OS_SECURITY_GROUPS` | - |
| `--openstack-ssh-port` | `OS_SSH_PORT` | `22` |

View File

@ -38,6 +38,7 @@ type Client interface {
DeleteInstance(d *Driver) error
WaitForInstanceStatus(d *Driver, status string) error
GetInstanceIPAddresses(d *Driver) ([]IPAddress, error)
GetPublicKey(keyPairName string) ([]byte, error)
CreateKeyPair(d *Driver, name string, publicKey string) error
DeleteKeyPair(d *Driver, name string) error
GetNetworkID(d *Driver) (string, error)
@ -300,6 +301,14 @@ func (c *GenericClient) GetTenantID(d *Driver) (string, error) {
return tenantId, err
}
func (c *GenericClient) GetPublicKey(keyPairName string) ([]byte, error) {
kp, err := keypairs.Get(c.Compute, keyPairName).Extract()
if err != nil {
return nil, err
}
return []byte(kp.PublicKey), nil
}
func (c *GenericClient) CreateKeyPair(d *Driver, name string, publicKey string) error {
opts := keypairs.CreateOpts{
Name: name,

View File

@ -37,6 +37,7 @@ type Driver struct {
KeyPairName string
NetworkName string
NetworkId string
PrivateKeyFile string
SecurityGroups []string
FloatingIpPool string
ComputeNetwork bool
@ -142,12 +143,24 @@ func (d *Driver) GetCreateFlags() []mcnflag.Flag {
Usage: "OpenStack image name to use for the instance",
Value: "",
},
mcnflag.StringFlag{
EnvVar: "OS_KEYPAIR_NAME",
Name: "openstack-keypair-name",
Usage: "OpenStack keypair to use to SSH to the instance",
Value: "",
},
mcnflag.StringFlag{
EnvVar: "OS_NETWORK_ID",
Name: "openstack-net-id",
Usage: "OpenStack network id the machine will be connected on",
Value: "",
},
mcnflag.StringFlag{
EnvVar: "OS_PRIVATE_KEY_FILE",
Name: "openstack-private-key-file",
Usage: "Private keyfile to use for SSH (absolute path)",
Value: "",
},
mcnflag.StringFlag{
EnvVar: "OS_NETWORK_NAME",
Name: "openstack-net-name",
@ -255,6 +268,8 @@ func (d *Driver) SetConfigFromFlags(flags drivers.DriverOptions) error {
d.ComputeNetwork = flags.Bool("openstack-nova-network")
d.SSHUser = flags.String("openstack-ssh-user")
d.SSHPort = flags.Int("openstack-ssh-port")
d.KeyPairName = flags.String("openstack-keypair-name")
d.PrivateKeyFile = flags.String("openstack-private-key-file")
d.SwarmMaster = flags.Bool("swarm-master")
d.SwarmHost = flags.String("swarm-host")
d.SwarmDiscovery = flags.String("swarm-discovery")
@ -339,13 +354,18 @@ func (d *Driver) GetState() (state.State, error) {
}
func (d *Driver) Create() error {
d.KeyPairName = fmt.Sprintf("%s-%s", d.MachineName, mcnutils.GenerateRandomID())
if err := d.resolveIds(); err != nil {
return err
}
if err := d.createSSHKey(); err != nil {
return err
if d.KeyPairName != "" {
if err := d.loadSSHKey(); err != nil {
return err
}
} else {
d.KeyPairName = fmt.Sprintf("%s-%s", d.MachineName, mcnutils.GenerateRandomID())
if err := d.createSSHKey(); err != nil {
return err
}
}
if err := d.createMachine(); err != nil {
return err
@ -397,6 +417,7 @@ func (d *Driver) Remove() error {
return err
}
log.Debug("deleting key pair...", map[string]string{"Name": d.KeyPairName})
// TODO (fsoppelsa) maybe we want to check this, in case of shared keypairs, before removal
if err := d.client.DeleteKeyPair(d, d.KeyPairName); err != nil {
return err
}
@ -422,6 +443,7 @@ 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"
errorBothOptions string = "Both %s and %s must be specified"
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'"
errorUnknownFlavorName string = "Unable to find flavor named %s"
@ -464,6 +486,9 @@ func (d *Driver) checkConfig() error {
if d.EndpointType != "" && (d.EndpointType != "publicURL" && d.EndpointType != "adminURL" && d.EndpointType != "internalURL") {
return fmt.Errorf(errorWrongEndpointType)
}
if (d.KeyPairName != "" && d.PrivateKeyFile == "") || (d.KeyPairName == "" && d.PrivateKeyFile != "") {
return fmt.Errorf(errorBothOptions, "KeyPairName", "PrivateKeyFile")
}
return nil
}
@ -607,6 +632,30 @@ func (d *Driver) initNetwork() error {
return nil
}
func (d *Driver) loadSSHKey() error {
log.Debug("Loading Key Pair", d.KeyPairName)
if err := d.initCompute(); err != nil {
return err
}
log.Debug("Loading Private Key from", d.PrivateKeyFile)
privateKey, err := ioutil.ReadFile(d.PrivateKeyFile)
if err != nil {
return err
}
publicKey, err := d.client.GetPublicKey(d.KeyPairName)
if err != nil {
return err
}
if err := ioutil.WriteFile(d.privateSSHKeyPath(), privateKey, 0600); err != nil {
return err
}
if err := ioutil.WriteFile(d.publicSSHKeyPath(), publicKey, 0600); err != nil {
return err
}
return nil
}
func (d *Driver) createSSHKey() error {
sanitizeKeyPairName(&d.KeyPairName)
log.Debug("Creating Key Pair...", map[string]string{"Name": d.KeyPairName})
@ -715,6 +764,10 @@ func (d *Driver) lookForIPAddress() error {
return nil
}
func (d *Driver) privateSSHKeyPath() string {
return d.GetSSHKeyPath()
}
func (d *Driver) publicSSHKeyPath() string {
return d.GetSSHKeyPath() + ".pub"
}