Allow selecting the network for doing the ssh with

Previously, nova ssh was searching for network types: public and
private, which seems to be incorrect (fixed and floating seems to be
correct), causing that this command has probably never worked.

This commit fixes the above and adds an option for selecting the network
to use, which is helpful when there are more networks for the VM.

Change-Id: I01ea6cee725c0feaacab60975c3792b0ac1305e9
Closes-Bug: #1227694
Closes-Bug: #1343991
This commit is contained in:
Jaroslav Henner 2014-07-07 11:45:40 +02:00
parent 50b1068ba6
commit ff4af92b6d
3 changed files with 150 additions and 51 deletions

View File

@ -2070,10 +2070,15 @@ class ShellTest(utils.TestCase):
def test_ssh(self, mock_system, mock_find_server):
class FakeResources(object):
addresses = {
"private": [{'version': 4, 'addr': "1.1.1.1"},
{'version': 6, 'addr': "2607:f0d0:1002::4"}],
"public": [{'version': 4, 'addr': "2.2.2.2"},
{'version': 6, 'addr': "7612:a1b2:2004::6"}]
"skynet": [
{'version': 4, 'addr': "1.1.1.1",
"OS-EXT-IPS:type": 'fixed'},
{'version': 4, 'addr': "2.2.2.2",
"OS-EXT-IPS:type": 'floating'},
{'version': 6, 'addr': "2607:f0d0:1002::4",
"OS-EXT-IPS:type": 'fixed'},
{'version': 6, 'addr': "7612:a1b2:2004::6"}
]
}
mock_find_server.return_value = FakeResources()
@ -2108,6 +2113,33 @@ class ShellTest(utils.TestCase):
mock_system.assert_called_with("ssh -6 -p22 "
"root@2607:f0d0:1002::4 -1")
@mock.patch('novaclient.v1_1.shell._find_server')
@mock.patch('os.system')
def test_ssh_multinet(self, mock_system, mock_find_server):
class FakeResources(object):
addresses = {
"skynet": [
{'version': 4, 'addr': "1.1.1.1",
"OS-EXT-IPS:type": 'fixed'},
{'version': 4, 'addr': "2.2.2.2"},
{'version': 6, 'addr': "2607:f0d0:1002::4",
"OS-EXT-IPS:type": 'fixed'}
],
"other": [
{'version': 4, 'addr': "2.3.4.5"},
{'version': 6, 'addr': "7612:a1b2:2004::6"}
]
}
mock_find_server.return_value = FakeResources()
self.run_command("ssh --network other server")
mock_system.assert_called_with("ssh -4 -p22 root@2.3.4.5 ")
self.run_command("ssh --ipv6 --network other server")
mock_system.assert_called_with("ssh -6 -p22 root@7612:a1b2:2004::6 ")
self.assertRaises(exceptions.ResourceNotFound,
self.run_command,
"ssh --ipv6 --network nonexistent server")
def test_keypair_add(self):
self.run_command('keypair-add test')
self.assert_called('POST', '/os-keypairs',

View File

@ -23,6 +23,7 @@ import copy
import datetime
import getpass
import locale
import logging
import os
import sys
import time
@ -40,6 +41,9 @@ from novaclient.v1_1 import quotas
from novaclient.v1_1 import servers
logger = logging.getLogger(__name__)
CLIENT_BDM2_KEYS = {
'id': 'uuid',
'source': 'source_type',
@ -3207,9 +3211,16 @@ def do_credentials(cs, _args):
dest='private',
action='store_true',
default=False,
help=_('Optional flag to indicate whether to only use private address '
'attached to an instance. (Default=False). If no public address is '
'found try private address'))
help=argparse.SUPPRESS)
@utils.arg('--address-type',
dest='address_type',
action='store',
type=str,
default='floating',
help=_('Optional flag to indicate which IP type to use. Possible values '
'includes fixed and floating (the Default).'))
@utils.arg('--network', metavar='<network>',
help=_('Network to use for the ssh.'), default=None)
@utils.arg('--ipv6',
dest='ipv6',
action='store_true',
@ -3234,35 +3245,57 @@ def do_ssh(cs, args):
args.server = server
addresses = _find_server(cs, args.server).addresses
address_type = "private" if args.private else "public"
address_type = "fixed" if args.private else args.address_type
version = 6 if args.ipv6 else 4
pretty_version = 'IPv%d' % version
if (address_type == "public" and address_type not in addresses and
"private" in addresses):
address_type = "private"
# Select the network to use.
if args.network:
network_addresses = addresses.get(args.network)
if not network_addresses:
msg = _("Server '%(server)s' is not attached to network "
"'%(network)s'")
raise exceptions.ResourceNotFound(
msg % {'server': args.server, 'network': args.network})
else:
if len(addresses) > 1:
msg = _("Server '%(server)s' is attached to more than one network."
" Please pick the network to use.")
raise exceptions.CommandError(msg % {'server': args.server})
elif not addresses:
msg = _("Server '%(server)s' is not attached to any network.")
raise exceptions.CommandError(msg % {'server': args.server})
else:
network_addresses = list(six.itervalues(addresses))[0]
if address_type not in addresses:
print(_("ERROR: No %(addr_type)s addresses found for '%(server)s'.") %
{'addr_type': address_type, 'server': args.server})
return
ip_address = None
for address in addresses[address_type]:
if address['version'] == version:
ip_address = address['addr']
break
# Select the address in the selected network.
# If the extension is not present, we assume the address to be floating.
match = lambda addr: all((
addr.get('version') == version,
addr.get('OS-EXT-IPS:type', 'floating') == address_type))
matching_addresses = [address.get('addr') for address in network_addresses
if match(address)]
if not any(matching_addresses):
msg = _("No address that would match network '%(network)s'"
" and type '%(address_type)s' of version %(pretty_version)s "
"has been found for server '%(server)s'.")
raise exceptions.ResourceNotFound(msg % {
'network': args.network, 'address_type': address_type,
'pretty_version': pretty_version, 'server': args.server})
elif len(matching_addresses) > 1:
msg = _("More than one %(pretty_version)s %(address_type)s address"
"found.")
raise exceptions.CommandError(msg % {'pretty_version': pretty_version,
'address_type': address_type})
else:
ip_address = matching_addresses[0]
identity = '-i %s' % args.identity if len(args.identity) else ''
if ip_address:
os.system("ssh -%d -p%d %s %s@%s %s" % (version, args.port, identity,
args.login, ip_address,
args.extra))
else:
pretty_version = "IPv%d" % version
print(_("ERROR: No %(addr_type)s %(pretty_version)s address found.") %
{'addr_type': address_type, 'pretty_version': pretty_version})
return
cmd = "ssh -%d -p%d %s %s@%s %s" % (version, args.port, identity,
args.login, ip_address, args.extra)
logger.debug("Executing cmd '%s'", cmd)
os.system(cmd)
_quota_resources = ['instances', 'cores', 'ram',

View File

@ -2664,8 +2664,16 @@ def do_extension_list(cs, _args):
dest='private',
action='store_true',
default=False,
help='Optional flag to indicate whether to use private address '
'attached to a server. (Default=False)')
help=argparse.SUPPRESS)
@utils.arg('--address-type',
dest='address_type',
action='store',
type=str,
default='floating',
help='Optional flag to indicate which IP type to use. Possible values '
'includes fixed and floating (the Default).')
@utils.arg('--network', metavar='<network>',
help='Network to use for the ssh.', default=None)
@utils.arg('--ipv6',
dest='ipv6',
action='store_true',
@ -2689,31 +2697,57 @@ def do_ssh(cs, args):
args.server = server
addresses = _find_server(cs, args.server).addresses
address_type = "private" if args.private else "public"
address_type = "fixed" if args.private else args.address_type
version = 6 if args.ipv6 else 4
pretty_version = 'IPv%d' % version
if address_type not in addresses:
print("ERROR: No %s addresses found for '%s'." % (address_type,
args.server))
return
# Select the network to use.
if args.network:
network_addresses = addresses.get(args.network)
if not network_addresses:
msg = _("Server '%(server)s' is not attached to network "
"'%(network)s'")
raise exceptions.ResourceNotFound(
msg % {'server': args.server, 'network': args.network})
else:
if len(addresses) > 1:
msg = _("Server '%(server)s' is attached to more than one network."
" Please pick the network to use.")
raise exceptions.CommandError(msg % {'server': args.server})
elif not addresses:
msg = _("Server '%(server)s' is not attached to any network.")
raise exceptions.CommandError(msg % {'server': args.server})
else:
network_addresses = list(six.itervalues(addresses))[0]
ip_address = None
for address in addresses[address_type]:
if address['version'] == version:
ip_address = address['addr']
break
# Select the address in the selected network.
# If the extension is not present, we assume the address to be floating.
match = lambda addr: all((
addr.get('version') == version,
addr.get('OS-EXT-IPS:type', 'floating') == address_type))
matching_addresses = [address.get('addr') for address in network_addresses
if match(address)]
if not any(matching_addresses):
msg = _("No address that would match network '%(network)s'"
" and type '%(address_type)s' of version %(pretty_version)s "
"has been found for server '%(server)s'.")
raise exceptions.ResourceNotFound(msg % {
'network': args.network, 'address_type': address_type,
'pretty_version': pretty_version, 'server': args.server})
elif len(matching_addresses) > 1:
msg = _("More than one %(pretty_version)s %(address_type)s address"
"found.")
raise exceptions.CommandError(msg % {'pretty_version': pretty_version,
'address_type': address_type})
else:
ip_address = matching_addresses[0]
identity = '-i %s' % args.identity if len(args.identity) else ''
if ip_address:
os.system("ssh -%d -p%d %s %s@%s %s" % (version, args.port, identity,
args.login, ip_address,
args.extra))
else:
pretty_version = "IPv%d" % version
print("ERROR: No %s %s address found." % (address_type,
pretty_version))
return
cmd = "ssh -%d -p%d %s %s@%s %s" % (version, args.port, identity,
args.login, ip_address, args.extra)
logger.debug("Executing cmd '%s'", cmd)
os.system(cmd)
_quota_resources = ['instances', 'cores', 'ram',