from kamaki.clients.cyclades import CycladesClient
from kamaki.cli.argument import (
FlagArgument, ValueArgument, KeyValueArgument, RepeatableArgument,
- ProgressBarArgument, DateArgument, IntArgument)
+ ProgressBarArgument, DateArgument, IntArgument, StatusArgument)
from kamaki.cli.commands import _command_init, errors, addLogSettings
from kamaki.cli.commands import (
_optional_output_cmd, _optional_json, _name_filter, _id_filter)
' [mode=]MODE: permission in octal (e.g., 0777)',
'e.g., -p /tmp/my.file,owner=root,mode=0777']
+server_states = ('BUILD', 'ACTIVE', 'STOPPED', 'REBOOT')
+
class _service_wait(object):
"""Detailed information on a Virtual Machine"""
arguments = dict(
- addr=FlagArgument(
+ nics=FlagArgument(
'Show only the network interfaces of this virtual server',
'--nics'),
- vnc=FlagArgument(
- 'Show VNC connection information (valid for a short period)',
- '--vnc-credentials'),
- stats=FlagArgument('Get URLs for server statistics', '--stats')
+ network_id=ValueArgument(
+ 'Show the connection details to that network', '--network-id'),
+ stats=FlagArgument('Get URLs for server statistics', '--stats'),
+ diagnostics=FlagArgument('Diagnostic information', '--diagnostics')
)
@errors.generic.all
@errors.cyclades.connection
@errors.cyclades.server_id
def _run(self, server_id):
- vm = self.client.get_server_details(server_id)
- if self['addr']:
- self._print(vm.get('attachments', []))
- elif self['vnc']:
- self.error(
- '(!) For security reasons, the following credentials are '
- 'invalidated\nafter a short time period, depending on the '
- 'server settings\n')
+ if self['nics']:
+ self._print(
+ self.client.get_server_nics(server_id), self.print_dict)
+ elif self['network_id']:
self._print(
- self.client.get_server_console(server_id), self.print_dict)
+ self.client.get_server_network_nics(
+ server_id, self['network_id']), self.print_dict)
elif self['stats']:
self._print(
self.client.get_server_stats(server_id), self.print_dict)
else:
+ vm = self.client.get_server_details(server_id)
uuids = self._uuids2usernames([vm['user_id'], vm['tenant_id']])
vm['user_id'] += ' (%s)' % uuids[vm['user_id']]
vm['tenant_id'] += ' (%s)' % uuids[vm['tenant_id']]
def main(self, server_id):
super(self.__class__, self)._run()
- choose_one = ('addr', 'vnc', 'stats')
+ choose_one = ('nics', 'stats', 'diagnostics')
count = len([a for a in choose_one if self[a]])
if count > 1:
- raise CLIInvalidArgument('Invalid argument compination', details=[
+ raise CLIInvalidArgument('Invalid argument combination', details=[
'Arguments %s cannot be used simultaneously' % ', '.join(
[self.arguments[a].lvalue for a in choose_one])])
self._run(server_id=server_id)
'%s' % ve])
-class NetworkIpArgument(RepeatableArgument):
+class NetworkArgument(RepeatableArgument):
+ """[id=]NETWORK_ID[,[ip=]IP]"""
@property
def value(self):
- return getattr(self, '_value', [])
+ return getattr(self, '_value', self.default)
@value.setter
def value(self, new_value):
- for v in (new_value or []):
- net_and_ip = v.split(',')
- if len(net_and_ip) < 2:
+ for v in new_value or []:
+ part1, sep, part2 = v.partition(',')
+ netid, ip = '', ''
+ if part1.startswith('id='):
+ netid = part1[len('id='):]
+ elif part1.startswith('ip='):
+ ip = part1[len('ip='):]
+ else:
+ netid = part1
+ if part2:
+ if (part2.startswith('id=') and netid) or (
+ part2.startswith('ip=') and ip):
+ raise CLIInvalidArgument(
+ 'Invalid network argument %s' % v, details=[
+ 'Valid format: [id=]NETWORK_ID[,[ip=]IP]'])
+ if part2.startswith('id='):
+ netid = part2[len('id='):]
+ elif part2.startswith('ip='):
+ ip = part2[len('ip='):]
+ elif netid:
+ ip = part2
+ else:
+ netid = part2
+ if not netid:
raise CLIInvalidArgument(
- 'Value "%s" is missing parts' % v,
- details=['Correct format: %s NETWORK_ID,IP' % (
- self.parsed_name[0])])
- self._value = getattr(self, '_value', list())
- self._value.append(
- dict(uuid=net_and_ip[0], fixed_ip=net_and_ip[1]))
+ 'Invalid network argument %s' % v, details=[
+ 'Valid format: [id=]NETWORK_ID[,[ip=]IP]'])
+ self._value = getattr(self, '_value', [])
+ self._value.append(dict(uuid=netid))
+ if ip:
+ self._value[-1]['fixed_ip'] = ip
@command(server_cmds)
arguments = dict(
server_name=ValueArgument('The name of the new server', '--name'),
- flavor_id=IntArgument('The ID of the hardware flavor', '--flavor-id'),
- image_id=ValueArgument('The ID of the hardware image', '--image-id'),
+ flavor_id=IntArgument('The ID of the flavor', '--flavor-id'),
+ image_id=ValueArgument('The ID of the image', '--image-id'),
personality=PersonalityArgument(
(80 * ' ').join(howto_personality), ('-p', '--personality')),
wait=FlagArgument('Wait server to build', ('-w', '--wait')),
'--cluster-size'),
max_threads=IntArgument(
'Max threads in cluster mode (default 1)', '--threads'),
- network_id=RepeatableArgument(
- 'Connect server to network (can be repeated)', '--network'),
- network_id_and_ip=NetworkIpArgument(
- 'Connect server to network w. floating ip ( NETWORK_ID,IP )'
- '(can be repeated)',
- '--network-with-ip'),
- automatic_ip=FlagArgument(
- 'Automatically assign an IP to the server', '--automatic-ip')
+ network_configuration=NetworkArgument(
+ 'Connect server to network: [id=]NETWORK_ID[,[ip=]IP] . '
+ 'Use only NETWORK_ID for private networks. . '
+ 'Use NETWORK_ID,[ip=]IP for networks with IP. . '
+ 'Can be repeated, mutually exclussive with --no-network',
+ '--network'),
+ no_network=FlagArgument(
+ 'Do not create any network NICs on the server. . '
+ 'Mutually exclusive to --network . '
+ 'If neither --network or --no-network are used, the default '
+ 'network policy is applied. These policies are set on the cloud, '
+ 'so kamaki is oblivious to them',
+ '--no-network')
)
required = ('server_name', 'flavor_id', 'image_id')
@errors.cyclades.cluster_size
def _create_cluster(self, prefix, flavor_id, image_id, size):
- if self['automatic_ip']:
- networks = []
- else:
- networks = [dict(uuid=netid) for netid in (
- self['network_id'] or [])] + (self['network_id_and_ip'] or [])
- networks = networks or None
+ networks = self['network_configuration'] or (
+ [] if self['no_network'] else None)
servers = [dict(
name='%s%s' % (prefix, i if size > 1 else ''),
flavor_id=flavor_id,
def main(self):
super(self.__class__, self)._run()
- if self['automatic_ip'] and (
- self['network_id'] or self['network_id_and_ip']):
- raise CLIInvalidArgument('Invalid argument combination', details=[
- 'Argument %s should not be combined with other' % (
- self.arguments['automatic_ip'].lvalue),
- 'network-related arguments i.e., %s or %s' % (
- self.arguments['network_id'].lvalue,
- self.arguments['network_id_and_ip'].lvalue)])
+ if self['no_network'] and self['network_configuration']:
+ raise CLIInvalidArgument(
+ 'Invalid argument compination', importance=2, details=[
+ 'Arguments %s and %s are mutually exclusive' % (
+ self.arguments['no_network'].lvalue,
+ self.arguments['network_configuration'].lvalue)])
self._run(
name=self['server_name'],
flavor_id=self['flavor_id'],
@command(server_cmds)
-class server_addr(_init_cyclades):
+class server_nics(_init_cyclades):
"""DEPRECATED, use: [kamaki] server info SERVER_ID --nics"""
def main(self, *args):
@command(server_cmds)
class server_console(_init_cyclades, _optional_json):
- """DEPRECATED, use: [kamaki] server info SERVER_ID --vnc-credentials"""
+ """Create a VMC console and show connection information"""
- def main(self, *args):
- raiseCLIError('DEPRECATED since v0.12', importance=3, details=[
- 'Replaced by',
- ' [kamaki] server info <SERVER_ID> --vnc-credentials'])
+ @errors.generic.all
+ @errors.cyclades.connection
+ @errors.cyclades.server_id
+ def _run(self, server_id):
+ self.error('The following credentials will be invalidated shortly')
+ self._print(
+ self.client.get_server_console(server_id), self.print_dict)
+
+ def main(self, server_id):
+ super(self.__class__, self)._run()
+ self._run(server_id=server_id)
@command(server_cmds)
def main(self, *args):
raiseCLIError('DEPRECATED since v0.12', importance=3, details=[
'Replaced by',
- ' [kamaki] server info <SERVER_ID> --stats'])
+ ' [kamaki] server modify <SERVER_ID> --name=NEW_NAME'])
@command(server_cmds)
@command(server_cmds)
class server_wait(_init_cyclades, _server_wait):
- """Wait for server to finish [BUILD, STOPPED, REBOOT, ACTIVE]"""
+ """Wait for server to change its status (default: BUILD)"""
arguments = dict(
timeout=IntArgument(
- 'Wait limit in seconds (default: 60)', '--timeout', default=60)
+ 'Wait limit in seconds (default: 60)', '--timeout', default=60),
+ server_status=StatusArgument(
+ 'Status to wait for (%s, default: %s)' % (
+ ', '.join(server_states), server_states[0]),
+ '--status',
+ valid_states=server_states)
)
@errors.generic.all
'status is already %s' % (
server_id, current_status, r['status']))
- def main(self, server_id, current_status='BUILD'):
+ def main(self, server_id):
super(self.__class__, self)._run()
- self._run(server_id=server_id, current_status=current_status)
+ self._run(
+ server_id=server_id, current_status=self['server_status'] or '')
@command(flavor_cmds)