class network_connect(_port_create): """Connect a network with a device (server or router)""" arguments = dict( name=ValueArgument('A human readable name for the port', '--name'), security_group_id=RepeatableArgument( 'Add a security group id (can be repeated)', ('-g', '--security-group')), subnet_id=ValueArgument( 'Subnet id for fixed ips (used with --ip-address)', '--subnet-id'), ip_address=ValueArgument( 'IP address for subnet id (used with --subnet-id', '--ip-address'), wait=FlagArgument('Wait network to connect', ('-w', '--wait')), device_id=RepeatableArgument( 'Connect this device to the network (can be repeated)', '--device-id')) required = ('device_id', ) @errors.Generic.all @errors.Cyclades.connection @errors.Cyclades.network_id def _run(self, network_id, server_id): self.error('Creating a port to connect network %s with device %s' % (network_id, server_id)) try: self.connect(network_id, server_id) except ClientError as ce: if ce.status in (400, 404): self._server_exists(server_id=server_id) raise def main(self, network_id): super(self.__class__, self)._run() for sid in self['device_id']: self._run(network_id=network_id, server_id=sid)
class image_modify(_ImageInit): """Add / update metadata and properties for an image Preserves values not explicitly modified """ arguments = dict( image_name=ValueArgument('Change name', '--name'), disk_format=ValueArgument('Change disk format', '--disk-format'), container_format=ValueArgument('Change container format', '--container-format'), status=ValueArgument('Change status', '--status'), publish=FlagArgument('Make the image public', '--public'), unpublish=FlagArgument('Make the image private', '--private'), property_to_set=KeyValueArgument( 'set property in key=value form (can be repeated)', ('-p', '--property-set')), property_to_del=RepeatableArgument( 'Delete property by key (can be repeated)', '--property-del'), member_ID_to_add=RepeatableArgument( 'Add member to image (can be repeated)', '--member-add'), member_ID_to_remove=RepeatableArgument( 'Remove a member (can be repeated)', '--member-del'), ) required = [ 'image_name', 'disk_format', 'container_format', 'status', 'publish', 'unpublish', 'property_to_set', 'member_ID_to_add', 'member_ID_to_remove', 'property_to_del' ] @errors.Generic.all @errors.Image.connection @errors.Image.permissions @errors.Image.id def _run(self, image_id): for mid in (self['member_ID_to_add'] or []): self.client.add_member(image_id, mid) for mid in (self['member_ID_to_remove'] or []): self.client.remove_member(image_id, mid) meta = self.client.get_meta(image_id) for k, v in self['property_to_set'].items(): meta['properties'][k.upper()] = v for k in (self['property_to_del'] or []): meta['properties'][k.upper()] = None self.client.update_image(image_id, name=self['image_name'], disk_format=self['disk_format'], container_format=self['container_format'], status=self['status'], public=self['publish'] or (False if self['unpublish'] else None), **meta['properties']) def main(self, image_id): super(self.__class__, self)._run() self._run(image_id=image_id)
class port_create(_port_create): """Create a new port (== connect server to network)""" arguments = dict( name=ValueArgument('A human readable name', '--name'), security_group_id=RepeatableArgument( 'Add a security group id (can be repeated)', ('-g', '--security-group')), subnet_id=ValueArgument( 'Subnet id for fixed ips (used with --ip-address)', '--subnet-id'), ip_address=ValueArgument('IP address for subnet id', '--ip-address'), network_id=ValueArgument('Set the network ID', '--network-id'), device_id=ValueArgument( 'The device is either a virtual server or a virtual router', '--device-id'), wait=FlagArgument('Wait port to be established', ('-w', '--wait')), ) required = ('network_id', 'device_id') @errors.Generic.all @errors.Cyclades.connection def _run(self): self.connect(self['network_id'], self['device_id']) def main(self): super(self.__class__, self)._run() self._run()
class imagecompute_modify(_CycladesInit): """Modify image properties (metadata)""" arguments = dict( property_to_add=KeyValueArgument( 'Add property in key=value format (can be repeated)', ('--property-add')), property_to_del=RepeatableArgument( 'Delete property by key (can be repeated)', ('--property-del')) ) required = ['property_to_add', 'property_to_del'] @errors.Generic.all @errors.Cyclades.connection @errors.Image.permissions @errors.Image.id def _run(self, image_id): if self['property_to_add']: self.client.update_image_metadata( image_id, **self['property_to_add']) for key in (self['property_to_del'] or []): self.client.delete_image_metadata(image_id, key) def main(self, image_id): super(self.__class__, self)._run() self._run(image_id=image_id)
class network_disconnect(_NetworkInit, _PortWait, OptionalOutput): """Disconnect a network from a device""" arguments = dict( wait=FlagArgument('Wait network to disconnect', ('-w', '--wait')), device_id=RepeatableArgument( 'Disconnect device from the network (can be repeated)', '--device-id')) required = ('device_id', ) @errors.Cyclades.server_id def _get_vm(self, server_id): return self._get_compute_client().get_server_details(server_id) @errors.Generic.all @errors.Cyclades.connection @errors.Cyclades.network_id def _run(self, network_id, server_id): vm = self._get_vm(server_id=server_id) ports = [ port for port in vm['attachments'] if (port['network_id'] in (network_id, )) ] if not ports: raiseCLIError('Device %s has no network %s attached' % (server_id, network_id), importance=2, details=[ 'To get device networking', ' kamaki server info %s --nics' % server_id ]) for port in ports: if self['wait']: port['status'] = self.client.get_port_details( port['id'])['status'] self.client.delete_port(port['id']) self.error('Deleting port %s (net-id: %s, device-id: %s):' % (port['id'], network_id, server_id)) if self['wait']: try: self.wait_while(port['id'], port['status']) except ClientError as ce: if ce.status not in (404, ): raise self.error('Port %s is deleted' % port['id']) def main(self, network_id): super(self.__class__, self)._run() for sid in self['device_id']: self._run(network_id=network_id, server_id=sid)
class ip_attach(_port_create): """Attach an IP on a virtual server""" arguments = dict(name=ValueArgument('A human readable name for the port', '--name'), security_group_id=RepeatableArgument( 'Add a security group id (can be repeated)', ('-g', '--security-group')), subnet_id=ValueArgument('Subnet id', '--subnet-id'), wait=FlagArgument('Wait IP to be attached', ('-w', '--wait')), server_id=ValueArgument('Server to attach to this IP', '--server-id')) required = ('server_id', ) @errors.Generic.all @errors.Cyclades.connection def _run(self, ip_or_ip_id): netid = None for ip in self.client.list_floatingips(): if ip_or_ip_id in (ip['floating_ip_address'], ip['id']): netid = ip['floating_network_id'] iparg = ValueArgument(parsed_name='--ip') iparg.value = ip['floating_ip_address'] self.arguments['ip_address'] = iparg break if netid: server_id = self['server_id'] self.error('Creating a port to attach IP %s to server %s' % (ip_or_ip_id, server_id)) try: self.connect(netid, server_id) except ClientError as ce: self.error('Failed to connect network %s with server %s' % (netid, server_id)) if ce.status in (400, 404): self._server_exists(server_id=server_id) self._network_exists(network_id=netid) raise else: raiseCLIError('%s does not match any reserved IPs or IP ids' % ip_or_ip_id, details=errors.Cyclades.about_ips) def main(self, ip_or_ip_id): super(self.__class__, self)._run() self._run(ip_or_ip_id=ip_or_ip_id)
class service_username2uuid(_AstakosInit, OptionalOutput): """Get service uuid(s) from username(s)""" arguments = dict(service_token=ValueArgument('Authenticate service', '--service-token'), username=RepeatableArgument('Username (can be repeated)', '--username')) required = ('service_token', 'username') @errors.Generic.all @errors.Astakos.astakosclient @with_temp_token def _run(self): if 1 == len(self['username']): self.print_(self.client.service_get_uuid(self['username'][0])) else: self.print_(self.client.service_get_uuids(self['username']), self.print_dict) def main(self): super(self.__class__, self)._run() self._run(token=self['service_token'])
class server_modify(_CycladesInit): """Modify attributes of a virtual server""" arguments = dict( server_name=ValueArgument('The new name', '--name'), flavor_id=IntArgument('Resize (set another flavor)', '--flavor-id'), firewall_profile=FirewallProfileArgument( 'Valid values: %s' % (', '.join(FirewallProfileArgument.profiles)), '--firewall'), metadata_to_set=KeyValueArgument( 'Set metadata in key=value form (can be repeated)', '--metadata-set'), metadata_to_delete=RepeatableArgument( 'Delete metadata by key (can be repeated)', '--metadata-del'), public_network_port_id=ValueArgument( 'Connection to set new firewall (* for all)', '--port-id'), ) required = [ 'server_name', 'flavor_id', 'firewall_profile', 'metadata_to_set', 'metadata_to_delete'] def _set_firewall_profile(self, server_id): vm = self._restruct_server_info( self.client.get_server_details(server_id)) ports = [p for p in vm['ports'] if 'firewallProfile' in p] pick_port = self.arguments['public_network_port_id'] if pick_port.value: ports = [p for p in ports if pick_port.value in ( '*', '%s' % p['id'])] elif len(ports) > 1: port_strings = ['Server %s ports to public networks:' % server_id] for p in ports: port_strings.append(' %s' % p['id']) for k in ('network_id', 'ipv4', 'ipv6', 'firewallProfile'): v = p.get(k) if v: port_strings.append('\t%s: %s' % (k, v)) raise CLIError( 'Multiple public connections on server %s' % ( server_id), details=port_strings + [ 'To select one:', ' %s PORT_ID' % pick_port.lvalue, 'To set all:', ' %s *' % pick_port.lvalue, ]) if not ports: pp = pick_port.value raise CLIError( 'No public networks attached on server %s%s' % ( server_id, ' through port %s' % pp if pp else ''), details=[ 'To see all networks:', ' kamaki network list', 'To see all connections:', ' kamaki server info %s --nics' % server_id, 'To connect to a network:', ' kamaki network connect NETWORK_ID --device-id %s' % ( server_id)]) for port in ports: self.error('Set port %s firewall to %s' % ( port['id'], self['firewall_profile'])) self.client.set_firewall_profile( server_id=server_id, profile=self['firewall_profile'], port_id=port['id']) def _server_is_stopped(self, server_id, cerror): vm = self.client.get_server_details(server_id) if vm['status'].lower() not in ('stopped'): raise CLIError(cerror, details=[ 'To resize a virtual server, it must be STOPPED', 'Server %s status is %s' % (server_id, vm['status']), 'To stop the server', ' kamaki server shutdown %s -w' % server_id]) @errors.Generic.all @errors.Cyclades.connection @errors.Cyclades.server_id def _run(self, server_id): if self['server_name'] is not None: self.client.update_server_name((server_id), self['server_name']) if self['flavor_id']: try: self.client.resize_server(server_id, self['flavor_id']) except ClientError as ce: if ce.status in (404, ): self._flavor_exists(flavor_id=self['flavor_id']) if ce.status in (400, ): self._server_is_stopped(server_id, ce) raise if self['firewall_profile']: self._set_firewall_profile(server_id) if self['metadata_to_set']: self.client.update_server_metadata( server_id, **self['metadata_to_set']) for key in (self['metadata_to_delete'] or []): errors.Cyclades.metadata( self.client.delete_server_metadata)(server_id, key=key) def main(self, server_id): super(self.__class__, self)._run() pnpid = self.arguments['public_network_port_id'] fp = self.arguments['firewall_profile'] if pnpid.value and not fp.value: raise CLIInvalidArgument('Invalid argument compination', details=[ 'Argument %s should always be combined with %s' % ( pnpid.lvalue, fp.lvalue)]) self._run(server_id=server_id)