Skip to content

Commit c63d303

Browse files
author
Fabrizio Soppelsa
committed
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>
1 parent 14ea20f commit c63d303

File tree

3 files changed

+70
-4
lines changed

3 files changed

+70
-4
lines changed

docs/drivers/openstack.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,12 @@ Options:
2828
- `--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
2929
IP address already allocated but not assigned to any machine, this IP will be chosen and assigned to the machine. If
3030
there is no IP address already allocated a new IP will be allocated and assigned to the machine.
31+
- `--openstack-keypair-name`: Specify the existing Nova keypair to use.
3132
- `--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.
3233
- `--openstack-ip-version`: If the instance has both IPv4 and IPv6 address, you can select IP version. If not provided `4` will be used.
3334
- `--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.
3435
- `--openstack-password`: User password. It can be omitted if the standard environment variable `OS_PASSWORD` is set.
36+
- `--openstack-private-key-file`: Used with `--openstack-keypair-name`, associates the private key to the keypair.
3537
- `--openstack-region`: The region to work on. Can be omitted if there is only one region on the OpenStack.
3638
- `--openstack-sec-groups`: If security groups are available on your OpenStack you can specify a comma separated list
3739
to use for the machine (e.g. `secgrp001,secgrp002`).
@@ -57,9 +59,11 @@ Environment variables and default values:
5759
| `--openstack-image-name` | `OS_IMAGE_NAME` | - |
5860
| `--openstack-insecure` | `OS_INSECURE` | `false` |
5961
| `--openstack-ip-version` | `OS_IP_VERSION` | `4` |
62+
| `--openstack-keypair-name` | `OS_KEYPAIR_NAME` | - |
6063
| `--openstack-net-id` | `OS_NETWORK_ID` | - |
6164
| `--openstack-net-name` | `OS_NETWORK_NAME` | - |
6265
| `--openstack-password` | `OS_PASSWORD` | - |
66+
| `--openstack-private-key-file` | `OS_PRIVATE_KEY_FILE` | - |
6367
| `--openstack-region` | `OS_REGION_NAME` | - |
6468
| `--openstack-sec-groups` | `OS_SECURITY_GROUPS` | - |
6569
| `--openstack-ssh-port` | `OS_SSH_PORT` | `22` |

drivers/openstack/client.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ type Client interface {
3838
DeleteInstance(d *Driver) error
3939
WaitForInstanceStatus(d *Driver, status string) error
4040
GetInstanceIPAddresses(d *Driver) ([]IPAddress, error)
41+
GetPublicKey(keyPairName string) ([]byte, error)
4142
CreateKeyPair(d *Driver, name string, publicKey string) error
4243
DeleteKeyPair(d *Driver, name string) error
4344
GetNetworkID(d *Driver) (string, error)
@@ -300,6 +301,14 @@ func (c *GenericClient) GetTenantID(d *Driver) (string, error) {
300301
return tenantId, err
301302
}
302303

304+
func (c *GenericClient) GetPublicKey(keyPairName string) ([]byte, error) {
305+
kp, err := keypairs.Get(c.Compute, keyPairName).Extract()
306+
if err != nil {
307+
return nil, err
308+
}
309+
return []byte(kp.PublicKey), nil
310+
}
311+
303312
func (c *GenericClient) CreateKeyPair(d *Driver, name string, publicKey string) error {
304313
opts := keypairs.CreateOpts{
305314
Name: name,

drivers/openstack/openstack.go

Lines changed: 57 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ type Driver struct {
3737
KeyPairName string
3838
NetworkName string
3939
NetworkId string
40+
PrivateKeyFile string
4041
SecurityGroups []string
4142
FloatingIpPool string
4243
ComputeNetwork bool
@@ -142,12 +143,24 @@ func (d *Driver) GetCreateFlags() []mcnflag.Flag {
142143
Usage: "OpenStack image name to use for the instance",
143144
Value: "",
144145
},
146+
mcnflag.StringFlag{
147+
EnvVar: "OS_KEYPAIR_NAME",
148+
Name: "openstack-keypair-name",
149+
Usage: "OpenStack keypair to use to SSH to the instance",
150+
Value: "",
151+
},
145152
mcnflag.StringFlag{
146153
EnvVar: "OS_NETWORK_ID",
147154
Name: "openstack-net-id",
148155
Usage: "OpenStack network id the machine will be connected on",
149156
Value: "",
150157
},
158+
mcnflag.StringFlag{
159+
EnvVar: "OS_PRIVATE_KEY_FILE",
160+
Name: "openstack-private-key-file",
161+
Usage: "Private keyfile to use for SSH (absolute path)",
162+
Value: "",
163+
},
151164
mcnflag.StringFlag{
152165
EnvVar: "OS_NETWORK_NAME",
153166
Name: "openstack-net-name",
@@ -255,6 +268,8 @@ func (d *Driver) SetConfigFromFlags(flags drivers.DriverOptions) error {
255268
d.ComputeNetwork = flags.Bool("openstack-nova-network")
256269
d.SSHUser = flags.String("openstack-ssh-user")
257270
d.SSHPort = flags.Int("openstack-ssh-port")
271+
d.KeyPairName = flags.String("openstack-keypair-name")
272+
d.PrivateKeyFile = flags.String("openstack-private-key-file")
258273
d.SwarmMaster = flags.Bool("swarm-master")
259274
d.SwarmHost = flags.String("swarm-host")
260275
d.SwarmDiscovery = flags.String("swarm-discovery")
@@ -339,13 +354,18 @@ func (d *Driver) GetState() (state.State, error) {
339354
}
340355

341356
func (d *Driver) Create() error {
342-
d.KeyPairName = fmt.Sprintf("%s-%s", d.MachineName, mcnutils.GenerateRandomID())
343-
344357
if err := d.resolveIds(); err != nil {
345358
return err
346359
}
347-
if err := d.createSSHKey(); err != nil {
348-
return err
360+
if d.KeyPairName != "" {
361+
if err := d.loadSSHKey(); err != nil {
362+
return err
363+
}
364+
} else {
365+
d.KeyPairName = fmt.Sprintf("%s-%s", d.MachineName, mcnutils.GenerateRandomID())
366+
if err := d.createSSHKey(); err != nil {
367+
return err
368+
}
349369
}
350370
if err := d.createMachine(); err != nil {
351371
return err
@@ -397,6 +417,7 @@ func (d *Driver) Remove() error {
397417
return err
398418
}
399419
log.Debug("deleting key pair...", map[string]string{"Name": d.KeyPairName})
420+
// TODO (fsoppelsa) maybe we want to check this, in case of shared keypairs, before removal
400421
if err := d.client.DeleteKeyPair(d, d.KeyPairName); err != nil {
401422
return err
402423
}
@@ -422,6 +443,7 @@ const (
422443
errorMandatoryEnvOrOption string = "%s must be specified either using the environment variable %s or the CLI option %s"
423444
errorMandatoryOption string = "%s must be specified using the CLI option %s"
424445
errorExclusiveOptions string = "Either %s or %s must be specified, not both"
446+
errorBothOptions string = "Both %s and %s must be specified"
425447
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"
426448
errorWrongEndpointType string = "Endpoint type must be 'publicURL', 'adminURL' or 'internalURL'"
427449
errorUnknownFlavorName string = "Unable to find flavor named %s"
@@ -464,6 +486,9 @@ func (d *Driver) checkConfig() error {
464486
if d.EndpointType != "" && (d.EndpointType != "publicURL" && d.EndpointType != "adminURL" && d.EndpointType != "internalURL") {
465487
return fmt.Errorf(errorWrongEndpointType)
466488
}
489+
if (d.KeyPairName != "" && d.PrivateKeyFile == "") || (d.KeyPairName == "" && d.PrivateKeyFile != "") {
490+
return fmt.Errorf(errorBothOptions, "KeyPairName", "PrivateKeyFile")
491+
}
467492
return nil
468493
}
469494

@@ -607,6 +632,30 @@ func (d *Driver) initNetwork() error {
607632
return nil
608633
}
609634

635+
func (d *Driver) loadSSHKey() error {
636+
log.Debug("Loading Key Pair", d.KeyPairName)
637+
if err := d.initCompute(); err != nil {
638+
return err
639+
}
640+
log.Debug("Loading Private Key from", d.PrivateKeyFile)
641+
privateKey, err := ioutil.ReadFile(d.PrivateKeyFile)
642+
if err != nil {
643+
return err
644+
}
645+
publicKey, err := d.client.GetPublicKey(d.KeyPairName)
646+
if err != nil {
647+
return err
648+
}
649+
if err := ioutil.WriteFile(d.privateSSHKeyPath(), privateKey, 0600); err != nil {
650+
return err
651+
}
652+
if err := ioutil.WriteFile(d.publicSSHKeyPath(), publicKey, 0600); err != nil {
653+
return err
654+
}
655+
656+
return nil
657+
}
658+
610659
func (d *Driver) createSSHKey() error {
611660
sanitizeKeyPairName(&d.KeyPairName)
612661
log.Debug("Creating Key Pair...", map[string]string{"Name": d.KeyPairName})
@@ -715,6 +764,10 @@ func (d *Driver) lookForIPAddress() error {
715764
return nil
716765
}
717766

767+
func (d *Driver) privateSSHKeyPath() string {
768+
return d.GetSSHKeyPath()
769+
}
770+
718771
func (d *Driver) publicSSHKeyPath() string {
719772
return d.GetSSHKeyPath() + ".pub"
720773
}

0 commit comments

Comments
 (0)