Revision ec6c3949

b/kamaki/cli/commands/image.py
48 48
    FlagArgument, ValueArgument, RepeatableArgument, KeyValueArgument,
49 49
    IntArgument, ProgressBarArgument)
50 50
from kamaki.cli.commands.cyclades import _init_cyclades
51
from kamaki.cli.errors import raiseCLIError, CLIBaseUrlError
51
from kamaki.cli.errors import (
52
    raiseCLIError, CLIBaseUrlError, CLIInvalidArgument)
52 53
from kamaki.cli.commands import _command_init, errors, addLogSettings
53 54
from kamaki.cli.commands import (
54 55
    _optional_output_cmd, _optional_json, _name_filter, _id_filter)
......
373 374
        self._run(image_id=image_id)
374 375

  
375 376

  
377
class PithosLocationArgument(ValueArgument):
378
    """Resolve pithos url, return in the form pithos://uuid/container/path"""
379

  
380
    def __init__(
381
            self, help=None, parsed_name=None, default=None, user_uuid=None):
382
        super(PithosLocationArgument, self).__init__(
383
            help=help, parsed_name=parsed_name, default=default)
384
        self.uuid, self.container, self.path = user_uuid, None, None
385

  
386
    def setdefault(self, term, value):
387
        if not getattr(self, term, None):
388
            setattr(self, term, value)
389

  
390
    @property
391
    def value(self):
392
        return 'pithos://%s/%s/%s' % (self.uuid, self.container, self.path)
393

  
394
    @value.setter
395
    def value(self, location):
396
        if location:
397
            from kamaki.cli.commands.pithos import _pithos_container as pc
398
            try:
399
                uuid, self.container, self.path = pc._resolve_pithos_url(
400
                    location)
401
                self.uuid = uuid or self.uuid
402
                for term in ('container', 'path'):
403
                    assert getattr(self, term, None), 'No %s' % term
404
            except Exception as e:
405
                raise CLIInvalidArgument(
406
                    'Invalid Pithos+ location %s (%s)' % (location, e),
407
                    details=[
408
                        'The image location must be a valid Pithos+',
409
                        'location. There are two valid formats:',
410
                        '  pithos://USER_UUID/CONTAINER/PATH',
411
                        'OR',
412
                        '  /CONTAINER/PATH',
413
                        'To see all containers:',
414
                        '  [kamaki] container list',
415
                        'To list the contents of a container:',
416
                        '  [kamaki] container list CONTAINER'])
417

  
418

  
376 419
@command(image_cmds)
377 420
class image_register(_init_image, _optional_json):
378 421
    """(Re)Register an image file to an Image service
......
381 424
    only automatically (e.g., image id). There are also some custom user
382 425
    metadata, called properties.
383 426
    A register command creates a remote meta file at
384
    .  <container>:<image path>.meta
427
    /<container>/<image path>.meta
385 428
    Users may download and edit this file and use it to re-register one or more
386 429
    images.
387 430
    In case of a meta file, runtime arguments for metadata or properties
......
417 460
        uuid=ValueArgument('Custom user uuid', '--uuid'),
418 461
        local_image_path=ValueArgument(
419 462
            'Local image file path to upload and register '
420
            '(still need target file in the form container:remote-path )',
463
            '(still need target file in the form /ontainer/remote-path )',
421 464
            '--upload-image-file'),
422 465
        progress_bar=ProgressBarArgument(
423
            'Do not use progress bar', '--no-progress-bar', default=False)
466
            'Do not use progress bar', '--no-progress-bar', default=False),
467
        name=ValueArgument('The name of the new image', '--name'),
468
        pithos_location=PithosLocationArgument(
469
            'The Pithos+ image location to put the image at. Format:       '
470
            'pithos://USER_UUID/CONTAINER/IMAGE                  or   '
471
            '/CONTAINER/IMAGE',
472
            '--location')
424 473
    )
474
    required = ('name', 'pithos_location')
425 475

  
426
    def _get_user_id(self):
427
        atoken = self.client.token
428
        if getattr(self, 'auth_base', False):
429
            return self.auth_base.term('id', atoken)
430
        else:
431
            astakos_url = self.config.get('user', 'url') or self.config.get(
432
                'astakos', 'url')
433
            if not astakos_url:
434
                raise CLIBaseUrlError(service='astakos')
435
            user = AstakosClient(astakos_url, atoken)
436
            return user.term('id')
437

  
438
    def _get_pithos_client(self, container):
476
    def _get_pithos_client(self, locator):
439 477
        if self['no_metafile_upload']:
440 478
            return None
441 479
        ptoken = self.client.token
......
447 485
            purl = self.config.get_cloud('pithos', 'url')
448 486
        if not purl:
449 487
            raise CLIBaseUrlError(service='pithos')
450
        return PithosClient(purl, ptoken, self._get_user_id(), container)
451

  
452
    def _store_remote_metafile(self, pclient, remote_path, metadata):
453
        return pclient.upload_from_string(
454
            remote_path, _validate_image_meta(metadata, return_str=True),
455
            container_info_cache=self.container_info_cache)
488
        return PithosClient(purl, ptoken, locator.uuid, locator.container)
456 489

  
457 490
    def _load_params_from_file(self, location):
458 491
        params, properties = dict(), dict()
......
488 521
        for k, v in self['properties'].items():
489 522
            properties[k.upper().replace('-', '_')] = v
490 523

  
491
    def _validate_location(self, location):
492
        if not location:
493
            raiseCLIError(
494
                'No image file location provided',
495
                importance=2, details=[
496
                    'An image location is needed. Image location format:',
497
                    '  <container>:<path>',
498
                    ' where an image file at the above location must exist.'
499
                    ] + howto_image_file)
500
        try:
501
            return _validate_image_location(location)
502
        except AssertionError as ae:
503
            raiseCLIError(
504
                ae, 'Invalid image location format',
505
                importance=1, details=[
506
                    'Valid image location format:',
507
                    '  <container>:<img-file-path>'
508
                    ] + howto_image_file)
509

  
510
    @staticmethod
511
    def _old_location_format(location):
512
        prefix = 'pithos://'
513
        try:
514
            if location.startswith(prefix):
515
                uuid, sep, rest = location[len(prefix):].partition('/')
516
                container, sep, path = rest.partition('/')
517
                return (uuid, container, path)
518
        except Exception as e:
519
            raiseCLIError(e, 'Invalid location format', details=[
520
                'Correct location format:', '  <container>:<image path>'])
521
        return ()
522

  
523
    def _mine_location(self, container_path):
524
        old_response = self._old_location_format(container_path)
525
        if old_response:
526
            return old_response
527
        uuid = self['uuid'] or (self._username2uuid(self['owner_name']) if (
528
                    self['owner_name']) else self._get_user_id())
529
        if not uuid:
530
            if self['owner_name']:
531
                raiseCLIError('No user with username %s' % self['owner_name'])
532
            raiseCLIError('Failed to get user uuid', details=[
533
                'For details on current user:',
534
                '  /user whoami',
535
                'To authenticate a new user through a user token:',
536
                '  /user authenticate <token>'])
537
        if self['container']:
538
            return uuid, self['container'], container_path
539
        container, sep, path = container_path.partition(':')
540
        if not (bool(container) and bool(path)):
541
            raiseCLIError(
542
                'Incorrect container-path format', importance=1, details=[
543
                'Use : to seperate container form path',
544
                '  <container>:<image-path>',
545
                'OR',
546
                'Use -C to specifiy a container',
547
                '  -C <container> <image-path>'] + howto_image_file)
548

  
549
        return uuid, container, path
550

  
551 524
    @errors.generic.all
552 525
    @errors.plankton.connection
553
    @errors.pithos.container
554
    def _run(self, name, uuid, dst_cont, img_path):
526
    def _run(self, name, location):
527
        locator = self.arguments['pithos_location']
555 528
        if self['local_image_path']:
556 529
            with open(self['local_image_path']) as f:
557
                pithos = self._get_pithos_client(dst_cont)
530
                pithos = self._get_pithos_client(locator)
558 531
                (pbar, upload_cb) = self._safe_progress_bar('Uploading')
559 532
                if pbar:
560 533
                    hash_bar = pbar.clone()
561 534
                    hash_cb = hash_bar.get_generator('Calculating hashes')
562 535
                pithos.upload_object(
563
                    img_path, f,
536
                    locator.path, f,
564 537
                    hash_cb=hash_cb, upload_cb=upload_cb,
565 538
                    container_info_cache=self.container_info_cache)
566 539
                pbar.finish()
567 540

  
568
        location = 'pithos://%s/%s/%s' % (uuid, dst_cont, img_path)
569 541
        (params, properties, new_loc) = self._load_params_from_file(location)
570 542
        if location != new_loc:
571
            uuid, dst_cont, img_path = self._validate_location(new_loc)
543
            locator.value = new_loc
572 544
        self._load_params_from_args(params, properties)
573
        pclient = self._get_pithos_client(dst_cont)
545
        pclient = self._get_pithos_client(locator)
574 546

  
575 547
        #check if metafile exists
576
        meta_path = '%s.meta' % img_path
548
        meta_path = '%s.meta' % locator.path
577 549
        if pclient and not self['metafile_force']:
578 550
            try:
579 551
                pclient.get_object_info(meta_path)
580 552
                raiseCLIError(
581
                    'Metadata file %s:%s already exists, abort' % (
582
                        dst_cont, meta_path),
553
                    'Metadata file /%s/%s already exists, abort' % (
554
                        locator.container, meta_path),
583 555
                    details=['Registration ABORTED', 'Try -f to overwrite'])
584 556
            except ClientError as ce:
585 557
                if ce.status != 404:
......
594 566
                    ce, 'Nonexistent image file location\n\t%s' % location,
595 567
                    details=[
596 568
                        'Does the image file %s exist at container %s ?' % (
597
                            img_path, dst_cont)] + howto_image_file)
569
                            locator.path,
570
                            locator.container)] + howto_image_file)
598 571
            raise
599 572
        r['owner'] += ' (%s)' % self._uuid2username(r['owner'])
600 573
        self._print(r, self.print_dict)
......
607 580
                    container_info_cache=self.container_info_cache)
608 581
            except TypeError:
609 582
                self.error(
610
                    'Failed to dump metafile %s:%s' % (dst_cont, meta_path))
583
                    'Failed to dump metafile /%s/%s' % (
584
                        locator.container, meta_path))
611 585
                return
612 586
            if self['json_output'] or self['output_format']:
613 587
                self.print_json(dict(
614
                    metafile_location='%s:%s' % (dst_cont, meta_path),
588
                    metafile_location='/%s/%s' % (
589
                        locator.container, meta_path),
615 590
                    headers=meta_headers))
616 591
            else:
617
                self.error('Metadata file uploaded as %s:%s (version %s)' % (
618
                    dst_cont, meta_path, meta_headers['x-object-version']))
592
                self.error('Metadata file uploaded as /%s/%s (version %s)' % (
593
                    locator.container,
594
                    meta_path,
595
                    meta_headers['x-object-version']))
619 596

  
620
    def main(self, name, container___image_path):
597
    def main(self):
621 598
        super(self.__class__, self)._run()
622
        self._run(name, *self._mine_location(container___image_path))
599
        self.arguments['pithos_location'].setdefault(
600
            'uuid', self.auth_base.user_term('id'))
601
        self._run(self['name'], self['pithos_location'])
623 602

  
624 603

  
625 604
@command(image_cmds)
b/kamaki/cli/commands/pithos.py
181 181

  
182 182
    def _run(self, url=None):
183 183
        acc, con, self.path = self._resolve_pithos_url(url or '')
184
        self.account = acc or getattr(self, 'account', '')
184
        #  self.account = acc or getattr(self, 'account', '')
185 185
        super(_pithos_container, self)._run()
186 186
        self.container = con or self['container'] or getattr(
187 187
            self, 'container', None) or getattr(self.client, 'container', '')
188
        self.client.account = acc or self.client.account
188 189
        self.client.container = self.container
189 190

  
190 191

  
......
618 619
            container=self[
619 620
                'destination_container'] or dst_con or self.client.container,
620 621
            account=self[
621
                'destination_user_uuid'] or dst_acc or self.client.account)
622
                'destination_user_uuid'] or dst_acc or self.account)
622 623
        self.dst_path = dst_path or self.path
623 624

  
624 625

  
......
647 648
                    src_object=src,
648 649
                    dst_container=self.dst_client.container,
649 650
                    dst_object=dst,
650
                    source_account=self.account,
651
                    source_account=self.client.account,
651 652
                    source_version=self['source_version'],
652 653
                    public=self['public'],
653 654
                    content_type=self['content_type'])

Also available in: Unified diff