Statistics
| Branch: | Tag: | Revision:

root / kamaki / cli / commands / pithos.py @ 74b7c6dc

History | View | Annotate | Download (43.3 kB)

1 e3f01d64 Stavros Sachtouris
# Copyright 2011-2013 GRNET S.A. All rights reserved.
2 7493ccb6 Stavros Sachtouris
#
3 7493ccb6 Stavros Sachtouris
# Redistribution and use in source and binary forms, with or
4 7493ccb6 Stavros Sachtouris
# without modification, are permitted provided that the following
5 7493ccb6 Stavros Sachtouris
# conditions are met:
6 7493ccb6 Stavros Sachtouris
#
7 7493ccb6 Stavros Sachtouris
#   1. Redistributions of source code must retain the above
8 7493ccb6 Stavros Sachtouris
#      copyright notice, this list of conditions and the following
9 7493ccb6 Stavros Sachtouris
#      disclaimer.
10 7493ccb6 Stavros Sachtouris
#
11 7493ccb6 Stavros Sachtouris
#   2. Redistributions in binary form must reproduce the above
12 7493ccb6 Stavros Sachtouris
#      copyright notice, this list of conditions and the following
13 7493ccb6 Stavros Sachtouris
#      disclaimer in the documentation and/or other materials
14 7493ccb6 Stavros Sachtouris
#      provided with the distribution.
15 7493ccb6 Stavros Sachtouris
#
16 7493ccb6 Stavros Sachtouris
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 7493ccb6 Stavros Sachtouris
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 7493ccb6 Stavros Sachtouris
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 7493ccb6 Stavros Sachtouris
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20 7493ccb6 Stavros Sachtouris
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 7493ccb6 Stavros Sachtouris
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 7493ccb6 Stavros Sachtouris
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 7493ccb6 Stavros Sachtouris
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 7493ccb6 Stavros Sachtouris
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 7493ccb6 Stavros Sachtouris
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 7493ccb6 Stavros Sachtouris
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 7493ccb6 Stavros Sachtouris
# POSSIBILITY OF SUCH DAMAGE.
28 7493ccb6 Stavros Sachtouris
#
29 7493ccb6 Stavros Sachtouris
# The views and conclusions contained in the software and
30 7493ccb6 Stavros Sachtouris
# documentation are those of the authors and should not be
31 7493ccb6 Stavros Sachtouris
# interpreted as representing official policies, either expressed
32 7493ccb6 Stavros Sachtouris
# or implied, of GRNET S.A.command
33 7493ccb6 Stavros Sachtouris
34 fa9c0c38 Stavros Sachtouris
from io import StringIO
35 76f58e2e Stavros Sachtouris
from pydoc import pager
36 edc1182f Stavros Sachtouris
from os import path, walk, makedirs
37 1395c40e Stavros Sachtouris
38 effa4b8f Stavros Sachtouris
from kamaki.clients.pithos import PithosClient, ClientError
39 effa4b8f Stavros Sachtouris
40 234954d1 Stavros Sachtouris
from kamaki.cli import command
41 d486baec Stavros Sachtouris
from kamaki.cli.command_tree import CommandTree
42 6d190dd1 Stavros Sachtouris
from kamaki.cli.commands import (
43 cec2dfcd Stavros Sachtouris
    _command_init, errors, addLogSettings, DontRaiseKeyError, _optional_json,
44 cec2dfcd Stavros Sachtouris
    _name_filter, _optional_output_cmd)
45 74b7c6dc Stavros Sachtouris
from kamaki.cli.errors import (
46 74b7c6dc Stavros Sachtouris
    CLIBaseUrlError, CLIError, CLIInvalidArgument, raiseCLIError)
47 cec2dfcd Stavros Sachtouris
from kamaki.cli.argument import (
48 edc1182f Stavros Sachtouris
    FlagArgument, IntArgument, ValueArgument, DateArgument,
49 edc1182f Stavros Sachtouris
    ProgressBarArgument, RepeatableArgument)
50 edc1182f Stavros Sachtouris
from kamaki.cli.utils import (
51 edc1182f Stavros Sachtouris
    format_size, bold, get_path_size, guess_mime_type)
52 201baa17 Stavros Sachtouris
53 cec2dfcd Stavros Sachtouris
file_cmds = CommandTree('file', 'Pithos+/Storage object level API commands')
54 cec2dfcd Stavros Sachtouris
container_cmds = CommandTree(
55 cec2dfcd Stavros Sachtouris
    'container', 'Pithos+/Storage container level API commands')
56 cec2dfcd Stavros Sachtouris
sharers_commands = CommandTree('sharers', 'Pithos+/Storage sharers')
57 cec2dfcd Stavros Sachtouris
_commands = [file_cmds, container_cmds, sharers_commands]
58 234954d1 Stavros Sachtouris
59 e3d4d442 Stavros Sachtouris
60 5eae854d Stavros Sachtouris
class _pithos_init(_command_init):
61 cec2dfcd Stavros Sachtouris
    """Initilize a pithos+ client
62 cec2dfcd Stavros Sachtouris
    There is always a default account (current user uuid)
63 cec2dfcd Stavros Sachtouris
    There is always a default container (pithos)
64 cec2dfcd Stavros Sachtouris
    """
65 ece4ae4b Stavros Sachtouris
66 b4f69041 Stavros Sachtouris
    @DontRaiseKeyError
67 b4f69041 Stavros Sachtouris
    def _custom_container(self):
68 144b3551 Stavros Sachtouris
        return self.config.get_cloud(self.cloud, 'pithos_container')
69 b4f69041 Stavros Sachtouris
70 b4f69041 Stavros Sachtouris
    @DontRaiseKeyError
71 b4f69041 Stavros Sachtouris
    def _custom_uuid(self):
72 144b3551 Stavros Sachtouris
        return self.config.get_cloud(self.cloud, 'pithos_uuid')
73 b4f69041 Stavros Sachtouris
74 b4f69041 Stavros Sachtouris
    def _set_account(self):
75 b4f69041 Stavros Sachtouris
        self.account = self._custom_uuid()
76 b4f69041 Stavros Sachtouris
        if self.account:
77 b4f69041 Stavros Sachtouris
            return
78 cec2dfcd Stavros Sachtouris
        astakos = getattr(self, 'auth_base', None)
79 cec2dfcd Stavros Sachtouris
        if astakos:
80 cec2dfcd Stavros Sachtouris
            self.account = astakos.user_term('id', self.token)
81 b4f69041 Stavros Sachtouris
        else:
82 cec2dfcd Stavros Sachtouris
            raise CLIBaseUrlError(service='astakos')
83 b4f69041 Stavros Sachtouris
84 1395c40e Stavros Sachtouris
    @errors.generic.all
85 b4f69041 Stavros Sachtouris
    @addLogSettings
86 1395c40e Stavros Sachtouris
    def _run(self):
87 cec2dfcd Stavros Sachtouris
        cloud = getattr(self, 'cloud', None)
88 cec2dfcd Stavros Sachtouris
        if cloud:
89 b4f69041 Stavros Sachtouris
            self.base_url = self._custom_url('pithos')
90 b4f69041 Stavros Sachtouris
        else:
91 b4f69041 Stavros Sachtouris
            self.cloud = 'default'
92 b4f69041 Stavros Sachtouris
        self.token = self._custom_token('pithos')
93 cec2dfcd Stavros Sachtouris
        self.container = self._custom_container() or 'pithos'
94 8cec3671 Stavros Sachtouris
95 cec2dfcd Stavros Sachtouris
        astakos = getattr(self, 'auth_base', None)
96 cec2dfcd Stavros Sachtouris
        if astakos:
97 cec2dfcd Stavros Sachtouris
            self.token = self.token or astakos.token
98 b4f69041 Stavros Sachtouris
            if not self.base_url:
99 cec2dfcd Stavros Sachtouris
                pithos_endpoints = astakos.get_service_endpoints(
100 b4f69041 Stavros Sachtouris
                    self._custom_type('pithos') or 'object-store',
101 b4f69041 Stavros Sachtouris
                    self._custom_version('pithos') or '')
102 b4f69041 Stavros Sachtouris
                self.base_url = pithos_endpoints['publicURL']
103 cec2dfcd Stavros Sachtouris
        else:
104 cec2dfcd Stavros Sachtouris
            raise CLIBaseUrlError(service='astakos')
105 8cec3671 Stavros Sachtouris
106 1f5debf7 Stavros Sachtouris
        self._set_account()
107 de73876b Stavros Sachtouris
        self.client = PithosClient(
108 cec2dfcd Stavros Sachtouris
            self.base_url, self.token, self.account, self.container)
109 7493ccb6 Stavros Sachtouris
110 1395c40e Stavros Sachtouris
    def main(self):
111 1395c40e Stavros Sachtouris
        self._run()
112 1395c40e Stavros Sachtouris
113 234954d1 Stavros Sachtouris
114 cec2dfcd Stavros Sachtouris
class _pithos_account(_pithos_init):
115 cec2dfcd Stavros Sachtouris
    """Setup account"""
116 7493ccb6 Stavros Sachtouris
117 effa4b8f Stavros Sachtouris
    def __init__(self, arguments={}, auth_base=None, cloud=None):
118 effa4b8f Stavros Sachtouris
        super(_pithos_account, self).__init__(arguments, auth_base, cloud)
119 439826ec Stavros Sachtouris
        self['account'] = ValueArgument(
120 cec2dfcd Stavros Sachtouris
            'Use (a different) user uuid', ('-A', '--account'))
121 1395c40e Stavros Sachtouris
122 cec2dfcd Stavros Sachtouris
    def _run(self):
123 cec2dfcd Stavros Sachtouris
        super(_pithos_account, self)._run()
124 cec2dfcd Stavros Sachtouris
        self.client.account = self['account'] or getattr(
125 cec2dfcd Stavros Sachtouris
            self, 'account', getattr(self.client, 'account', None))
126 234954d1 Stavros Sachtouris
127 7493ccb6 Stavros Sachtouris
128 cec2dfcd Stavros Sachtouris
class _pithos_container(_pithos_account):
129 cec2dfcd Stavros Sachtouris
    """Setup container"""
130 2d7ce81e Stavros Sachtouris
131 effa4b8f Stavros Sachtouris
    def __init__(self, arguments={}, auth_base=None, cloud=None):
132 effa4b8f Stavros Sachtouris
        super(_pithos_container, self).__init__(arguments, auth_base, cloud)
133 439826ec Stavros Sachtouris
        self['container'] = ValueArgument(
134 cec2dfcd Stavros Sachtouris
            'Use this container (default: pithos)', ('-C', '--container'))
135 7493ccb6 Stavros Sachtouris
136 effa4b8f Stavros Sachtouris
    @staticmethod
137 effa4b8f Stavros Sachtouris
    def _is_dir(remote_dict):
138 effa4b8f Stavros Sachtouris
        return 'application/directory' == remote_dict.get(
139 effa4b8f Stavros Sachtouris
            'content_type', remote_dict.get('content-type', ''))
140 effa4b8f Stavros Sachtouris
141 effa4b8f Stavros Sachtouris
    @staticmethod
142 effa4b8f Stavros Sachtouris
    def _resolve_pithos_url(url):
143 cec2dfcd Stavros Sachtouris
        """Match urls of one of the following formats:
144 cec2dfcd Stavros Sachtouris
        pithos://ACCOUNT/CONTAINER/OBJECT_PATH
145 cec2dfcd Stavros Sachtouris
        /CONTAINER/OBJECT_PATH
146 effa4b8f Stavros Sachtouris
        return account, container, path
147 1395c40e Stavros Sachtouris
        """
148 edc1182f Stavros Sachtouris
        account, container, obj_path, prefix = '', '', url, 'pithos://'
149 cec2dfcd Stavros Sachtouris
        if url.startswith(prefix):
150 effa4b8f Stavros Sachtouris
            account, sep, url = url[len(prefix):].partition('/')
151 cec2dfcd Stavros Sachtouris
            url = '/%s' % url
152 cec2dfcd Stavros Sachtouris
        if url.startswith('/'):
153 edc1182f Stavros Sachtouris
            container, sep, obj_path = url[1:].partition('/')
154 edc1182f Stavros Sachtouris
        return account, container, obj_path
155 447c9568 Stavros Sachtouris
156 cec2dfcd Stavros Sachtouris
    def _run(self, url=None):
157 effa4b8f Stavros Sachtouris
        acc, con, self.path = self._resolve_pithos_url(url or '')
158 effa4b8f Stavros Sachtouris
        self.account = acc or getattr(self, 'account', '')
159 cec2dfcd Stavros Sachtouris
        super(_pithos_container, self)._run()
160 effa4b8f Stavros Sachtouris
        self.container = con or self['container'] or getattr(
161 cec2dfcd Stavros Sachtouris
            self, 'container', None) or getattr(self.client, 'container', '')
162 effa4b8f Stavros Sachtouris
        self.client.container = self.container
163 447c9568 Stavros Sachtouris
164 1395c40e Stavros Sachtouris
165 cec2dfcd Stavros Sachtouris
@command(file_cmds)
166 cec2dfcd Stavros Sachtouris
class file_list(_pithos_container, _optional_json, _name_filter):
167 cec2dfcd Stavros Sachtouris
    """List all objects in a container or a directory object"""
168 7493ccb6 Stavros Sachtouris
169 47ae7577 Stavros Sachtouris
    arguments = dict(
170 f40f0cb7 Stavros Sachtouris
        detail=FlagArgument('detailed output', ('-l', '--list')),
171 f40f0cb7 Stavros Sachtouris
        limit=IntArgument('limit number of listed items', ('-n', '--number')),
172 f40f0cb7 Stavros Sachtouris
        marker=ValueArgument('output greater that marker', '--marker'),
173 47ae7577 Stavros Sachtouris
        delimiter=ValueArgument('show output up to delimiter', '--delimiter'),
174 47ae7577 Stavros Sachtouris
        meta=ValueArgument(
175 201baa17 Stavros Sachtouris
            'show output with specified meta keys', '--meta',
176 47ae7577 Stavros Sachtouris
            default=[]),
177 47ae7577 Stavros Sachtouris
        if_modified_since=ValueArgument(
178 201baa17 Stavros Sachtouris
            'show output modified since then', '--if-modified-since'),
179 47ae7577 Stavros Sachtouris
        if_unmodified_since=ValueArgument(
180 201baa17 Stavros Sachtouris
            'show output not modified since then', '--if-unmodified-since'),
181 47ae7577 Stavros Sachtouris
        until=DateArgument('show metadata until then', '--until'),
182 47ae7577 Stavros Sachtouris
        format=ValueArgument(
183 201baa17 Stavros Sachtouris
            'format to parse until data (default: d/m/Y H:M:S )', '--format'),
184 47ae7577 Stavros Sachtouris
        shared=FlagArgument('show only shared', '--shared'),
185 fa9c0c38 Stavros Sachtouris
        more=FlagArgument('read long results', '--more'),
186 8694bdad Stavros Sachtouris
        enum=FlagArgument('Enumerate results', '--enumerate'),
187 8694bdad Stavros Sachtouris
        recursive=FlagArgument(
188 8694bdad Stavros Sachtouris
            'Recursively list containers and their contents',
189 8694bdad Stavros Sachtouris
            ('-R', '--recursive'))
190 47ae7577 Stavros Sachtouris
    )
191 c41a86b2 Stavros Sachtouris
192 1757c616 Stavros Sachtouris
    def print_objects(self, object_list):
193 234954d1 Stavros Sachtouris
        for index, obj in enumerate(object_list):
194 7493ccb6 Stavros Sachtouris
            pretty_obj = obj.copy()
195 7493ccb6 Stavros Sachtouris
            index += 1
196 234954d1 Stavros Sachtouris
            empty_space = ' ' * (len(str(len(object_list))) - len(str(index)))
197 a339a3ee Stavros Sachtouris
            if 'subdir' in obj:
198 a339a3ee Stavros Sachtouris
                continue
199 edc1182f Stavros Sachtouris
            if self._is_dir(obj):
200 edc1182f Stavros Sachtouris
                size = 'D'
201 7493ccb6 Stavros Sachtouris
            else:
202 edc1182f Stavros Sachtouris
                size = format_size(obj['bytes'])
203 234954d1 Stavros Sachtouris
                pretty_obj['bytes'] = '%s (%s)' % (obj['bytes'], size)
204 fa9c0c38 Stavros Sachtouris
            oname = obj['name'] if self['more'] else bold(obj['name'])
205 cec2dfcd Stavros Sachtouris
            prfx = ('%s%s. ' % (empty_space, index)) if self['enum'] else ''
206 47ae7577 Stavros Sachtouris
            if self['detail']:
207 ff1c0296 Stavros Sachtouris
                self.writeln('%s%s' % (prfx, oname))
208 76f58e2e Stavros Sachtouris
                self.print_dict(pretty_obj, exclude=('name'))
209 ff1c0296 Stavros Sachtouris
                self.writeln()
210 7493ccb6 Stavros Sachtouris
            else:
211 ff1c0296 Stavros Sachtouris
                oname = '%s%9s %s' % (prfx, size, oname)
212 edc1182f Stavros Sachtouris
                oname += '/' if self._is_dir(obj) else u''
213 ff1c0296 Stavros Sachtouris
                self.writeln(oname)
214 7493ccb6 Stavros Sachtouris
215 1395c40e Stavros Sachtouris
    @errors.generic.all
216 1395c40e Stavros Sachtouris
    @errors.pithos.connection
217 1395c40e Stavros Sachtouris
    @errors.pithos.container
218 cec2dfcd Stavros Sachtouris
    @errors.pithos.object_path
219 1395c40e Stavros Sachtouris
    def _run(self):
220 cec2dfcd Stavros Sachtouris
        r = self.client.container_get(
221 cec2dfcd Stavros Sachtouris
            limit=False if self['more'] else self['limit'],
222 cec2dfcd Stavros Sachtouris
            marker=self['marker'],
223 cec2dfcd Stavros Sachtouris
            prefix=self['name_pref'] or '/',
224 cec2dfcd Stavros Sachtouris
            delimiter=self['delimiter'],
225 cec2dfcd Stavros Sachtouris
            path=self.path or '',
226 cec2dfcd Stavros Sachtouris
            if_modified_since=self['if_modified_since'],
227 cec2dfcd Stavros Sachtouris
            if_unmodified_since=self['if_unmodified_since'],
228 cec2dfcd Stavros Sachtouris
            until=self['until'],
229 cec2dfcd Stavros Sachtouris
            meta=self['meta'],
230 cec2dfcd Stavros Sachtouris
            show_only_shared=self['shared'])
231 cec2dfcd Stavros Sachtouris
        files = self._filter_by_name(r.json)
232 1757c616 Stavros Sachtouris
        if self['more']:
233 1757c616 Stavros Sachtouris
            outbu, self._out = self._out, StringIO()
234 1757c616 Stavros Sachtouris
        try:
235 f76c6bbc Stavros Sachtouris
            if self['json_output'] or self['output_format']:
236 1757c616 Stavros Sachtouris
                self._print(files)
237 1757c616 Stavros Sachtouris
            else:
238 cec2dfcd Stavros Sachtouris
                self.print_objects(files)
239 1757c616 Stavros Sachtouris
        finally:
240 1757c616 Stavros Sachtouris
            if self['more']:
241 1757c616 Stavros Sachtouris
                pager(self._out.getvalue())
242 1757c616 Stavros Sachtouris
                self._out = outbu
243 1395c40e Stavros Sachtouris
244 cec2dfcd Stavros Sachtouris
    def main(self, path_or_url='/'):
245 cec2dfcd Stavros Sachtouris
        super(self.__class__, self)._run(path_or_url)
246 1395c40e Stavros Sachtouris
        self._run()
247 7493ccb6 Stavros Sachtouris
248 234954d1 Stavros Sachtouris
249 cec2dfcd Stavros Sachtouris
@command(file_cmds)
250 cec2dfcd Stavros Sachtouris
class file_create(_pithos_container, _optional_output_cmd):
251 effa4b8f Stavros Sachtouris
    """Create an empty file"""
252 1e29b9f6 Stavros Sachtouris
253 1e29b9f6 Stavros Sachtouris
    arguments = dict(
254 1e29b9f6 Stavros Sachtouris
        content_type=ValueArgument(
255 1e29b9f6 Stavros Sachtouris
            'Set content type (default: application/octet-stream)',
256 1e29b9f6 Stavros Sachtouris
            '--content-type',
257 915b99b5 Stavros Sachtouris
            default='application/octet-stream')
258 1e29b9f6 Stavros Sachtouris
    )
259 1e29b9f6 Stavros Sachtouris
260 effa4b8f Stavros Sachtouris
    @errors.generic.all
261 effa4b8f Stavros Sachtouris
    @errors.pithos.connection
262 effa4b8f Stavros Sachtouris
    @errors.pithos.container
263 1395c40e Stavros Sachtouris
    def _run(self):
264 915b99b5 Stavros Sachtouris
        self._optional_output(
265 915b99b5 Stavros Sachtouris
            self.client.create_object(self.path, self['content_type']))
266 1395c40e Stavros Sachtouris
267 cec2dfcd Stavros Sachtouris
    def main(self, path_or_url):
268 cec2dfcd Stavros Sachtouris
        super(self.__class__, self)._run(path_or_url)
269 b4cf92b8 Stavros Sachtouris
        self._run()
270 effa4b8f Stavros Sachtouris
271 effa4b8f Stavros Sachtouris
272 effa4b8f Stavros Sachtouris
@command(file_cmds)
273 effa4b8f Stavros Sachtouris
class file_mkdir(_pithos_container, _optional_output_cmd):
274 effa4b8f Stavros Sachtouris
    """Create a directory: /file create --content-type='applcation/directory'
275 effa4b8f Stavros Sachtouris
    """
276 effa4b8f Stavros Sachtouris
277 effa4b8f Stavros Sachtouris
    @errors.generic.all
278 effa4b8f Stavros Sachtouris
    @errors.pithos.connection
279 effa4b8f Stavros Sachtouris
    @errors.pithos.container
280 effa4b8f Stavros Sachtouris
    def _run(self):
281 effa4b8f Stavros Sachtouris
        self._optional_output(self.client.create_directory(self.path))
282 effa4b8f Stavros Sachtouris
283 74b7c6dc Stavros Sachtouris
    def main(self, path_or_url):
284 74b7c6dc Stavros Sachtouris
        super(self.__class__, self)._run(path_or_url)
285 effa4b8f Stavros Sachtouris
        self._run()
286 effa4b8f Stavros Sachtouris
287 effa4b8f Stavros Sachtouris
288 effa4b8f Stavros Sachtouris
class _source_destination(_pithos_container, _optional_output_cmd):
289 effa4b8f Stavros Sachtouris
290 de932277 Stavros Sachtouris
    sd_arguments = dict(
291 effa4b8f Stavros Sachtouris
        destination_user_uuid=ValueArgument(
292 effa4b8f Stavros Sachtouris
            'default: current user uuid', '--to-account'),
293 effa4b8f Stavros Sachtouris
        destination_container=ValueArgument(
294 effa4b8f Stavros Sachtouris
            'default: pithos', '--to-container'),
295 effa4b8f Stavros Sachtouris
        source_prefix=FlagArgument(
296 effa4b8f Stavros Sachtouris
            'Transfer all files that are prefixed with SOURCE PATH If the '
297 effa4b8f Stavros Sachtouris
            'destination path is specified, replace SOURCE_PATH with '
298 effa4b8f Stavros Sachtouris
            'DESTINATION_PATH',
299 de932277 Stavros Sachtouris
            ('-r', '--recursive')),
300 effa4b8f Stavros Sachtouris
        force=FlagArgument(
301 effa4b8f Stavros Sachtouris
            'Overwrite destination objects, if needed', ('-f', '--force'))
302 effa4b8f Stavros Sachtouris
    )
303 effa4b8f Stavros Sachtouris
304 effa4b8f Stavros Sachtouris
    def __init__(self, arguments={}, auth_base=None, cloud=None):
305 effa4b8f Stavros Sachtouris
        self.arguments.update(arguments)
306 de932277 Stavros Sachtouris
        self.arguments.update(self.sd_arguments)
307 de932277 Stavros Sachtouris
        super(_source_destination, self).__init__(
308 de932277 Stavros Sachtouris
            self.arguments, auth_base, cloud)
309 de932277 Stavros Sachtouris
310 de932277 Stavros Sachtouris
    def _report_transfer(self, src, dst, transfer_name):
311 de932277 Stavros Sachtouris
        if not dst:
312 de932277 Stavros Sachtouris
            if transfer_name in ('move', ):
313 de932277 Stavros Sachtouris
                self.error('  delete source directory %s' % src)
314 de932277 Stavros Sachtouris
            return
315 de932277 Stavros Sachtouris
        dst_prf = '' if self.account == self.dst_client.account else (
316 de932277 Stavros Sachtouris
                'pithos://%s' % self.dst_client.account)
317 de932277 Stavros Sachtouris
        if src:
318 de932277 Stavros Sachtouris
            src_prf = '' if self.account == self.dst_client.account else (
319 de932277 Stavros Sachtouris
                    'pithos://%s' % self.account)
320 de932277 Stavros Sachtouris
            self.error('  %s %s/%s/%s\n  -->  %s/%s/%s' % (
321 de932277 Stavros Sachtouris
                transfer_name,
322 de932277 Stavros Sachtouris
                src_prf, self.container, src,
323 de932277 Stavros Sachtouris
                dst_prf, self.dst_client.container, dst))
324 de932277 Stavros Sachtouris
        else:
325 de932277 Stavros Sachtouris
            self.error('  mkdir %s/%s/%s' % (
326 de932277 Stavros Sachtouris
                dst_prf, self.dst_client.container, dst))
327 effa4b8f Stavros Sachtouris
328 effa4b8f Stavros Sachtouris
    @errors.generic.all
329 effa4b8f Stavros Sachtouris
    @errors.pithos.account
330 effa4b8f Stavros Sachtouris
    def _src_dst(self, version=None):
331 effa4b8f Stavros Sachtouris
        """Preconditions:
332 effa4b8f Stavros Sachtouris
        self.account, self.container, self.path
333 effa4b8f Stavros Sachtouris
        self.dst_acc, self.dst_con, self.dst_path
334 effa4b8f Stavros Sachtouris
        They should all be configured properly
335 effa4b8f Stavros Sachtouris
        :returns: [(src_path, dst_path), ...], if src_path is None, create
336 effa4b8f Stavros Sachtouris
            destination directory
337 effa4b8f Stavros Sachtouris
        """
338 effa4b8f Stavros Sachtouris
        src_objects, dst_objects, pairs = dict(), dict(), []
339 effa4b8f Stavros Sachtouris
        try:
340 effa4b8f Stavros Sachtouris
            for obj in self.dst_client.list_objects(
341 effa4b8f Stavros Sachtouris
                    prefix=self.dst_path or self.path or '/'):
342 effa4b8f Stavros Sachtouris
                dst_objects[obj['name']] = obj
343 effa4b8f Stavros Sachtouris
        except ClientError as ce:
344 effa4b8f Stavros Sachtouris
            if ce.status in (404, ):
345 effa4b8f Stavros Sachtouris
                raise CLIError(
346 effa4b8f Stavros Sachtouris
                    'Destination container pithos://%s/%s not found' % (
347 effa4b8f Stavros Sachtouris
                        self.dst_client.account, self.dst_client.container))
348 de932277 Stavros Sachtouris
            raise ce
349 effa4b8f Stavros Sachtouris
        if self['source_prefix']:
350 effa4b8f Stavros Sachtouris
            #  Copy and replace prefixes
351 effa4b8f Stavros Sachtouris
            for src_obj in self.client.list_objects(prefix=self.path or '/'):
352 effa4b8f Stavros Sachtouris
                src_objects[src_obj['name']] = src_obj
353 effa4b8f Stavros Sachtouris
            for src_path, src_obj in src_objects.items():
354 effa4b8f Stavros Sachtouris
                dst_path = '%s%s' % (
355 effa4b8f Stavros Sachtouris
                    self.dst_path or self.path, src_path[len(self.path):])
356 effa4b8f Stavros Sachtouris
                dst_obj = dst_objects.get(dst_path, None)
357 effa4b8f Stavros Sachtouris
                if self['force'] or not dst_obj:
358 effa4b8f Stavros Sachtouris
                    #  Just do it
359 de932277 Stavros Sachtouris
                    pairs.append((
360 de932277 Stavros Sachtouris
                        None if self._is_dir(src_obj) else src_path, dst_path))
361 de932277 Stavros Sachtouris
                    if self._is_dir(src_obj):
362 de932277 Stavros Sachtouris
                        pairs.append((self.path or dst_path, None))
363 effa4b8f Stavros Sachtouris
                elif not (self._is_dir(dst_obj) and self._is_dir(src_obj)):
364 effa4b8f Stavros Sachtouris
                    raise CLIError(
365 effa4b8f Stavros Sachtouris
                        'Destination object exists', importance=2, details=[
366 effa4b8f Stavros Sachtouris
                            'Failed while transfering:',
367 effa4b8f Stavros Sachtouris
                            '    pithos://%s/%s/%s' % (
368 effa4b8f Stavros Sachtouris
                                    self.account,
369 effa4b8f Stavros Sachtouris
                                    self.container,
370 effa4b8f Stavros Sachtouris
                                    src_path),
371 effa4b8f Stavros Sachtouris
                            '--> pithos://%s/%s/%s' % (
372 effa4b8f Stavros Sachtouris
                                    self.dst_client.account,
373 effa4b8f Stavros Sachtouris
                                    self.dst_client.container,
374 effa4b8f Stavros Sachtouris
                                    dst_path),
375 effa4b8f Stavros Sachtouris
                            'Use %s to transfer overwrite' % ('/'.join(
376 effa4b8f Stavros Sachtouris
                                    self.arguments['force'].parsed_name))])
377 effa4b8f Stavros Sachtouris
        else:
378 de932277 Stavros Sachtouris
            #  One object transfer
379 de932277 Stavros Sachtouris
            try:
380 de932277 Stavros Sachtouris
                src_obj = self.client.get_object_info(self.path)
381 de932277 Stavros Sachtouris
            except ClientError as ce:
382 de932277 Stavros Sachtouris
                if ce.status in (204, ):
383 de932277 Stavros Sachtouris
                    raise CLIError(
384 de932277 Stavros Sachtouris
                        'Missing specific path container %s' % self.container,
385 de932277 Stavros Sachtouris
                        importance=2, details=[
386 de932277 Stavros Sachtouris
                            'To transfer container contents %s' % (
387 de932277 Stavros Sachtouris
                                '/'.join(self.arguments[
388 de932277 Stavros Sachtouris
                                    'source_prefix'].parsed_name))])
389 de932277 Stavros Sachtouris
                raise
390 effa4b8f Stavros Sachtouris
            dst_path = self.dst_path or self.path
391 74b7c6dc Stavros Sachtouris
            dst_obj = dst_objects.get(dst_path or self.path, None)
392 effa4b8f Stavros Sachtouris
            if self['force'] or not dst_obj:
393 effa4b8f Stavros Sachtouris
                pairs.append(
394 de932277 Stavros Sachtouris
                    (None if self._is_dir(src_obj) else self.path, dst_path))
395 de932277 Stavros Sachtouris
                if self._is_dir(src_obj):
396 de932277 Stavros Sachtouris
                    pairs.append((self.path or dst_path, None))
397 effa4b8f Stavros Sachtouris
            elif self._is_dir(src_obj):
398 effa4b8f Stavros Sachtouris
                raise CLIError(
399 effa4b8f Stavros Sachtouris
                    'Cannot transfer an application/directory object',
400 effa4b8f Stavros Sachtouris
                    importance=2, details=[
401 effa4b8f Stavros Sachtouris
                        'The object pithos://%s/%s/%s is a directory' % (
402 effa4b8f Stavros Sachtouris
                            self.account,
403 effa4b8f Stavros Sachtouris
                            self.container,
404 de932277 Stavros Sachtouris
                            self.path),
405 effa4b8f Stavros Sachtouris
                        'To recursively copy a directory, use',
406 effa4b8f Stavros Sachtouris
                        '  %s' % ('/'.join(
407 effa4b8f Stavros Sachtouris
                            self.arguments['source_prefix'].parsed_name)),
408 effa4b8f Stavros Sachtouris
                        'To create a file, use',
409 effa4b8f Stavros Sachtouris
                        '  /file create  (general purpose)',
410 effa4b8f Stavros Sachtouris
                        '  /file mkdir   (a directory object)'])
411 effa4b8f Stavros Sachtouris
            else:
412 effa4b8f Stavros Sachtouris
                raise CLIError(
413 effa4b8f Stavros Sachtouris
                    'Destination object exists',
414 effa4b8f Stavros Sachtouris
                    importance=2, details=[
415 effa4b8f Stavros Sachtouris
                        'Failed while transfering:',
416 effa4b8f Stavros Sachtouris
                        '    pithos://%s/%s/%s' % (
417 effa4b8f Stavros Sachtouris
                                self.account,
418 effa4b8f Stavros Sachtouris
                                self.container,
419 de932277 Stavros Sachtouris
                                self.path),
420 effa4b8f Stavros Sachtouris
                        '--> pithos://%s/%s/%s' % (
421 effa4b8f Stavros Sachtouris
                                self.dst_client.account,
422 effa4b8f Stavros Sachtouris
                                self.dst_client.container,
423 effa4b8f Stavros Sachtouris
                                dst_path),
424 effa4b8f Stavros Sachtouris
                        'Use %s to transfer overwrite' % ('/'.join(
425 effa4b8f Stavros Sachtouris
                                self.arguments['force'].parsed_name))])
426 effa4b8f Stavros Sachtouris
        return pairs
427 effa4b8f Stavros Sachtouris
428 effa4b8f Stavros Sachtouris
    def _run(self, source_path_or_url, destination_path_or_url=''):
429 effa4b8f Stavros Sachtouris
        super(_source_destination, self)._run(source_path_or_url)
430 effa4b8f Stavros Sachtouris
        dst_acc, dst_con, dst_path = self._resolve_pithos_url(
431 effa4b8f Stavros Sachtouris
            destination_path_or_url)
432 effa4b8f Stavros Sachtouris
        self.dst_client = PithosClient(
433 effa4b8f Stavros Sachtouris
            base_url=self.client.base_url, token=self.client.token,
434 effa4b8f Stavros Sachtouris
            container=self[
435 effa4b8f Stavros Sachtouris
                'destination_container'] or dst_con or self.client.container,
436 effa4b8f Stavros Sachtouris
            account=self[
437 effa4b8f Stavros Sachtouris
                'destination_user_uuid'] or dst_acc or self.client.account)
438 effa4b8f Stavros Sachtouris
        self.dst_path = dst_path or self.path
439 effa4b8f Stavros Sachtouris
440 effa4b8f Stavros Sachtouris
441 effa4b8f Stavros Sachtouris
@command(file_cmds)
442 effa4b8f Stavros Sachtouris
class file_copy(_source_destination):
443 effa4b8f Stavros Sachtouris
    """Copy objects, even between different accounts or containers"""
444 effa4b8f Stavros Sachtouris
445 effa4b8f Stavros Sachtouris
    arguments = dict(
446 effa4b8f Stavros Sachtouris
        public=ValueArgument('publish new object', '--public'),
447 effa4b8f Stavros Sachtouris
        content_type=ValueArgument(
448 effa4b8f Stavros Sachtouris
            'change object\'s content type', '--content-type'),
449 effa4b8f Stavros Sachtouris
        source_version=ValueArgument(
450 effa4b8f Stavros Sachtouris
            'copy specific version', ('-S', '--source-version'))
451 effa4b8f Stavros Sachtouris
    )
452 effa4b8f Stavros Sachtouris
453 effa4b8f Stavros Sachtouris
    @errors.generic.all
454 effa4b8f Stavros Sachtouris
    @errors.pithos.connection
455 effa4b8f Stavros Sachtouris
    @errors.pithos.container
456 effa4b8f Stavros Sachtouris
    @errors.pithos.account
457 effa4b8f Stavros Sachtouris
    def _run(self):
458 effa4b8f Stavros Sachtouris
        for src, dst in self._src_dst(self['source_version']):
459 de932277 Stavros Sachtouris
            self._report_transfer(src, dst, 'copy')
460 de932277 Stavros Sachtouris
            if src and dst:
461 effa4b8f Stavros Sachtouris
                self.dst_client.copy_object(
462 effa4b8f Stavros Sachtouris
                    src_container=self.client.container,
463 effa4b8f Stavros Sachtouris
                    src_object=src,
464 effa4b8f Stavros Sachtouris
                    dst_container=self.dst_client.container,
465 effa4b8f Stavros Sachtouris
                    dst_object=dst,
466 effa4b8f Stavros Sachtouris
                    source_account=self.account,
467 effa4b8f Stavros Sachtouris
                    source_version=self['source_version'],
468 effa4b8f Stavros Sachtouris
                    public=self['public'],
469 effa4b8f Stavros Sachtouris
                    content_type=self['content_type'])
470 de932277 Stavros Sachtouris
            elif dst:
471 de932277 Stavros Sachtouris
                self.dst_client.create_directory(dst)
472 effa4b8f Stavros Sachtouris
473 effa4b8f Stavros Sachtouris
    def main(self, source_path_or_url, destination_path_or_url=None):
474 effa4b8f Stavros Sachtouris
        super(file_copy, self)._run(
475 effa4b8f Stavros Sachtouris
            source_path_or_url, destination_path_or_url or '')
476 effa4b8f Stavros Sachtouris
        self._run()
477 effa4b8f Stavros Sachtouris
478 effa4b8f Stavros Sachtouris
479 effa4b8f Stavros Sachtouris
@command(file_cmds)
480 effa4b8f Stavros Sachtouris
class file_move(_source_destination):
481 effa4b8f Stavros Sachtouris
    """Move objects, even between different accounts or containers"""
482 effa4b8f Stavros Sachtouris
483 effa4b8f Stavros Sachtouris
    arguments = dict(
484 effa4b8f Stavros Sachtouris
        public=ValueArgument('publish new object', '--public'),
485 effa4b8f Stavros Sachtouris
        content_type=ValueArgument(
486 effa4b8f Stavros Sachtouris
            'change object\'s content type', '--content-type')
487 effa4b8f Stavros Sachtouris
    )
488 effa4b8f Stavros Sachtouris
489 effa4b8f Stavros Sachtouris
    @errors.generic.all
490 effa4b8f Stavros Sachtouris
    @errors.pithos.connection
491 effa4b8f Stavros Sachtouris
    @errors.pithos.container
492 effa4b8f Stavros Sachtouris
    @errors.pithos.account
493 effa4b8f Stavros Sachtouris
    def _run(self):
494 effa4b8f Stavros Sachtouris
        for src, dst in self._src_dst():
495 de932277 Stavros Sachtouris
            self._report_transfer(src, dst, 'move')
496 de932277 Stavros Sachtouris
            if src and dst:
497 effa4b8f Stavros Sachtouris
                self.dst_client.move_object(
498 effa4b8f Stavros Sachtouris
                    src_container=self.client.container,
499 effa4b8f Stavros Sachtouris
                    src_object=src,
500 effa4b8f Stavros Sachtouris
                    dst_container=self.dst_client.container,
501 effa4b8f Stavros Sachtouris
                    dst_object=dst,
502 effa4b8f Stavros Sachtouris
                    source_account=self.account,
503 effa4b8f Stavros Sachtouris
                    public=self['public'],
504 effa4b8f Stavros Sachtouris
                    content_type=self['content_type'])
505 de932277 Stavros Sachtouris
            elif dst:
506 de932277 Stavros Sachtouris
                self.dst_client.create_directory(dst)
507 effa4b8f Stavros Sachtouris
            else:
508 de932277 Stavros Sachtouris
                self.client.del_object(src)
509 effa4b8f Stavros Sachtouris
510 effa4b8f Stavros Sachtouris
    def main(self, source_path_or_url, destination_path_or_url=None):
511 effa4b8f Stavros Sachtouris
        super(file_move, self)._run(
512 effa4b8f Stavros Sachtouris
            source_path_or_url, destination_path_or_url or '')
513 effa4b8f Stavros Sachtouris
        self._run()
514 edc1182f Stavros Sachtouris
515 edc1182f Stavros Sachtouris
516 edc1182f Stavros Sachtouris
@command(file_cmds)
517 edc1182f Stavros Sachtouris
class file_append(_pithos_container, _optional_output_cmd):
518 edc1182f Stavros Sachtouris
    """Append local file to (existing) remote object
519 edc1182f Stavros Sachtouris
    The remote object should exist.
520 edc1182f Stavros Sachtouris
    If the remote object is a directory, it is transformed into a file.
521 edc1182f Stavros Sachtouris
    In the later case, objects under the directory remain intact.
522 edc1182f Stavros Sachtouris
    """
523 edc1182f Stavros Sachtouris
524 edc1182f Stavros Sachtouris
    arguments = dict(
525 edc1182f Stavros Sachtouris
        progress_bar=ProgressBarArgument(
526 edc1182f Stavros Sachtouris
            'do not show progress bar', ('-N', '--no-progress-bar'),
527 edc1182f Stavros Sachtouris
            default=False),
528 edc1182f Stavros Sachtouris
        max_threads=IntArgument('default: 1', '--threads'),
529 edc1182f Stavros Sachtouris
    )
530 edc1182f Stavros Sachtouris
531 edc1182f Stavros Sachtouris
    @errors.generic.all
532 edc1182f Stavros Sachtouris
    @errors.pithos.connection
533 edc1182f Stavros Sachtouris
    @errors.pithos.container
534 edc1182f Stavros Sachtouris
    @errors.pithos.object_path
535 edc1182f Stavros Sachtouris
    def _run(self, local_path):
536 edc1182f Stavros Sachtouris
        if self['max_threads'] > 0:
537 edc1182f Stavros Sachtouris
            self.client.MAX_THREADS = int(self['max_threads'])
538 edc1182f Stavros Sachtouris
        (progress_bar, upload_cb) = self._safe_progress_bar('Appending')
539 edc1182f Stavros Sachtouris
        try:
540 edc1182f Stavros Sachtouris
            with open(local_path, 'rb') as f:
541 edc1182f Stavros Sachtouris
                self._optional_output(
542 edc1182f Stavros Sachtouris
                    self.client.append_object(self.path, f, upload_cb))
543 edc1182f Stavros Sachtouris
        finally:
544 edc1182f Stavros Sachtouris
            self._safe_progress_bar_finish(progress_bar)
545 edc1182f Stavros Sachtouris
546 edc1182f Stavros Sachtouris
    def main(self, local_path, remote_path_or_url):
547 edc1182f Stavros Sachtouris
        super(self.__class__, self)._run(remote_path_or_url)
548 edc1182f Stavros Sachtouris
        self._run(local_path)
549 edc1182f Stavros Sachtouris
550 edc1182f Stavros Sachtouris
551 edc1182f Stavros Sachtouris
@command(file_cmds)
552 edc1182f Stavros Sachtouris
class file_truncate(_pithos_container, _optional_output_cmd):
553 edc1182f Stavros Sachtouris
    """Truncate remote file up to size"""
554 edc1182f Stavros Sachtouris
555 edc1182f Stavros Sachtouris
    arguments = dict(
556 edc1182f Stavros Sachtouris
        size_in_bytes=IntArgument('Length of file after truncation', '--size')
557 edc1182f Stavros Sachtouris
    )
558 edc1182f Stavros Sachtouris
    required = ('size_in_bytes', )
559 edc1182f Stavros Sachtouris
560 edc1182f Stavros Sachtouris
    @errors.generic.all
561 edc1182f Stavros Sachtouris
    @errors.pithos.connection
562 edc1182f Stavros Sachtouris
    @errors.pithos.container
563 edc1182f Stavros Sachtouris
    @errors.pithos.object_path
564 edc1182f Stavros Sachtouris
    @errors.pithos.object_size
565 edc1182f Stavros Sachtouris
    def _run(self, size):
566 edc1182f Stavros Sachtouris
        self._optional_output(self.client.truncate_object(self.path, size))
567 edc1182f Stavros Sachtouris
568 edc1182f Stavros Sachtouris
    def main(self, path_or_url):
569 edc1182f Stavros Sachtouris
        super(self.__class__, self)._run(path_or_url)
570 edc1182f Stavros Sachtouris
        self._run(size=self['size_in_bytes'])
571 edc1182f Stavros Sachtouris
572 edc1182f Stavros Sachtouris
573 edc1182f Stavros Sachtouris
@command(file_cmds)
574 edc1182f Stavros Sachtouris
class file_overwrite(_pithos_container, _optional_output_cmd):
575 edc1182f Stavros Sachtouris
    """Overwrite part of a remote file"""
576 edc1182f Stavros Sachtouris
577 edc1182f Stavros Sachtouris
    arguments = dict(
578 edc1182f Stavros Sachtouris
        progress_bar=ProgressBarArgument(
579 edc1182f Stavros Sachtouris
            'do not show progress bar', ('-N', '--no-progress-bar'),
580 edc1182f Stavros Sachtouris
            default=False),
581 edc1182f Stavros Sachtouris
        start_position=IntArgument('File position in bytes', '--from'),
582 edc1182f Stavros Sachtouris
        end_position=IntArgument('File position in bytes', '--to')
583 edc1182f Stavros Sachtouris
    )
584 edc1182f Stavros Sachtouris
    required = ('start_position', 'end_position')
585 edc1182f Stavros Sachtouris
586 edc1182f Stavros Sachtouris
    @errors.generic.all
587 edc1182f Stavros Sachtouris
    @errors.pithos.connection
588 edc1182f Stavros Sachtouris
    @errors.pithos.container
589 edc1182f Stavros Sachtouris
    @errors.pithos.object_path
590 edc1182f Stavros Sachtouris
    @errors.pithos.object_size
591 edc1182f Stavros Sachtouris
    def _run(self, local_path, start, end):
592 edc1182f Stavros Sachtouris
        start, end = int(start), int(end)
593 edc1182f Stavros Sachtouris
        (progress_bar, upload_cb) = self._safe_progress_bar(
594 edc1182f Stavros Sachtouris
            'Overwrite %s bytes' % (end - start))
595 edc1182f Stavros Sachtouris
        try:
596 edc1182f Stavros Sachtouris
            with open(path.abspath(local_path), 'rb') as f:
597 edc1182f Stavros Sachtouris
                self._optional_output(self.client.overwrite_object(
598 edc1182f Stavros Sachtouris
                    obj=self.path,
599 edc1182f Stavros Sachtouris
                    start=start,
600 edc1182f Stavros Sachtouris
                    end=end,
601 edc1182f Stavros Sachtouris
                    source_file=f,
602 edc1182f Stavros Sachtouris
                    upload_cb=upload_cb))
603 edc1182f Stavros Sachtouris
        finally:
604 edc1182f Stavros Sachtouris
            self._safe_progress_bar_finish(progress_bar)
605 edc1182f Stavros Sachtouris
606 edc1182f Stavros Sachtouris
    def main(self, local_path, path_or_url):
607 edc1182f Stavros Sachtouris
        super(self.__class__, self)._run(path_or_url)
608 edc1182f Stavros Sachtouris
        self.path = self.path or path.basename(local_path)
609 edc1182f Stavros Sachtouris
        self._run(
610 edc1182f Stavros Sachtouris
            local_path=local_path,
611 edc1182f Stavros Sachtouris
            start=self['start_position'],
612 edc1182f Stavros Sachtouris
            end=self['end_position'])
613 edc1182f Stavros Sachtouris
614 edc1182f Stavros Sachtouris
615 edc1182f Stavros Sachtouris
@command(file_cmds)
616 edc1182f Stavros Sachtouris
class file_upload(_pithos_container, _optional_output_cmd):
617 edc1182f Stavros Sachtouris
    """Upload a file"""
618 edc1182f Stavros Sachtouris
619 edc1182f Stavros Sachtouris
    arguments = dict(
620 edc1182f Stavros Sachtouris
        max_threads=IntArgument('default: 5', '--threads'),
621 edc1182f Stavros Sachtouris
        content_encoding=ValueArgument(
622 edc1182f Stavros Sachtouris
            'set MIME content type', '--content-encoding'),
623 edc1182f Stavros Sachtouris
        content_disposition=ValueArgument(
624 edc1182f Stavros Sachtouris
            'specify objects presentation style', '--content-disposition'),
625 edc1182f Stavros Sachtouris
        content_type=ValueArgument('specify content type', '--content-type'),
626 edc1182f Stavros Sachtouris
        uuid_for_read_permission=RepeatableArgument(
627 edc1182f Stavros Sachtouris
            'Give read access to a user of group (can be repeated)',
628 edc1182f Stavros Sachtouris
            '--read-permission'),
629 edc1182f Stavros Sachtouris
        uuid_for_write_permission=RepeatableArgument(
630 edc1182f Stavros Sachtouris
            'Give write access to a user of group (can be repeated)',
631 edc1182f Stavros Sachtouris
            '--write-permission'),
632 edc1182f Stavros Sachtouris
        public=FlagArgument('make object publicly accessible', '--public'),
633 edc1182f Stavros Sachtouris
        overwrite=FlagArgument('Force (over)write', ('-f', '--force')),
634 edc1182f Stavros Sachtouris
        recursive=FlagArgument(
635 edc1182f Stavros Sachtouris
            'Recursively upload directory *contents* + subdirectories',
636 edc1182f Stavros Sachtouris
            ('-r', '--recursive')),
637 edc1182f Stavros Sachtouris
        unchunked=FlagArgument(
638 edc1182f Stavros Sachtouris
            'Upload file as one block (not recommended)', '--unchunked'),
639 edc1182f Stavros Sachtouris
        md5_checksum=ValueArgument(
640 edc1182f Stavros Sachtouris
            'Confirm upload with a custom checksum (MD5)', '--etag'),
641 edc1182f Stavros Sachtouris
        use_hashes=FlagArgument(
642 edc1182f Stavros Sachtouris
            'Source file contains hashmap not data', '--source-is-hashmap'),
643 edc1182f Stavros Sachtouris
    )
644 edc1182f Stavros Sachtouris
645 edc1182f Stavros Sachtouris
    def _sharing(self):
646 edc1182f Stavros Sachtouris
        sharing = dict()
647 edc1182f Stavros Sachtouris
        readlist = self['uuid_for_read_permission']
648 edc1182f Stavros Sachtouris
        if readlist:
649 edc1182f Stavros Sachtouris
            sharing['read'] = self['uuid_for_read_permission']
650 edc1182f Stavros Sachtouris
        writelist = self['uuid_for_write_permission']
651 edc1182f Stavros Sachtouris
        if writelist:
652 edc1182f Stavros Sachtouris
            sharing['write'] = self['uuid_for_write_permission']
653 edc1182f Stavros Sachtouris
        return sharing or None
654 edc1182f Stavros Sachtouris
655 edc1182f Stavros Sachtouris
    def _check_container_limit(self, path):
656 edc1182f Stavros Sachtouris
        cl_dict = self.client.get_container_limit()
657 edc1182f Stavros Sachtouris
        container_limit = int(cl_dict['x-container-policy-quota'])
658 edc1182f Stavros Sachtouris
        r = self.client.container_get()
659 edc1182f Stavros Sachtouris
        used_bytes = sum(int(o['bytes']) for o in r.json)
660 edc1182f Stavros Sachtouris
        path_size = get_path_size(path)
661 edc1182f Stavros Sachtouris
        if container_limit and path_size > (container_limit - used_bytes):
662 edc1182f Stavros Sachtouris
            raise CLIError(
663 edc1182f Stavros Sachtouris
                'Container %s (limit(%s) - used(%s)) < (size(%s) of %s)' % (
664 edc1182f Stavros Sachtouris
                    self.client.container,
665 edc1182f Stavros Sachtouris
                    format_size(container_limit),
666 edc1182f Stavros Sachtouris
                    format_size(used_bytes),
667 edc1182f Stavros Sachtouris
                    format_size(path_size),
668 edc1182f Stavros Sachtouris
                    path),
669 edc1182f Stavros Sachtouris
                details=[
670 edc1182f Stavros Sachtouris
                    'Check accound limit: /file quota',
671 edc1182f Stavros Sachtouris
                    'Check container limit:',
672 edc1182f Stavros Sachtouris
                    '\t/file containerlimit get %s' % self.client.container,
673 edc1182f Stavros Sachtouris
                    'Increase container limit:',
674 edc1182f Stavros Sachtouris
                    '\t/file containerlimit set <new limit> %s' % (
675 edc1182f Stavros Sachtouris
                        self.client.container)])
676 edc1182f Stavros Sachtouris
677 edc1182f Stavros Sachtouris
    def _src_dst(self, local_path, remote_path, objlist=None):
678 edc1182f Stavros Sachtouris
        lpath = path.abspath(local_path)
679 edc1182f Stavros Sachtouris
        short_path = path.basename(path.abspath(local_path))
680 edc1182f Stavros Sachtouris
        rpath = remote_path or short_path
681 edc1182f Stavros Sachtouris
        if path.isdir(lpath):
682 edc1182f Stavros Sachtouris
            if not self['recursive']:
683 edc1182f Stavros Sachtouris
                raise CLIError('%s is a directory' % lpath, details=[
684 edc1182f Stavros Sachtouris
                    'Use %s to upload directories & contents' % '/'.join(
685 edc1182f Stavros Sachtouris
                        self.arguments['recursive'].parsed_name)])
686 edc1182f Stavros Sachtouris
            robj = self.client.container_get(path=rpath)
687 edc1182f Stavros Sachtouris
            if not self['overwrite']:
688 edc1182f Stavros Sachtouris
                if robj.json:
689 edc1182f Stavros Sachtouris
                    raise CLIError(
690 edc1182f Stavros Sachtouris
                        'Objects/files prefixed as %s already exist' % rpath,
691 edc1182f Stavros Sachtouris
                        details=['Existing objects:'] + ['\t%s:\t%s' % (
692 edc1182f Stavros Sachtouris
                            o['name'],
693 edc1182f Stavros Sachtouris
                            o['content_type'][12:]) for o in robj.json] + [
694 edc1182f Stavros Sachtouris
                            'Use -f to add, overwrite or resume'])
695 edc1182f Stavros Sachtouris
                else:
696 edc1182f Stavros Sachtouris
                    try:
697 edc1182f Stavros Sachtouris
                        topobj = self.client.get_object_info(rpath)
698 edc1182f Stavros Sachtouris
                        if not self._is_dir(topobj):
699 edc1182f Stavros Sachtouris
                            raise CLIError(
700 edc1182f Stavros Sachtouris
                                'Object /%s/%s exists but not a directory' % (
701 edc1182f Stavros Sachtouris
                                    self.container, rpath),
702 edc1182f Stavros Sachtouris
                                details=['Use -f to overwrite'])
703 edc1182f Stavros Sachtouris
                    except ClientError as ce:
704 edc1182f Stavros Sachtouris
                        if ce.status not in (404, ):
705 edc1182f Stavros Sachtouris
                            raise
706 edc1182f Stavros Sachtouris
            self._check_container_limit(lpath)
707 edc1182f Stavros Sachtouris
            prev = ''
708 edc1182f Stavros Sachtouris
            for top, subdirs, files in walk(lpath):
709 edc1182f Stavros Sachtouris
                if top != prev:
710 edc1182f Stavros Sachtouris
                    prev = top
711 edc1182f Stavros Sachtouris
                    try:
712 edc1182f Stavros Sachtouris
                        rel_path = rpath + top.split(lpath)[1]
713 edc1182f Stavros Sachtouris
                    except IndexError:
714 edc1182f Stavros Sachtouris
                        rel_path = rpath
715 edc1182f Stavros Sachtouris
                    self.error('mkdir %s:%s' % (
716 edc1182f Stavros Sachtouris
                        self.client.container, rel_path))
717 edc1182f Stavros Sachtouris
                    self.client.create_directory(rel_path)
718 edc1182f Stavros Sachtouris
                for f in files:
719 edc1182f Stavros Sachtouris
                    fpath = path.join(top, f)
720 edc1182f Stavros Sachtouris
                    if path.isfile(fpath):
721 edc1182f Stavros Sachtouris
                        rel_path = rel_path.replace(path.sep, '/')
722 edc1182f Stavros Sachtouris
                        pathfix = f.replace(path.sep, '/')
723 edc1182f Stavros Sachtouris
                        yield open(fpath, 'rb'), '%s/%s' % (rel_path, pathfix)
724 edc1182f Stavros Sachtouris
                    else:
725 edc1182f Stavros Sachtouris
                        self.error('%s is not a regular file' % fpath)
726 edc1182f Stavros Sachtouris
        else:
727 edc1182f Stavros Sachtouris
            if not path.isfile(lpath):
728 edc1182f Stavros Sachtouris
                raise CLIError(('%s is not a regular file' % lpath) if (
729 edc1182f Stavros Sachtouris
                    path.exists(lpath)) else '%s does not exist' % lpath)
730 edc1182f Stavros Sachtouris
            try:
731 edc1182f Stavros Sachtouris
                robj = self.client.get_object_info(rpath)
732 edc1182f Stavros Sachtouris
                if remote_path and self._is_dir(robj):
733 edc1182f Stavros Sachtouris
                    rpath += '/%s' % (short_path.replace(path.sep, '/'))
734 edc1182f Stavros Sachtouris
                    self.client.get_object_info(rpath)
735 edc1182f Stavros Sachtouris
                if not self['overwrite']:
736 edc1182f Stavros Sachtouris
                    raise CLIError(
737 edc1182f Stavros Sachtouris
                        'Object /%s/%s already exists' % (
738 edc1182f Stavros Sachtouris
                            self.container, rpath),
739 edc1182f Stavros Sachtouris
                        details=['use -f to overwrite / resume'])
740 edc1182f Stavros Sachtouris
            except ClientError as ce:
741 edc1182f Stavros Sachtouris
                if ce.status not in (404, ):
742 edc1182f Stavros Sachtouris
                    raise
743 edc1182f Stavros Sachtouris
            self._check_container_limit(lpath)
744 edc1182f Stavros Sachtouris
            yield open(lpath, 'rb'), rpath
745 edc1182f Stavros Sachtouris
746 edc1182f Stavros Sachtouris
    def _run(self, local_path, remote_path):
747 edc1182f Stavros Sachtouris
        if self['max_threads'] > 0:
748 edc1182f Stavros Sachtouris
            self.client.MAX_THREADS = int(self['max_threads'])
749 edc1182f Stavros Sachtouris
        params = dict(
750 edc1182f Stavros Sachtouris
            content_encoding=self['content_encoding'],
751 edc1182f Stavros Sachtouris
            content_type=self['content_type'],
752 edc1182f Stavros Sachtouris
            content_disposition=self['content_disposition'],
753 edc1182f Stavros Sachtouris
            sharing=self._sharing(),
754 edc1182f Stavros Sachtouris
            public=self['public'])
755 edc1182f Stavros Sachtouris
        uploaded, container_info_cache = list, dict()
756 edc1182f Stavros Sachtouris
        rpref = 'pithos://%s' if self['account'] else ''
757 edc1182f Stavros Sachtouris
        for f, rpath in self._src_dst(local_path, remote_path):
758 edc1182f Stavros Sachtouris
            self.error('%s --> %s/%s/%s' % (
759 edc1182f Stavros Sachtouris
                f.name, rpref, self.client.container, rpath))
760 edc1182f Stavros Sachtouris
            if not (self['content_type'] and self['content_encoding']):
761 edc1182f Stavros Sachtouris
                ctype, cenc = guess_mime_type(f.name)
762 edc1182f Stavros Sachtouris
                params['content_type'] = self['content_type'] or ctype
763 edc1182f Stavros Sachtouris
                params['content_encoding'] = self['content_encoding'] or cenc
764 edc1182f Stavros Sachtouris
            if self['unchunked']:
765 edc1182f Stavros Sachtouris
                r = self.client.upload_object_unchunked(
766 edc1182f Stavros Sachtouris
                    rpath, f,
767 edc1182f Stavros Sachtouris
                    etag=self['md5_checksum'], withHashFile=self['use_hashes'],
768 edc1182f Stavros Sachtouris
                    **params)
769 edc1182f Stavros Sachtouris
                if self['with_output'] or self['json_output']:
770 edc1182f Stavros Sachtouris
                    r['name'] = '/%s/%s' % (self.client.container, rpath)
771 edc1182f Stavros Sachtouris
                    uploaded.append(r)
772 edc1182f Stavros Sachtouris
            else:
773 edc1182f Stavros Sachtouris
                try:
774 edc1182f Stavros Sachtouris
                    (progress_bar, upload_cb) = self._safe_progress_bar(
775 edc1182f Stavros Sachtouris
                        'Uploading %s' % f.name.split(path.sep)[-1])
776 edc1182f Stavros Sachtouris
                    if progress_bar:
777 edc1182f Stavros Sachtouris
                        hash_bar = progress_bar.clone()
778 edc1182f Stavros Sachtouris
                        hash_cb = hash_bar.get_generator(
779 edc1182f Stavros Sachtouris
                            'Calculating block hashes')
780 edc1182f Stavros Sachtouris
                    else:
781 edc1182f Stavros Sachtouris
                        hash_cb = None
782 edc1182f Stavros Sachtouris
                    r = self.client.upload_object(
783 edc1182f Stavros Sachtouris
                        rpath, f,
784 edc1182f Stavros Sachtouris
                        hash_cb=hash_cb,
785 edc1182f Stavros Sachtouris
                        upload_cb=upload_cb,
786 edc1182f Stavros Sachtouris
                        container_info_cache=container_info_cache,
787 edc1182f Stavros Sachtouris
                        **params)
788 edc1182f Stavros Sachtouris
                    if self['with_output'] or self['json_output']:
789 edc1182f Stavros Sachtouris
                        r['name'] = '/%s/%s' % (self.client.container, rpath)
790 edc1182f Stavros Sachtouris
                        uploaded.append(r)
791 edc1182f Stavros Sachtouris
                except Exception:
792 edc1182f Stavros Sachtouris
                    self._safe_progress_bar_finish(progress_bar)
793 edc1182f Stavros Sachtouris
                    raise
794 edc1182f Stavros Sachtouris
                finally:
795 edc1182f Stavros Sachtouris
                    self._safe_progress_bar_finish(progress_bar)
796 edc1182f Stavros Sachtouris
        self._optional_output(uploaded)
797 edc1182f Stavros Sachtouris
        self.error('Upload completed')
798 edc1182f Stavros Sachtouris
799 edc1182f Stavros Sachtouris
    def main(self, local_path, remote_path_or_url):
800 edc1182f Stavros Sachtouris
        super(self.__class__, self)._run(remote_path_or_url)
801 edc1182f Stavros Sachtouris
        remote_path = self.path or path.basename(path.abspath(local_path))
802 edc1182f Stavros Sachtouris
        self._run(local_path=local_path, remote_path=remote_path)
803 edc1182f Stavros Sachtouris
804 edc1182f Stavros Sachtouris
805 edc1182f Stavros Sachtouris
class RangeArgument(ValueArgument):
806 edc1182f Stavros Sachtouris
    """
807 edc1182f Stavros Sachtouris
    :value type: string of the form <start>-<end> where <start> and <end> are
808 edc1182f Stavros Sachtouris
        integers
809 edc1182f Stavros Sachtouris
    :value returns: the input string, after type checking <start> and <end>
810 edc1182f Stavros Sachtouris
    """
811 edc1182f Stavros Sachtouris
812 edc1182f Stavros Sachtouris
    @property
813 edc1182f Stavros Sachtouris
    def value(self):
814 edc1182f Stavros Sachtouris
        return getattr(self, '_value', self.default)
815 edc1182f Stavros Sachtouris
816 edc1182f Stavros Sachtouris
    @value.setter
817 edc1182f Stavros Sachtouris
    def value(self, newvalues):
818 edc1182f Stavros Sachtouris
        if newvalues:
819 edc1182f Stavros Sachtouris
            self._value = getattr(self, '_value', self.default)
820 edc1182f Stavros Sachtouris
            for newvalue in newvalues.split(','):
821 edc1182f Stavros Sachtouris
                self._value = ('%s,' % self._value) if self._value else ''
822 edc1182f Stavros Sachtouris
                start, sep, end = newvalue.partition('-')
823 edc1182f Stavros Sachtouris
                if sep:
824 edc1182f Stavros Sachtouris
                    if start:
825 edc1182f Stavros Sachtouris
                        start, end = (int(start), int(end))
826 edc1182f Stavros Sachtouris
                        if start > end:
827 edc1182f Stavros Sachtouris
                            raise CLIInvalidArgument(
828 edc1182f Stavros Sachtouris
                                'Invalid range %s' % newvalue, details=[
829 edc1182f Stavros Sachtouris
                                'Valid range formats',
830 edc1182f Stavros Sachtouris
                                '  START-END', '  UP_TO', '  -FROM',
831 edc1182f Stavros Sachtouris
                                'where all values are integers'])
832 edc1182f Stavros Sachtouris
                        self._value += '%s-%s' % (start, end)
833 edc1182f Stavros Sachtouris
                    else:
834 edc1182f Stavros Sachtouris
                        self._value += '-%s' % int(end)
835 edc1182f Stavros Sachtouris
                else:
836 edc1182f Stavros Sachtouris
                    self._value += '%s' % int(start)
837 edc1182f Stavros Sachtouris
838 edc1182f Stavros Sachtouris
839 edc1182f Stavros Sachtouris
@command(file_cmds)
840 edc1182f Stavros Sachtouris
class file_cat(_pithos_container):
841 edc1182f Stavros Sachtouris
    """Fetch remote file contents"""
842 edc1182f Stavros Sachtouris
843 edc1182f Stavros Sachtouris
    arguments = dict(
844 edc1182f Stavros Sachtouris
        range=RangeArgument('show range of data', '--range'),
845 edc1182f Stavros Sachtouris
        if_match=ValueArgument('show output if ETags match', '--if-match'),
846 edc1182f Stavros Sachtouris
        if_none_match=ValueArgument(
847 edc1182f Stavros Sachtouris
            'show output if ETags match', '--if-none-match'),
848 edc1182f Stavros Sachtouris
        if_modified_since=DateArgument(
849 edc1182f Stavros Sachtouris
            'show output modified since then', '--if-modified-since'),
850 edc1182f Stavros Sachtouris
        if_unmodified_since=DateArgument(
851 edc1182f Stavros Sachtouris
            'show output unmodified since then', '--if-unmodified-since'),
852 edc1182f Stavros Sachtouris
        object_version=ValueArgument(
853 edc1182f Stavros Sachtouris
            'Get contents of the chosen version', '--object-version')
854 edc1182f Stavros Sachtouris
    )
855 edc1182f Stavros Sachtouris
856 edc1182f Stavros Sachtouris
    @errors.generic.all
857 edc1182f Stavros Sachtouris
    @errors.pithos.connection
858 edc1182f Stavros Sachtouris
    @errors.pithos.container
859 edc1182f Stavros Sachtouris
    @errors.pithos.object_path
860 edc1182f Stavros Sachtouris
    def _run(self):
861 edc1182f Stavros Sachtouris
        self.client.download_object(
862 edc1182f Stavros Sachtouris
            self.path, self._out,
863 edc1182f Stavros Sachtouris
            range_str=self['range'],
864 edc1182f Stavros Sachtouris
            version=self['object_version'],
865 edc1182f Stavros Sachtouris
            if_match=self['if_match'],
866 edc1182f Stavros Sachtouris
            if_none_match=self['if_none_match'],
867 edc1182f Stavros Sachtouris
            if_modified_since=self['if_modified_since'],
868 edc1182f Stavros Sachtouris
            if_unmodified_since=self['if_unmodified_since'])
869 edc1182f Stavros Sachtouris
870 edc1182f Stavros Sachtouris
    def main(self, path_or_url):
871 edc1182f Stavros Sachtouris
        super(self.__class__, self)._run(path_or_url)
872 edc1182f Stavros Sachtouris
        self._run()
873 74b7c6dc Stavros Sachtouris
874 74b7c6dc Stavros Sachtouris
875 74b7c6dc Stavros Sachtouris
@command(file_cmds)
876 74b7c6dc Stavros Sachtouris
class file_download(_pithos_container):
877 74b7c6dc Stavros Sachtouris
    """Download a remove file or directory object to local file system"""
878 74b7c6dc Stavros Sachtouris
879 74b7c6dc Stavros Sachtouris
    arguments = dict(
880 74b7c6dc Stavros Sachtouris
        resume=FlagArgument(
881 74b7c6dc Stavros Sachtouris
            'Resume/Overwrite (attempt resume, else overwrite)',
882 74b7c6dc Stavros Sachtouris
            ('-f', '--resume')),
883 74b7c6dc Stavros Sachtouris
        range=RangeArgument('Download only that range of data', '--range'),
884 74b7c6dc Stavros Sachtouris
        matching_etag=ValueArgument('download iff ETag match', '--if-match'),
885 74b7c6dc Stavros Sachtouris
        non_matching_etag=ValueArgument(
886 74b7c6dc Stavros Sachtouris
            'download iff ETags DO NOT match', '--if-none-match'),
887 74b7c6dc Stavros Sachtouris
        modified_since_date=DateArgument(
888 74b7c6dc Stavros Sachtouris
            'download iff remote file is modified since then',
889 74b7c6dc Stavros Sachtouris
            '--if-modified-since'),
890 74b7c6dc Stavros Sachtouris
        unmodified_since_date=DateArgument(
891 74b7c6dc Stavros Sachtouris
            'show output iff remote file is unmodified since then',
892 74b7c6dc Stavros Sachtouris
            '--if-unmodified-since'),
893 74b7c6dc Stavros Sachtouris
        object_version=ValueArgument(
894 74b7c6dc Stavros Sachtouris
            'download a file of a specific version',
895 74b7c6dc Stavros Sachtouris
            ('-O', '--object-version')),
896 74b7c6dc Stavros Sachtouris
        max_threads=IntArgument('default: 5', '--threads'),
897 74b7c6dc Stavros Sachtouris
        progress_bar=ProgressBarArgument(
898 74b7c6dc Stavros Sachtouris
            'do not show progress bar', ('-N', '--no-progress-bar'),
899 74b7c6dc Stavros Sachtouris
            default=False),
900 74b7c6dc Stavros Sachtouris
        recursive=FlagArgument(
901 74b7c6dc Stavros Sachtouris
            'Download a remote directory object and its contents',
902 74b7c6dc Stavros Sachtouris
            ('-r', '--recursive'))
903 74b7c6dc Stavros Sachtouris
        )
904 74b7c6dc Stavros Sachtouris
905 74b7c6dc Stavros Sachtouris
    def _src_dst(self, local_path):
906 74b7c6dc Stavros Sachtouris
        """Create a list of (src, dst) where src is a remote location and dst
907 74b7c6dc Stavros Sachtouris
        is an open file descriptor. Directories are denoted as (None, dirpath)
908 74b7c6dc Stavros Sachtouris
        and they are pretended to other objects in a very strict order (shorter
909 74b7c6dc Stavros Sachtouris
        to longer path)."""
910 74b7c6dc Stavros Sachtouris
        ret = []
911 74b7c6dc Stavros Sachtouris
        try:
912 74b7c6dc Stavros Sachtouris
            obj = self.client.get_object_info(
913 74b7c6dc Stavros Sachtouris
                self.path, version=self['object_version'])
914 74b7c6dc Stavros Sachtouris
            obj.setdefault('name', self.path.strip('/'))
915 74b7c6dc Stavros Sachtouris
        except ClientError as ce:
916 74b7c6dc Stavros Sachtouris
            if ce.status in (404, ):
917 74b7c6dc Stavros Sachtouris
                raiseCLIError(ce, details=[
918 74b7c6dc Stavros Sachtouris
                    'To download an object, the object must exist either as a'
919 74b7c6dc Stavros Sachtouris
                    ' file or as a directory.',
920 74b7c6dc Stavros Sachtouris
                    'For example, to download everything under prefix/ the '
921 74b7c6dc Stavros Sachtouris
                    'directory "prefix" must exist.',
922 74b7c6dc Stavros Sachtouris
                    'To see if an remote object is actually there:',
923 74b7c6dc Stavros Sachtouris
                    '  /file info [/CONTAINER/]OBJECT',
924 74b7c6dc Stavros Sachtouris
                    'To create a directory object:',
925 74b7c6dc Stavros Sachtouris
                    '  /file mkdir [/CONTAINER/]OBJECT'])
926 74b7c6dc Stavros Sachtouris
            if ce.status in (204, ):
927 74b7c6dc Stavros Sachtouris
                raise CLIError(
928 74b7c6dc Stavros Sachtouris
                    'No file or directory objects to download',
929 74b7c6dc Stavros Sachtouris
                    details=[
930 74b7c6dc Stavros Sachtouris
                        'To download a container (e.g., %s):' % self.container,
931 74b7c6dc Stavros Sachtouris
                        '  [kamaki] container download %s [LOCAL_PATH]' % (
932 74b7c6dc Stavros Sachtouris
                            self.container)])
933 74b7c6dc Stavros Sachtouris
            raise
934 74b7c6dc Stavros Sachtouris
        rpath = self.path.strip('/')
935 74b7c6dc Stavros Sachtouris
        local_path = local_path[-1:] if (
936 74b7c6dc Stavros Sachtouris
            local_path.endswith('/')) else local_path
937 74b7c6dc Stavros Sachtouris
938 74b7c6dc Stavros Sachtouris
        if self._is_dir(obj):
939 74b7c6dc Stavros Sachtouris
            if self['recursive']:
940 74b7c6dc Stavros Sachtouris
                dirs, files = [obj, ], []
941 74b7c6dc Stavros Sachtouris
                objects = self.client.container_get(
942 74b7c6dc Stavros Sachtouris
                    path=self.path or '/',
943 74b7c6dc Stavros Sachtouris
                    if_modified_since=self['modified_since_date'],
944 74b7c6dc Stavros Sachtouris
                    if_unmodified_since=self['unmodified_since_date'])
945 74b7c6dc Stavros Sachtouris
                for obj in objects.json:
946 74b7c6dc Stavros Sachtouris
                    (dirs if self._is_dir(obj) else files).append(obj)
947 74b7c6dc Stavros Sachtouris
948 74b7c6dc Stavros Sachtouris
                #  Put the directories on top of the list
949 74b7c6dc Stavros Sachtouris
                for dpath in sorted(['%s%s' % (
950 74b7c6dc Stavros Sachtouris
                        local_path, d['name'][len(rpath):]) for d in dirs]):
951 74b7c6dc Stavros Sachtouris
                    if path.exists(dpath):
952 74b7c6dc Stavros Sachtouris
                        if path.isdir(dpath):
953 74b7c6dc Stavros Sachtouris
                            continue
954 74b7c6dc Stavros Sachtouris
                        raise CLIError(
955 74b7c6dc Stavros Sachtouris
                            'Cannot replace local file %s with a directory '
956 74b7c6dc Stavros Sachtouris
                            'of the same name' % dpath,
957 74b7c6dc Stavros Sachtouris
                            details=[
958 74b7c6dc Stavros Sachtouris
                                'Either remove the file or specify a'
959 74b7c6dc Stavros Sachtouris
                                'different target location'])
960 74b7c6dc Stavros Sachtouris
                    ret.append((None, dpath, None))
961 74b7c6dc Stavros Sachtouris
962 74b7c6dc Stavros Sachtouris
                #  Append the file objects
963 74b7c6dc Stavros Sachtouris
                for opath in [o['name'] for o in files]:
964 74b7c6dc Stavros Sachtouris
                    lpath = '%s%s' % (local_path, opath[len(rpath):])
965 74b7c6dc Stavros Sachtouris
                    if self['resume']:
966 74b7c6dc Stavros Sachtouris
                        fxists = path.exists(lpath)
967 74b7c6dc Stavros Sachtouris
                        if fxists and path.isdir(lpath):
968 74b7c6dc Stavros Sachtouris
                            raise CLIError(
969 74b7c6dc Stavros Sachtouris
                                'Cannot change local dir %s info file' % (
970 74b7c6dc Stavros Sachtouris
                                    lpath),
971 74b7c6dc Stavros Sachtouris
                                details=[
972 74b7c6dc Stavros Sachtouris
                                    'Either remove the file or specify a'
973 74b7c6dc Stavros Sachtouris
                                    'different target location'])
974 74b7c6dc Stavros Sachtouris
                        ret.append((opath, lpath, fxists))
975 74b7c6dc Stavros Sachtouris
                    elif path.exists(lpath):
976 74b7c6dc Stavros Sachtouris
                        raise CLIError(
977 74b7c6dc Stavros Sachtouris
                            'Cannot overwrite %s' % lpath,
978 74b7c6dc Stavros Sachtouris
                            details=['To overwrite/resume, use  %s' % '/'.join(
979 74b7c6dc Stavros Sachtouris
                                self.arguments['resume'].parsed_name)])
980 74b7c6dc Stavros Sachtouris
                    else:
981 74b7c6dc Stavros Sachtouris
                        ret.append((opath, lpath, None))
982 74b7c6dc Stavros Sachtouris
            else:
983 74b7c6dc Stavros Sachtouris
                raise CLIError(
984 74b7c6dc Stavros Sachtouris
                    'Remote object /%s/%s is a directory' % (
985 74b7c6dc Stavros Sachtouris
                        self.container, local_path),
986 74b7c6dc Stavros Sachtouris
                    details=['Use %s to download directories' % '/'.join(
987 74b7c6dc Stavros Sachtouris
                        self.arguments['recursive'].parsed_name)])
988 74b7c6dc Stavros Sachtouris
        else:
989 74b7c6dc Stavros Sachtouris
            #  Remote object is just a file
990 74b7c6dc Stavros Sachtouris
            if path.exists(local_path) and not self['resume']:
991 74b7c6dc Stavros Sachtouris
                raise CLIError(
992 74b7c6dc Stavros Sachtouris
                    'Cannot overwrite local file %s' % (lpath),
993 74b7c6dc Stavros Sachtouris
                    details=['To overwrite/resume, use  %s' % '/'.join(
994 74b7c6dc Stavros Sachtouris
                        self.arguments['resume'].parsed_name)])
995 74b7c6dc Stavros Sachtouris
            ret.append((rpath, local_path, self['resume']))
996 74b7c6dc Stavros Sachtouris
        for r, l, resume in ret:
997 74b7c6dc Stavros Sachtouris
            if r:
998 74b7c6dc Stavros Sachtouris
                with open(l, 'rwb+' if resume else 'wb+') as f:
999 74b7c6dc Stavros Sachtouris
                    yield (r, f)
1000 74b7c6dc Stavros Sachtouris
            else:
1001 74b7c6dc Stavros Sachtouris
                yield (r, l)
1002 74b7c6dc Stavros Sachtouris
1003 74b7c6dc Stavros Sachtouris
    @errors.generic.all
1004 74b7c6dc Stavros Sachtouris
    @errors.pithos.connection
1005 74b7c6dc Stavros Sachtouris
    @errors.pithos.container
1006 74b7c6dc Stavros Sachtouris
    @errors.pithos.object_path
1007 74b7c6dc Stavros Sachtouris
    @errors.pithos.local_path
1008 74b7c6dc Stavros Sachtouris
    def _run(self, local_path):
1009 74b7c6dc Stavros Sachtouris
        self.client.MAX_THREADS = self['max_threads'] or 5
1010 74b7c6dc Stavros Sachtouris
        progress_bar = None
1011 74b7c6dc Stavros Sachtouris
        try:
1012 74b7c6dc Stavros Sachtouris
            for rpath, output_file in self._src_dst(local_path):
1013 74b7c6dc Stavros Sachtouris
                if not rpath:
1014 74b7c6dc Stavros Sachtouris
                    self.error('Create local directory %s' % output_file)
1015 74b7c6dc Stavros Sachtouris
                    makedirs(output_file)
1016 74b7c6dc Stavros Sachtouris
                    continue
1017 74b7c6dc Stavros Sachtouris
                self.error('/%s/%s --> %s' % (
1018 74b7c6dc Stavros Sachtouris
                    self.container, rpath, output_file.name))
1019 74b7c6dc Stavros Sachtouris
                progress_bar, download_cb = self._safe_progress_bar(
1020 74b7c6dc Stavros Sachtouris
                    '  download')
1021 74b7c6dc Stavros Sachtouris
                self.client.download_object(
1022 74b7c6dc Stavros Sachtouris
                    rpath, output_file,
1023 74b7c6dc Stavros Sachtouris
                    download_cb=download_cb,
1024 74b7c6dc Stavros Sachtouris
                    range_str=self['range'],
1025 74b7c6dc Stavros Sachtouris
                    version=self['object_version'],
1026 74b7c6dc Stavros Sachtouris
                    if_match=self['matching_etag'],
1027 74b7c6dc Stavros Sachtouris
                    resume=self['resume'],
1028 74b7c6dc Stavros Sachtouris
                    if_none_match=self['non_matching_etag'],
1029 74b7c6dc Stavros Sachtouris
                    if_modified_since=self['modified_since_date'],
1030 74b7c6dc Stavros Sachtouris
                    if_unmodified_since=self['unmodified_since_date'])
1031 74b7c6dc Stavros Sachtouris
        except KeyboardInterrupt:
1032 74b7c6dc Stavros Sachtouris
            from threading import activeCount, enumerate as activethreads
1033 74b7c6dc Stavros Sachtouris
            timeout = 0.5
1034 74b7c6dc Stavros Sachtouris
            while activeCount() > 1:
1035 74b7c6dc Stavros Sachtouris
                self._out.write('\nCancel %s threads: ' % (activeCount() - 1))
1036 74b7c6dc Stavros Sachtouris
                self._out.flush()
1037 74b7c6dc Stavros Sachtouris
                for thread in activethreads():
1038 74b7c6dc Stavros Sachtouris
                    try:
1039 74b7c6dc Stavros Sachtouris
                        thread.join(timeout)
1040 74b7c6dc Stavros Sachtouris
                        self._out.write('.' if thread.isAlive() else '*')
1041 74b7c6dc Stavros Sachtouris
                    except RuntimeError:
1042 74b7c6dc Stavros Sachtouris
                        continue
1043 74b7c6dc Stavros Sachtouris
                    finally:
1044 74b7c6dc Stavros Sachtouris
                        self._out.flush()
1045 74b7c6dc Stavros Sachtouris
                        timeout += 0.1
1046 74b7c6dc Stavros Sachtouris
            self.error('\nDownload canceled by user')
1047 74b7c6dc Stavros Sachtouris
            if local_path is not None:
1048 74b7c6dc Stavros Sachtouris
                self.error('to resume, re-run with --resume')
1049 74b7c6dc Stavros Sachtouris
        except Exception:
1050 74b7c6dc Stavros Sachtouris
            self._safe_progress_bar_finish(progress_bar)
1051 74b7c6dc Stavros Sachtouris
            raise
1052 74b7c6dc Stavros Sachtouris
        finally:
1053 74b7c6dc Stavros Sachtouris
            self._safe_progress_bar_finish(progress_bar)
1054 74b7c6dc Stavros Sachtouris
1055 74b7c6dc Stavros Sachtouris
    def main(self, remote_path_or_url, local_path=None):
1056 74b7c6dc Stavros Sachtouris
        super(self.__class__, self)._run(remote_path_or_url)
1057 74b7c6dc Stavros Sachtouris
        local_path = local_path or self.path or '.'
1058 74b7c6dc Stavros Sachtouris
        self._run(local_path=local_path)