Statistics
| Branch: | Tag: | Revision:

root / kamaki / cli / commands / pithos.py @ f59635ab

History | View | Annotate | Download (69.2 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 1395c40e Stavros Sachtouris
from time import localtime, strftime
35 fa9c0c38 Stavros Sachtouris
from io import StringIO
36 76f58e2e Stavros Sachtouris
from pydoc import pager
37 edc1182f Stavros Sachtouris
from os import path, walk, makedirs
38 1395c40e Stavros Sachtouris
39 effa4b8f Stavros Sachtouris
from kamaki.clients.pithos import PithosClient, ClientError
40 1395c40e Stavros Sachtouris
41 234954d1 Stavros Sachtouris
from kamaki.cli import command
42 d486baec Stavros Sachtouris
from kamaki.cli.command_tree import CommandTree
43 6d190dd1 Stavros Sachtouris
from kamaki.cli.commands import (
44 cec2dfcd Stavros Sachtouris
    _command_init, errors, addLogSettings, DontRaiseKeyError, _optional_json,
45 cec2dfcd Stavros Sachtouris
    _name_filter, _optional_output_cmd)
46 8694bdad Stavros Sachtouris
from kamaki.cli.errors import (
47 bfd0f8db Stavros Sachtouris
    CLIBaseUrlError, CLIError, CLIInvalidArgument, raiseCLIError,
48 bfd0f8db Stavros Sachtouris
    CLISyntaxError)
49 cec2dfcd Stavros Sachtouris
from kamaki.cli.argument import (
50 bfa33995 Stavros Sachtouris
    FlagArgument, IntArgument, ValueArgument, DateArgument, KeyValueArgument,
51 7b109aa7 Stavros Sachtouris
    ProgressBarArgument, RepeatableArgument, DataSizeArgument)
52 7147e1ca Stavros Sachtouris
from kamaki.cli.utils import (
53 edc1182f Stavros Sachtouris
    format_size, bold, get_path_size, guess_mime_type)
54 234954d1 Stavros Sachtouris
55 cec2dfcd Stavros Sachtouris
file_cmds = CommandTree('file', 'Pithos+/Storage object level API commands')
56 cec2dfcd Stavros Sachtouris
container_cmds = CommandTree(
57 cec2dfcd Stavros Sachtouris
    'container', 'Pithos+/Storage container level API commands')
58 bfd0f8db Stavros Sachtouris
sharer_cmds = CommandTree('sharer', 'Pithos+/Storage sharers')
59 bfd0f8db Stavros Sachtouris
group_cmds = CommandTree('group', 'Pithos+/Storage user groups')
60 bfd0f8db Stavros Sachtouris
_commands = [file_cmds, container_cmds, sharer_cmds, group_cmds]
61 234954d1 Stavros Sachtouris
62 e3d4d442 Stavros Sachtouris
63 5eae854d Stavros Sachtouris
class _pithos_init(_command_init):
64 cec2dfcd Stavros Sachtouris
    """Initilize a pithos+ client
65 cec2dfcd Stavros Sachtouris
    There is always a default account (current user uuid)
66 cec2dfcd Stavros Sachtouris
    There is always a default container (pithos)
67 cec2dfcd Stavros Sachtouris
    """
68 ece4ae4b Stavros Sachtouris
69 b4f69041 Stavros Sachtouris
    @DontRaiseKeyError
70 b4f69041 Stavros Sachtouris
    def _custom_container(self):
71 144b3551 Stavros Sachtouris
        return self.config.get_cloud(self.cloud, 'pithos_container')
72 b4f69041 Stavros Sachtouris
73 b4f69041 Stavros Sachtouris
    @DontRaiseKeyError
74 b4f69041 Stavros Sachtouris
    def _custom_uuid(self):
75 144b3551 Stavros Sachtouris
        return self.config.get_cloud(self.cloud, 'pithos_uuid')
76 b4f69041 Stavros Sachtouris
77 b4f69041 Stavros Sachtouris
    def _set_account(self):
78 b4f69041 Stavros Sachtouris
        self.account = self._custom_uuid()
79 b4f69041 Stavros Sachtouris
        if self.account:
80 b4f69041 Stavros Sachtouris
            return
81 cec2dfcd Stavros Sachtouris
        astakos = getattr(self, 'auth_base', None)
82 cec2dfcd Stavros Sachtouris
        if astakos:
83 cec2dfcd Stavros Sachtouris
            self.account = astakos.user_term('id', self.token)
84 b4f69041 Stavros Sachtouris
        else:
85 cec2dfcd Stavros Sachtouris
            raise CLIBaseUrlError(service='astakos')
86 b4f69041 Stavros Sachtouris
87 1395c40e Stavros Sachtouris
    @errors.generic.all
88 b4f69041 Stavros Sachtouris
    @addLogSettings
89 1395c40e Stavros Sachtouris
    def _run(self):
90 cec2dfcd Stavros Sachtouris
        cloud = getattr(self, 'cloud', None)
91 cec2dfcd Stavros Sachtouris
        if cloud:
92 b4f69041 Stavros Sachtouris
            self.base_url = self._custom_url('pithos')
93 b4f69041 Stavros Sachtouris
        else:
94 b4f69041 Stavros Sachtouris
            self.cloud = 'default'
95 b4f69041 Stavros Sachtouris
        self.token = self._custom_token('pithos')
96 cec2dfcd Stavros Sachtouris
        self.container = self._custom_container() or 'pithos'
97 8cec3671 Stavros Sachtouris
98 cec2dfcd Stavros Sachtouris
        astakos = getattr(self, 'auth_base', None)
99 cec2dfcd Stavros Sachtouris
        if astakos:
100 cec2dfcd Stavros Sachtouris
            self.token = self.token or astakos.token
101 b4f69041 Stavros Sachtouris
            if not self.base_url:
102 cec2dfcd Stavros Sachtouris
                pithos_endpoints = astakos.get_service_endpoints(
103 b4f69041 Stavros Sachtouris
                    self._custom_type('pithos') or 'object-store',
104 b4f69041 Stavros Sachtouris
                    self._custom_version('pithos') or '')
105 b4f69041 Stavros Sachtouris
                self.base_url = pithos_endpoints['publicURL']
106 cec2dfcd Stavros Sachtouris
        else:
107 cec2dfcd Stavros Sachtouris
            raise CLIBaseUrlError(service='astakos')
108 8cec3671 Stavros Sachtouris
109 1f5debf7 Stavros Sachtouris
        self._set_account()
110 de73876b Stavros Sachtouris
        self.client = PithosClient(
111 cec2dfcd Stavros Sachtouris
            self.base_url, self.token, self.account, self.container)
112 7493ccb6 Stavros Sachtouris
113 1395c40e Stavros Sachtouris
    def main(self):
114 1395c40e Stavros Sachtouris
        self._run()
115 1395c40e Stavros Sachtouris
116 234954d1 Stavros Sachtouris
117 cec2dfcd Stavros Sachtouris
class _pithos_account(_pithos_init):
118 cec2dfcd Stavros Sachtouris
    """Setup account"""
119 7493ccb6 Stavros Sachtouris
120 b4f69041 Stavros Sachtouris
    def __init__(self, arguments={}, auth_base=None, cloud=None):
121 effa4b8f Stavros Sachtouris
        super(_pithos_account, self).__init__(arguments, auth_base, cloud)
122 439826ec Stavros Sachtouris
        self['account'] = ValueArgument(
123 cec2dfcd Stavros Sachtouris
            'Use (a different) user uuid', ('-A', '--account'))
124 7493ccb6 Stavros Sachtouris
125 7b109aa7 Stavros Sachtouris
    def print_objects(self, object_list):
126 7b109aa7 Stavros Sachtouris
        for index, obj in enumerate(object_list):
127 7b109aa7 Stavros Sachtouris
            pretty_obj = obj.copy()
128 7b109aa7 Stavros Sachtouris
            index += 1
129 7b109aa7 Stavros Sachtouris
            empty_space = ' ' * (len(str(len(object_list))) - len(str(index)))
130 7b109aa7 Stavros Sachtouris
            if 'subdir' in obj:
131 7b109aa7 Stavros Sachtouris
                continue
132 7b109aa7 Stavros Sachtouris
            if self._is_dir(obj):
133 7b109aa7 Stavros Sachtouris
                size = 'D'
134 7b109aa7 Stavros Sachtouris
            else:
135 7b109aa7 Stavros Sachtouris
                size = format_size(obj['bytes'])
136 7b109aa7 Stavros Sachtouris
                pretty_obj['bytes'] = '%s (%s)' % (obj['bytes'], size)
137 7b109aa7 Stavros Sachtouris
            oname = obj['name'] if self['more'] else bold(obj['name'])
138 7b109aa7 Stavros Sachtouris
            prfx = ('%s%s. ' % (empty_space, index)) if self['enum'] else ''
139 7b109aa7 Stavros Sachtouris
            if self['detail']:
140 7b109aa7 Stavros Sachtouris
                self.writeln('%s%s' % (prfx, oname))
141 7b109aa7 Stavros Sachtouris
                self.print_dict(pretty_obj, exclude=('name'))
142 7b109aa7 Stavros Sachtouris
                self.writeln()
143 7b109aa7 Stavros Sachtouris
            else:
144 7b109aa7 Stavros Sachtouris
                oname = '%s%9s %s' % (prfx, size, oname)
145 7b109aa7 Stavros Sachtouris
                oname += '/' if self._is_dir(obj) else u''
146 7b109aa7 Stavros Sachtouris
                self.writeln(oname)
147 7493ccb6 Stavros Sachtouris
148 7b109aa7 Stavros Sachtouris
    @staticmethod
149 7b109aa7 Stavros Sachtouris
    def _is_dir(remote_dict):
150 7b109aa7 Stavros Sachtouris
        return 'application/directory' == remote_dict.get(
151 7b109aa7 Stavros Sachtouris
            'content_type', remote_dict.get('content-type', ''))
152 1395c40e Stavros Sachtouris
153 cec2dfcd Stavros Sachtouris
    def _run(self):
154 cec2dfcd Stavros Sachtouris
        super(_pithos_account, self)._run()
155 cec2dfcd Stavros Sachtouris
        self.client.account = self['account'] or getattr(
156 cec2dfcd Stavros Sachtouris
            self, 'account', getattr(self.client, 'account', None))
157 234954d1 Stavros Sachtouris
158 7493ccb6 Stavros Sachtouris
159 cec2dfcd Stavros Sachtouris
class _pithos_container(_pithos_account):
160 cec2dfcd Stavros Sachtouris
    """Setup container"""
161 2d7ce81e Stavros Sachtouris
162 b4f69041 Stavros Sachtouris
    def __init__(self, arguments={}, auth_base=None, cloud=None):
163 effa4b8f Stavros Sachtouris
        super(_pithos_container, self).__init__(arguments, auth_base, cloud)
164 439826ec Stavros Sachtouris
        self['container'] = ValueArgument(
165 cec2dfcd Stavros Sachtouris
            'Use this container (default: pithos)', ('-C', '--container'))
166 7493ccb6 Stavros Sachtouris
167 effa4b8f Stavros Sachtouris
    @staticmethod
168 effa4b8f Stavros Sachtouris
    def _resolve_pithos_url(url):
169 cec2dfcd Stavros Sachtouris
        """Match urls of one of the following formats:
170 cec2dfcd Stavros Sachtouris
        pithos://ACCOUNT/CONTAINER/OBJECT_PATH
171 cec2dfcd Stavros Sachtouris
        /CONTAINER/OBJECT_PATH
172 effa4b8f Stavros Sachtouris
        return account, container, path
173 1395c40e Stavros Sachtouris
        """
174 edc1182f Stavros Sachtouris
        account, container, obj_path, prefix = '', '', url, 'pithos://'
175 cec2dfcd Stavros Sachtouris
        if url.startswith(prefix):
176 effa4b8f Stavros Sachtouris
            account, sep, url = url[len(prefix):].partition('/')
177 cec2dfcd Stavros Sachtouris
            url = '/%s' % url
178 cec2dfcd Stavros Sachtouris
        if url.startswith('/'):
179 edc1182f Stavros Sachtouris
            container, sep, obj_path = url[1:].partition('/')
180 edc1182f Stavros Sachtouris
        return account, container, obj_path
181 447c9568 Stavros Sachtouris
182 cec2dfcd Stavros Sachtouris
    def _run(self, url=None):
183 effa4b8f Stavros Sachtouris
        acc, con, self.path = self._resolve_pithos_url(url or '')
184 432cea25 Stavros Sachtouris
        #  self.account = acc or getattr(self, 'account', '')
185 cec2dfcd Stavros Sachtouris
        super(_pithos_container, self)._run()
186 effa4b8f Stavros Sachtouris
        self.container = con or self['container'] or getattr(
187 cec2dfcd Stavros Sachtouris
            self, 'container', None) or getattr(self.client, 'container', '')
188 432cea25 Stavros Sachtouris
        self.client.account = acc or self.client.account
189 effa4b8f Stavros Sachtouris
        self.client.container = self.container
190 447c9568 Stavros Sachtouris
191 1395c40e Stavros Sachtouris
192 cec2dfcd Stavros Sachtouris
@command(file_cmds)
193 20206179 Stavros Sachtouris
class file_info(_pithos_container, _optional_json):
194 20206179 Stavros Sachtouris
    """Get information/details about a file"""
195 20206179 Stavros Sachtouris
196 20206179 Stavros Sachtouris
    arguments = dict(
197 20206179 Stavros Sachtouris
        object_version=ValueArgument(
198 20206179 Stavros Sachtouris
            'download a file of a specific version', '--object-version'),
199 20206179 Stavros Sachtouris
        hashmap=FlagArgument(
200 20206179 Stavros Sachtouris
            'Get file hashmap instead of details', '--hashmap'),
201 20206179 Stavros Sachtouris
        matching_etag=ValueArgument(
202 20206179 Stavros Sachtouris
            'show output if ETags match', '--if-match'),
203 20206179 Stavros Sachtouris
        non_matching_etag=ValueArgument(
204 20206179 Stavros Sachtouris
            'show output if ETags DO NOT match', '--if-none-match'),
205 20206179 Stavros Sachtouris
        modified_since_date=DateArgument(
206 20206179 Stavros Sachtouris
            'show output modified since then', '--if-modified-since'),
207 20206179 Stavros Sachtouris
        unmodified_since_date=DateArgument(
208 20206179 Stavros Sachtouris
            'show output unmodified since then', '--if-unmodified-since'),
209 bfa33995 Stavros Sachtouris
        sharing=FlagArgument(
210 bfa33995 Stavros Sachtouris
            'show object permissions and sharing information', '--sharing'),
211 bfa33995 Stavros Sachtouris
        metadata=FlagArgument('show only object metadata', '--metadata'),
212 bfa33995 Stavros Sachtouris
        versions=FlagArgument(
213 bfa33995 Stavros Sachtouris
            'show the list of versions for the file', '--object-versions')
214 20206179 Stavros Sachtouris
    )
215 20206179 Stavros Sachtouris
216 bfa33995 Stavros Sachtouris
    def version_print(self, versions):
217 bfa33995 Stavros Sachtouris
        return {'/%s/%s' % (self.container, self.path): [
218 bfa33995 Stavros Sachtouris
            dict(version_id=vitem[0], created=strftime(
219 bfa33995 Stavros Sachtouris
                '%d-%m-%Y %H:%M:%S',
220 bfa33995 Stavros Sachtouris
                localtime(float(vitem[1])))) for vitem in versions]}
221 7493ccb6 Stavros Sachtouris
222 1395c40e Stavros Sachtouris
    @errors.generic.all
223 20206179 Stavros Sachtouris
    @errors.pithos.connection
224 20206179 Stavros Sachtouris
    @errors.pithos.container
225 20206179 Stavros Sachtouris
    @errors.pithos.object_path
226 20206179 Stavros Sachtouris
    def _run(self):
227 20206179 Stavros Sachtouris
        if self['hashmap']:
228 20206179 Stavros Sachtouris
            r = self.client.get_object_hashmap(
229 20206179 Stavros Sachtouris
                self.path,
230 20206179 Stavros Sachtouris
                version=self['object_version'],
231 20206179 Stavros Sachtouris
                if_match=self['matching_etag'],
232 20206179 Stavros Sachtouris
                if_none_match=self['non_matching_etag'],
233 20206179 Stavros Sachtouris
                if_modified_since=self['modified_since_date'],
234 20206179 Stavros Sachtouris
                if_unmodified_since=self['unmodified_since_date'])
235 bfa33995 Stavros Sachtouris
        elif self['sharing']:
236 20206179 Stavros Sachtouris
            r = self.client.get_object_sharing(self.path)
237 bfa33995 Stavros Sachtouris
            r['public url'] = self.client.get_object_info(
238 bfa33995 Stavros Sachtouris
                self.path, version=self['object_version']).get(
239 bfa33995 Stavros Sachtouris
                    'x-object-public', None)
240 bfa33995 Stavros Sachtouris
        elif self['metadata']:
241 bfa33995 Stavros Sachtouris
            r, preflen = dict(), len('x-object-meta-')
242 bfa33995 Stavros Sachtouris
            for k, v in self.client.get_object_meta(self.path).items():
243 bfa33995 Stavros Sachtouris
                r[k[preflen:]] = v
244 bfa33995 Stavros Sachtouris
        elif self['versions']:
245 bfa33995 Stavros Sachtouris
            r = self.version_print(
246 bfa33995 Stavros Sachtouris
                self.client.get_object_versionlist(self.path))
247 20206179 Stavros Sachtouris
        else:
248 20206179 Stavros Sachtouris
            r = self.client.get_object_info(
249 20206179 Stavros Sachtouris
                self.path, version=self['object_version'])
250 20206179 Stavros Sachtouris
        self._print(r, self.print_dict)
251 20206179 Stavros Sachtouris
252 20206179 Stavros Sachtouris
    def main(self, path_or_url):
253 20206179 Stavros Sachtouris
        super(self.__class__, self)._run(path_or_url)
254 20206179 Stavros Sachtouris
        self._run()
255 20206179 Stavros Sachtouris
256 20206179 Stavros Sachtouris
257 20206179 Stavros Sachtouris
@command(file_cmds)
258 cec2dfcd Stavros Sachtouris
class file_list(_pithos_container, _optional_json, _name_filter):
259 cec2dfcd Stavros Sachtouris
    """List all objects in a container or a directory object"""
260 7493ccb6 Stavros Sachtouris
261 47ae7577 Stavros Sachtouris
    arguments = dict(
262 f40f0cb7 Stavros Sachtouris
        detail=FlagArgument('detailed output', ('-l', '--list')),
263 f40f0cb7 Stavros Sachtouris
        limit=IntArgument('limit number of listed items', ('-n', '--number')),
264 f40f0cb7 Stavros Sachtouris
        marker=ValueArgument('output greater that marker', '--marker'),
265 47ae7577 Stavros Sachtouris
        delimiter=ValueArgument('show output up to delimiter', '--delimiter'),
266 47ae7577 Stavros Sachtouris
        meta=ValueArgument(
267 201baa17 Stavros Sachtouris
            'show output with specified meta keys', '--meta',
268 47ae7577 Stavros Sachtouris
            default=[]),
269 47ae7577 Stavros Sachtouris
        if_modified_since=ValueArgument(
270 201baa17 Stavros Sachtouris
            'show output modified since then', '--if-modified-since'),
271 47ae7577 Stavros Sachtouris
        if_unmodified_since=ValueArgument(
272 201baa17 Stavros Sachtouris
            'show output not modified since then', '--if-unmodified-since'),
273 47ae7577 Stavros Sachtouris
        until=DateArgument('show metadata until then', '--until'),
274 47ae7577 Stavros Sachtouris
        format=ValueArgument(
275 201baa17 Stavros Sachtouris
            'format to parse until data (default: d/m/Y H:M:S )', '--format'),
276 1c366ac9 Stavros Sachtouris
        shared_by_me=FlagArgument(
277 1c366ac9 Stavros Sachtouris
            'show only files shared to other users', '--shared-by-me'),
278 1c366ac9 Stavros Sachtouris
        public=FlagArgument('show only published objects', '--public'),
279 fa9c0c38 Stavros Sachtouris
        more=FlagArgument('read long results', '--more'),
280 8694bdad Stavros Sachtouris
        enum=FlagArgument('Enumerate results', '--enumerate'),
281 8694bdad Stavros Sachtouris
        recursive=FlagArgument(
282 8694bdad Stavros Sachtouris
            'Recursively list containers and their contents',
283 8694bdad Stavros Sachtouris
            ('-R', '--recursive'))
284 47ae7577 Stavros Sachtouris
    )
285 c41a86b2 Stavros Sachtouris
286 1395c40e Stavros Sachtouris
    @errors.generic.all
287 1395c40e Stavros Sachtouris
    @errors.pithos.connection
288 1395c40e Stavros Sachtouris
    @errors.pithos.container
289 cec2dfcd Stavros Sachtouris
    @errors.pithos.object_path
290 1395c40e Stavros Sachtouris
    def _run(self):
291 cec2dfcd Stavros Sachtouris
        r = self.client.container_get(
292 cec2dfcd Stavros Sachtouris
            limit=False if self['more'] else self['limit'],
293 cec2dfcd Stavros Sachtouris
            marker=self['marker'],
294 7b109aa7 Stavros Sachtouris
            prefix=self['name_pref'],
295 cec2dfcd Stavros Sachtouris
            delimiter=self['delimiter'],
296 cec2dfcd Stavros Sachtouris
            path=self.path or '',
297 1c366ac9 Stavros Sachtouris
            show_only_shared=self['shared_by_me'],
298 cfba9587 Stavros Sachtouris
            public=self['public'],
299 cec2dfcd Stavros Sachtouris
            if_modified_since=self['if_modified_since'],
300 cec2dfcd Stavros Sachtouris
            if_unmodified_since=self['if_unmodified_since'],
301 cec2dfcd Stavros Sachtouris
            until=self['until'],
302 1c366ac9 Stavros Sachtouris
            meta=self['meta'])
303 7a3c66e1 Stavros Sachtouris
304 7a3c66e1 Stavros Sachtouris
        #  REMOVE THIS if version >> 0.12
305 7a3c66e1 Stavros Sachtouris
        if not r.json:
306 7a3c66e1 Stavros Sachtouris
            self.error('  NOTE: Since v0.12, use / for containers e.g.,')
307 7a3c66e1 Stavros Sachtouris
            self.error('    [kamaki] file list /pithos')
308 7a3c66e1 Stavros Sachtouris
309 cec2dfcd Stavros Sachtouris
        files = self._filter_by_name(r.json)
310 1757c616 Stavros Sachtouris
        if self['more']:
311 1757c616 Stavros Sachtouris
            outbu, self._out = self._out, StringIO()
312 1757c616 Stavros Sachtouris
        try:
313 f76c6bbc Stavros Sachtouris
            if self['json_output'] or self['output_format']:
314 1757c616 Stavros Sachtouris
                self._print(files)
315 1757c616 Stavros Sachtouris
            else:
316 cec2dfcd Stavros Sachtouris
                self.print_objects(files)
317 1757c616 Stavros Sachtouris
        finally:
318 1757c616 Stavros Sachtouris
            if self['more']:
319 1757c616 Stavros Sachtouris
                pager(self._out.getvalue())
320 1757c616 Stavros Sachtouris
                self._out = outbu
321 1395c40e Stavros Sachtouris
322 7b109aa7 Stavros Sachtouris
    def main(self, path_or_url=''):
323 cec2dfcd Stavros Sachtouris
        super(self.__class__, self)._run(path_or_url)
324 1395c40e Stavros Sachtouris
        self._run()
325 7493ccb6 Stavros Sachtouris
326 234954d1 Stavros Sachtouris
327 cec2dfcd Stavros Sachtouris
@command(file_cmds)
328 20206179 Stavros Sachtouris
class file_modify(_pithos_container):
329 20206179 Stavros Sachtouris
    """Modify the attributes of a file or directory object"""
330 20206179 Stavros Sachtouris
331 20206179 Stavros Sachtouris
    arguments = dict(
332 20206179 Stavros Sachtouris
        publish=FlagArgument(
333 20206179 Stavros Sachtouris
            'Make an object public (returns the public URL)', '--publish'),
334 20206179 Stavros Sachtouris
        unpublish=FlagArgument(
335 20206179 Stavros Sachtouris
            'Make an object unpublic', '--unpublish'),
336 20206179 Stavros Sachtouris
        uuid_for_read_permission=RepeatableArgument(
337 20206179 Stavros Sachtouris
            'Give read access to user/group (can be repeated, accumulative). '
338 20206179 Stavros Sachtouris
            'Format for users: UUID . Format for groups: UUID:GROUP . '
339 20206179 Stavros Sachtouris
            'Use * for all users/groups', '--read-permission'),
340 20206179 Stavros Sachtouris
        uuid_for_write_permission=RepeatableArgument(
341 20206179 Stavros Sachtouris
            'Give write access to user/group (can be repeated, accumulative). '
342 20206179 Stavros Sachtouris
            'Format for users: UUID . Format for groups: UUID:GROUP . '
343 20206179 Stavros Sachtouris
            'Use * for all users/groups', '--write-permission'),
344 bfa33995 Stavros Sachtouris
        no_permissions=FlagArgument('Remove permissions', '--no-permissions'),
345 bfa33995 Stavros Sachtouris
        metadata_to_set=KeyValueArgument(
346 bfa33995 Stavros Sachtouris
            'Add metadata (KEY=VALUE) to an object (can be repeated)',
347 bfa33995 Stavros Sachtouris
            '--metadata-add'),
348 bfa33995 Stavros Sachtouris
        metadata_key_to_delete=RepeatableArgument(
349 bfa33995 Stavros Sachtouris
            'Delete object metadata (can be repeated)', '--metadata-del'),
350 20206179 Stavros Sachtouris
    )
351 20206179 Stavros Sachtouris
    required = [
352 bfa33995 Stavros Sachtouris
        'publish', 'unpublish', 'uuid_for_read_permission', 'metadata_to_set',
353 bfa33995 Stavros Sachtouris
        'uuid_for_write_permission', 'no_permissions',
354 bfa33995 Stavros Sachtouris
        'metadata_key_to_delete']
355 7493ccb6 Stavros Sachtouris
356 1395c40e Stavros Sachtouris
    @errors.generic.all
357 1395c40e Stavros Sachtouris
    @errors.pithos.connection
358 1395c40e Stavros Sachtouris
    @errors.pithos.container
359 20206179 Stavros Sachtouris
    @errors.pithos.object_path
360 1395c40e Stavros Sachtouris
    def _run(self):
361 20206179 Stavros Sachtouris
        if self['publish']:
362 20206179 Stavros Sachtouris
            self.writeln(self.client.publish_object(self.path))
363 20206179 Stavros Sachtouris
        if self['unpublish']:
364 20206179 Stavros Sachtouris
            self.client.unpublish_object(self.path)
365 20206179 Stavros Sachtouris
        if self['uuid_for_read_permission'] or self[
366 20206179 Stavros Sachtouris
                'uuid_for_write_permission']:
367 20206179 Stavros Sachtouris
            perms = self.client.get_object_sharing(self.path)
368 20206179 Stavros Sachtouris
            read, write = perms.get('read', ''), perms.get('write', '')
369 20206179 Stavros Sachtouris
            read = read.split(',') if read else []
370 20206179 Stavros Sachtouris
            write = write.split(',') if write else []
371 1c366ac9 Stavros Sachtouris
            read += (self['uuid_for_read_permission'] or [])
372 1c366ac9 Stavros Sachtouris
            write += (self['uuid_for_write_permission'] or [])
373 20206179 Stavros Sachtouris
            self.client.set_object_sharing(
374 20206179 Stavros Sachtouris
                self.path, read_permission=read, write_permission=write)
375 20206179 Stavros Sachtouris
            self.print_dict(self.client.get_object_sharing(self.path))
376 20206179 Stavros Sachtouris
        if self['no_permissions']:
377 20206179 Stavros Sachtouris
            self.client.del_object_sharing(self.path)
378 bfa33995 Stavros Sachtouris
        metadata = self['metadata_to_set'] or dict()
379 1c366ac9 Stavros Sachtouris
        for k in (self['metadata_key_to_delete'] or []):
380 bfa33995 Stavros Sachtouris
            metadata[k] = ''
381 bfa33995 Stavros Sachtouris
        if metadata:
382 bfa33995 Stavros Sachtouris
            self.client.set_object_meta(self.path, metadata)
383 bfa33995 Stavros Sachtouris
            self.print_dict(self.client.get_object_meta(self.path))
384 20206179 Stavros Sachtouris
385 20206179 Stavros Sachtouris
    def main(self, path_or_url):
386 20206179 Stavros Sachtouris
        super(self.__class__, self)._run(path_or_url)
387 20206179 Stavros Sachtouris
        if self['publish'] and self['unpublish']:
388 20206179 Stavros Sachtouris
            raise CLIInvalidArgument(
389 20206179 Stavros Sachtouris
                'Arguments %s and %s cannot be used together' % (
390 eb647cfe Stavros Sachtouris
                    self.arguments['publish'].lvalue,
391 eb647cfe Stavros Sachtouris
                    self.arguments['publish'].lvalue))
392 20206179 Stavros Sachtouris
        if self['no_permissions'] and (
393 20206179 Stavros Sachtouris
                self['uuid_for_read_permission'] or self[
394 20206179 Stavros Sachtouris
                    'uuid_for_write_permission']):
395 20206179 Stavros Sachtouris
            raise CLIInvalidArgument(
396 eb647cfe Stavros Sachtouris
                '%s cannot be used with other permission arguments' % (
397 eb647cfe Stavros Sachtouris
                    self.arguments['no_permissions'].lvalue))
398 1395c40e Stavros Sachtouris
        self._run()
399 7493ccb6 Stavros Sachtouris
400 234954d1 Stavros Sachtouris
401 20206179 Stavros Sachtouris
@command(file_cmds)
402 cec2dfcd Stavros Sachtouris
class file_create(_pithos_container, _optional_output_cmd):
403 effa4b8f Stavros Sachtouris
    """Create an empty file"""
404 1e29b9f6 Stavros Sachtouris
405 1e29b9f6 Stavros Sachtouris
    arguments = dict(
406 1e29b9f6 Stavros Sachtouris
        content_type=ValueArgument(
407 1e29b9f6 Stavros Sachtouris
            'Set content type (default: application/octet-stream)',
408 1e29b9f6 Stavros Sachtouris
            '--content-type',
409 915b99b5 Stavros Sachtouris
            default='application/octet-stream')
410 1e29b9f6 Stavros Sachtouris
    )
411 1e29b9f6 Stavros Sachtouris
412 1395c40e Stavros Sachtouris
    @errors.generic.all
413 1395c40e Stavros Sachtouris
    @errors.pithos.connection
414 1395c40e Stavros Sachtouris
    @errors.pithos.container
415 1395c40e Stavros Sachtouris
    def _run(self):
416 915b99b5 Stavros Sachtouris
        self._optional_output(
417 915b99b5 Stavros Sachtouris
            self.client.create_object(self.path, self['content_type']))
418 1395c40e Stavros Sachtouris
419 cec2dfcd Stavros Sachtouris
    def main(self, path_or_url):
420 cec2dfcd Stavros Sachtouris
        super(self.__class__, self)._run(path_or_url)
421 b4cf92b8 Stavros Sachtouris
        self._run()
422 effa4b8f Stavros Sachtouris
423 effa4b8f Stavros Sachtouris
424 effa4b8f Stavros Sachtouris
@command(file_cmds)
425 effa4b8f Stavros Sachtouris
class file_mkdir(_pithos_container, _optional_output_cmd):
426 effa4b8f Stavros Sachtouris
    """Create a directory: /file create --content-type='applcation/directory'
427 effa4b8f Stavros Sachtouris
    """
428 effa4b8f Stavros Sachtouris
429 effa4b8f Stavros Sachtouris
    @errors.generic.all
430 effa4b8f Stavros Sachtouris
    @errors.pithos.connection
431 effa4b8f Stavros Sachtouris
    @errors.pithos.container
432 effa4b8f Stavros Sachtouris
    def _run(self):
433 effa4b8f Stavros Sachtouris
        self._optional_output(self.client.create_directory(self.path))
434 effa4b8f Stavros Sachtouris
435 74b7c6dc Stavros Sachtouris
    def main(self, path_or_url):
436 74b7c6dc Stavros Sachtouris
        super(self.__class__, self)._run(path_or_url)
437 1395c40e Stavros Sachtouris
        self._run()
438 1e29b9f6 Stavros Sachtouris
439 1e29b9f6 Stavros Sachtouris
440 20206179 Stavros Sachtouris
@command(file_cmds)
441 7b109aa7 Stavros Sachtouris
class file_delete(_pithos_container):
442 20206179 Stavros Sachtouris
    """Delete a file or directory object"""
443 7493ccb6 Stavros Sachtouris
444 2fe2672e Stavros Sachtouris
    arguments = dict(
445 20206179 Stavros Sachtouris
        until_date=DateArgument('remove history until then', '--until'),
446 20206179 Stavros Sachtouris
        yes=FlagArgument('Do not prompt for permission', '--yes'),
447 20206179 Stavros Sachtouris
        recursive=FlagArgument(
448 20206179 Stavros Sachtouris
            'If a directory, empty first', ('-r', '--recursive')),
449 20206179 Stavros Sachtouris
        delimiter=ValueArgument(
450 20206179 Stavros Sachtouris
            'delete objects prefixed with <object><delimiter>', '--delimiter')
451 2fe2672e Stavros Sachtouris
    )
452 7493ccb6 Stavros Sachtouris
453 1395c40e Stavros Sachtouris
    @errors.generic.all
454 1395c40e Stavros Sachtouris
    @errors.pithos.connection
455 1395c40e Stavros Sachtouris
    @errors.pithos.container
456 20206179 Stavros Sachtouris
    @errors.pithos.object_path
457 20206179 Stavros Sachtouris
    def _run(self):
458 20206179 Stavros Sachtouris
        if self.path:
459 20206179 Stavros Sachtouris
            if self['yes'] or self.ask_user(
460 20206179 Stavros Sachtouris
                    'Delete /%s/%s ?' % (self.container, self.path)):
461 7b109aa7 Stavros Sachtouris
                self.client.del_object(
462 20206179 Stavros Sachtouris
                    self.path,
463 20206179 Stavros Sachtouris
                    until=self['until_date'],
464 7b109aa7 Stavros Sachtouris
                    delimiter='/' if self['recursive'] else self['delimiter'])
465 20206179 Stavros Sachtouris
            else:
466 20206179 Stavros Sachtouris
                self.error('Aborted')
467 20206179 Stavros Sachtouris
        else:
468 7b109aa7 Stavros Sachtouris
            if self['yes'] or self.ask_user(
469 7b109aa7 Stavros Sachtouris
                    'Empty container /%s ?' % self.container):
470 7b109aa7 Stavros Sachtouris
                self.client.container_delete(self.container, delimiter='/')
471 7b109aa7 Stavros Sachtouris
            else:
472 7b109aa7 Stavros Sachtouris
                self.error('Aborted')
473 1395c40e Stavros Sachtouris
474 20206179 Stavros Sachtouris
    def main(self, path_or_url):
475 20206179 Stavros Sachtouris
        super(self.__class__, self)._run(path_or_url)
476 20206179 Stavros Sachtouris
        self._run()
477 7493ccb6 Stavros Sachtouris
478 234954d1 Stavros Sachtouris
479 effa4b8f Stavros Sachtouris
class _source_destination(_pithos_container, _optional_output_cmd):
480 2fe2672e Stavros Sachtouris
481 de932277 Stavros Sachtouris
    sd_arguments = dict(
482 effa4b8f Stavros Sachtouris
        destination_user_uuid=ValueArgument(
483 effa4b8f Stavros Sachtouris
            'default: current user uuid', '--to-account'),
484 effa4b8f Stavros Sachtouris
        destination_container=ValueArgument(
485 effa4b8f Stavros Sachtouris
            'default: pithos', '--to-container'),
486 effa4b8f Stavros Sachtouris
        source_prefix=FlagArgument(
487 effa4b8f Stavros Sachtouris
            'Transfer all files that are prefixed with SOURCE PATH If the '
488 effa4b8f Stavros Sachtouris
            'destination path is specified, replace SOURCE_PATH with '
489 effa4b8f Stavros Sachtouris
            'DESTINATION_PATH',
490 de932277 Stavros Sachtouris
            ('-r', '--recursive')),
491 effa4b8f Stavros Sachtouris
        force=FlagArgument(
492 bfa33995 Stavros Sachtouris
            'Overwrite destination objects, if needed', ('-f', '--force')),
493 bfa33995 Stavros Sachtouris
        source_version=ValueArgument(
494 bfa33995 Stavros Sachtouris
            'The version of the source object', '--source-version')
495 2fe2672e Stavros Sachtouris
    )
496 7493ccb6 Stavros Sachtouris
497 b4f69041 Stavros Sachtouris
    def __init__(self, arguments={}, auth_base=None, cloud=None):
498 761e0cbf Stavros Sachtouris
        self.arguments.update(arguments)
499 de932277 Stavros Sachtouris
        self.arguments.update(self.sd_arguments)
500 de932277 Stavros Sachtouris
        super(_source_destination, self).__init__(
501 b4f69041 Stavros Sachtouris
            self.arguments, auth_base, cloud)
502 761e0cbf Stavros Sachtouris
503 de932277 Stavros Sachtouris
    def _report_transfer(self, src, dst, transfer_name):
504 de932277 Stavros Sachtouris
        if not dst:
505 de932277 Stavros Sachtouris
            if transfer_name in ('move', ):
506 de932277 Stavros Sachtouris
                self.error('  delete source directory %s' % src)
507 de932277 Stavros Sachtouris
            return
508 de932277 Stavros Sachtouris
        dst_prf = '' if self.account == self.dst_client.account else (
509 de932277 Stavros Sachtouris
                'pithos://%s' % self.dst_client.account)
510 de932277 Stavros Sachtouris
        if src:
511 de932277 Stavros Sachtouris
            src_prf = '' if self.account == self.dst_client.account else (
512 de932277 Stavros Sachtouris
                    'pithos://%s' % self.account)
513 de932277 Stavros Sachtouris
            self.error('  %s %s/%s/%s\n  -->  %s/%s/%s' % (
514 de932277 Stavros Sachtouris
                transfer_name,
515 de932277 Stavros Sachtouris
                src_prf, self.container, src,
516 de932277 Stavros Sachtouris
                dst_prf, self.dst_client.container, dst))
517 de932277 Stavros Sachtouris
        else:
518 de932277 Stavros Sachtouris
            self.error('  mkdir %s/%s/%s' % (
519 de932277 Stavros Sachtouris
                dst_prf, self.dst_client.container, dst))
520 300da0fb Stavros Sachtouris
521 300da0fb Stavros Sachtouris
    @errors.generic.all
522 300da0fb Stavros Sachtouris
    @errors.pithos.account
523 effa4b8f Stavros Sachtouris
    def _src_dst(self, version=None):
524 effa4b8f Stavros Sachtouris
        """Preconditions:
525 effa4b8f Stavros Sachtouris
        self.account, self.container, self.path
526 effa4b8f Stavros Sachtouris
        self.dst_acc, self.dst_con, self.dst_path
527 effa4b8f Stavros Sachtouris
        They should all be configured properly
528 effa4b8f Stavros Sachtouris
        :returns: [(src_path, dst_path), ...], if src_path is None, create
529 effa4b8f Stavros Sachtouris
            destination directory
530 6736f171 Stavros Sachtouris
        """
531 effa4b8f Stavros Sachtouris
        src_objects, dst_objects, pairs = dict(), dict(), []
532 ece4ae4b Stavros Sachtouris
        try:
533 effa4b8f Stavros Sachtouris
            for obj in self.dst_client.list_objects(
534 effa4b8f Stavros Sachtouris
                    prefix=self.dst_path or self.path or '/'):
535 effa4b8f Stavros Sachtouris
                dst_objects[obj['name']] = obj
536 effa4b8f Stavros Sachtouris
        except ClientError as ce:
537 effa4b8f Stavros Sachtouris
            if ce.status in (404, ):
538 effa4b8f Stavros Sachtouris
                raise CLIError(
539 effa4b8f Stavros Sachtouris
                    'Destination container pithos://%s/%s not found' % (
540 effa4b8f Stavros Sachtouris
                        self.dst_client.account, self.dst_client.container))
541 de932277 Stavros Sachtouris
            raise ce
542 effa4b8f Stavros Sachtouris
        if self['source_prefix']:
543 effa4b8f Stavros Sachtouris
            #  Copy and replace prefixes
544 7b109aa7 Stavros Sachtouris
            for src_obj in self.client.list_objects(prefix=self.path):
545 effa4b8f Stavros Sachtouris
                src_objects[src_obj['name']] = src_obj
546 effa4b8f Stavros Sachtouris
            for src_path, src_obj in src_objects.items():
547 effa4b8f Stavros Sachtouris
                dst_path = '%s%s' % (
548 effa4b8f Stavros Sachtouris
                    self.dst_path or self.path, src_path[len(self.path):])
549 effa4b8f Stavros Sachtouris
                dst_obj = dst_objects.get(dst_path, None)
550 effa4b8f Stavros Sachtouris
                if self['force'] or not dst_obj:
551 effa4b8f Stavros Sachtouris
                    #  Just do it
552 de932277 Stavros Sachtouris
                    pairs.append((
553 de932277 Stavros Sachtouris
                        None if self._is_dir(src_obj) else src_path, dst_path))
554 de932277 Stavros Sachtouris
                    if self._is_dir(src_obj):
555 de932277 Stavros Sachtouris
                        pairs.append((self.path or dst_path, None))
556 effa4b8f Stavros Sachtouris
                elif not (self._is_dir(dst_obj) and self._is_dir(src_obj)):
557 effa4b8f Stavros Sachtouris
                    raise CLIError(
558 effa4b8f Stavros Sachtouris
                        'Destination object exists', importance=2, details=[
559 effa4b8f Stavros Sachtouris
                            'Failed while transfering:',
560 effa4b8f Stavros Sachtouris
                            '    pithos://%s/%s/%s' % (
561 effa4b8f Stavros Sachtouris
                                    self.account,
562 effa4b8f Stavros Sachtouris
                                    self.container,
563 effa4b8f Stavros Sachtouris
                                    src_path),
564 effa4b8f Stavros Sachtouris
                            '--> pithos://%s/%s/%s' % (
565 effa4b8f Stavros Sachtouris
                                    self.dst_client.account,
566 effa4b8f Stavros Sachtouris
                                    self.dst_client.container,
567 effa4b8f Stavros Sachtouris
                                    dst_path),
568 eb647cfe Stavros Sachtouris
                            'Use %s to transfer overwrite' % (
569 eb647cfe Stavros Sachtouris
                                    self.arguments['force'].lvalue)])
570 effa4b8f Stavros Sachtouris
        else:
571 de932277 Stavros Sachtouris
            #  One object transfer
572 de932277 Stavros Sachtouris
            try:
573 bfa33995 Stavros Sachtouris
                src_version_arg = self.arguments.get('source_version', None)
574 bfa33995 Stavros Sachtouris
                src_obj = self.client.get_object_info(
575 bfa33995 Stavros Sachtouris
                    self.path,
576 bfa33995 Stavros Sachtouris
                    version=src_version_arg.value if src_version_arg else None)
577 de932277 Stavros Sachtouris
            except ClientError as ce:
578 de932277 Stavros Sachtouris
                if ce.status in (204, ):
579 de932277 Stavros Sachtouris
                    raise CLIError(
580 de932277 Stavros Sachtouris
                        'Missing specific path container %s' % self.container,
581 de932277 Stavros Sachtouris
                        importance=2, details=[
582 de932277 Stavros Sachtouris
                            'To transfer container contents %s' % (
583 eb647cfe Stavros Sachtouris
                                self.arguments['source_prefix'].lvalue)])
584 ece4ae4b Stavros Sachtouris
                raise
585 effa4b8f Stavros Sachtouris
            dst_path = self.dst_path or self.path
586 74b7c6dc Stavros Sachtouris
            dst_obj = dst_objects.get(dst_path or self.path, None)
587 effa4b8f Stavros Sachtouris
            if self['force'] or not dst_obj:
588 effa4b8f Stavros Sachtouris
                pairs.append(
589 de932277 Stavros Sachtouris
                    (None if self._is_dir(src_obj) else self.path, dst_path))
590 de932277 Stavros Sachtouris
                if self._is_dir(src_obj):
591 de932277 Stavros Sachtouris
                    pairs.append((self.path or dst_path, None))
592 effa4b8f Stavros Sachtouris
            elif self._is_dir(src_obj):
593 effa4b8f Stavros Sachtouris
                raise CLIError(
594 effa4b8f Stavros Sachtouris
                    'Cannot transfer an application/directory object',
595 effa4b8f Stavros Sachtouris
                    importance=2, details=[
596 effa4b8f Stavros Sachtouris
                        'The object pithos://%s/%s/%s is a directory' % (
597 effa4b8f Stavros Sachtouris
                            self.account,
598 effa4b8f Stavros Sachtouris
                            self.container,
599 de932277 Stavros Sachtouris
                            self.path),
600 effa4b8f Stavros Sachtouris
                        'To recursively copy a directory, use',
601 eb647cfe Stavros Sachtouris
                        '  %s' % self.arguments['source_prefix'].lvalue,
602 effa4b8f Stavros Sachtouris
                        'To create a file, use',
603 effa4b8f Stavros Sachtouris
                        '  /file create  (general purpose)',
604 effa4b8f Stavros Sachtouris
                        '  /file mkdir   (a directory object)'])
605 effa4b8f Stavros Sachtouris
            else:
606 effa4b8f Stavros Sachtouris
                raise CLIError(
607 effa4b8f Stavros Sachtouris
                    'Destination object exists',
608 effa4b8f Stavros Sachtouris
                    importance=2, details=[
609 effa4b8f Stavros Sachtouris
                        'Failed while transfering:',
610 effa4b8f Stavros Sachtouris
                        '    pithos://%s/%s/%s' % (
611 effa4b8f Stavros Sachtouris
                                self.account,
612 effa4b8f Stavros Sachtouris
                                self.container,
613 de932277 Stavros Sachtouris
                                self.path),
614 effa4b8f Stavros Sachtouris
                        '--> pithos://%s/%s/%s' % (
615 effa4b8f Stavros Sachtouris
                                self.dst_client.account,
616 effa4b8f Stavros Sachtouris
                                self.dst_client.container,
617 effa4b8f Stavros Sachtouris
                                dst_path),
618 eb647cfe Stavros Sachtouris
                        'Use %s to transfer overwrite' % (
619 eb647cfe Stavros Sachtouris
                                self.arguments['force'].lvalue)])
620 effa4b8f Stavros Sachtouris
        return pairs
621 effa4b8f Stavros Sachtouris
622 effa4b8f Stavros Sachtouris
    def _run(self, source_path_or_url, destination_path_or_url=''):
623 effa4b8f Stavros Sachtouris
        super(_source_destination, self)._run(source_path_or_url)
624 effa4b8f Stavros Sachtouris
        dst_acc, dst_con, dst_path = self._resolve_pithos_url(
625 effa4b8f Stavros Sachtouris
            destination_path_or_url)
626 effa4b8f Stavros Sachtouris
        self.dst_client = PithosClient(
627 effa4b8f Stavros Sachtouris
            base_url=self.client.base_url, token=self.client.token,
628 effa4b8f Stavros Sachtouris
            container=self[
629 effa4b8f Stavros Sachtouris
                'destination_container'] or dst_con or self.client.container,
630 effa4b8f Stavros Sachtouris
            account=self[
631 432cea25 Stavros Sachtouris
                'destination_user_uuid'] or dst_acc or self.account)
632 effa4b8f Stavros Sachtouris
        self.dst_path = dst_path or self.path
633 300da0fb Stavros Sachtouris
634 ece4ae4b Stavros Sachtouris
635 effa4b8f Stavros Sachtouris
@command(file_cmds)
636 effa4b8f Stavros Sachtouris
class file_copy(_source_destination):
637 effa4b8f Stavros Sachtouris
    """Copy objects, even between different accounts or containers"""
638 761e0cbf Stavros Sachtouris
639 761e0cbf Stavros Sachtouris
    arguments = dict(
640 effa4b8f Stavros Sachtouris
        public=ValueArgument('publish new object', '--public'),
641 761e0cbf Stavros Sachtouris
        content_type=ValueArgument(
642 201baa17 Stavros Sachtouris
            'change object\'s content type', '--content-type'),
643 1d3f006b Stavros Sachtouris
        source_version=ValueArgument(
644 bfa33995 Stavros Sachtouris
            'The version of the source object', '--object-version')
645 761e0cbf Stavros Sachtouris
    )
646 761e0cbf Stavros Sachtouris
647 1395c40e Stavros Sachtouris
    @errors.generic.all
648 1395c40e Stavros Sachtouris
    @errors.pithos.connection
649 1395c40e Stavros Sachtouris
    @errors.pithos.container
650 300da0fb Stavros Sachtouris
    @errors.pithos.account
651 effa4b8f Stavros Sachtouris
    def _run(self):
652 effa4b8f Stavros Sachtouris
        for src, dst in self._src_dst(self['source_version']):
653 de932277 Stavros Sachtouris
            self._report_transfer(src, dst, 'copy')
654 de932277 Stavros Sachtouris
            if src and dst:
655 effa4b8f Stavros Sachtouris
                self.dst_client.copy_object(
656 effa4b8f Stavros Sachtouris
                    src_container=self.client.container,
657 effa4b8f Stavros Sachtouris
                    src_object=src,
658 effa4b8f Stavros Sachtouris
                    dst_container=self.dst_client.container,
659 effa4b8f Stavros Sachtouris
                    dst_object=dst,
660 432cea25 Stavros Sachtouris
                    source_account=self.client.account,
661 effa4b8f Stavros Sachtouris
                    source_version=self['source_version'],
662 effa4b8f Stavros Sachtouris
                    public=self['public'],
663 effa4b8f Stavros Sachtouris
                    content_type=self['content_type'])
664 de932277 Stavros Sachtouris
            elif dst:
665 de932277 Stavros Sachtouris
                self.dst_client.create_directory(dst)
666 effa4b8f Stavros Sachtouris
667 effa4b8f Stavros Sachtouris
    def main(self, source_path_or_url, destination_path_or_url=None):
668 3ae60112 Stavros Sachtouris
        super(file_copy, self)._run(
669 effa4b8f Stavros Sachtouris
            source_path_or_url, destination_path_or_url or '')
670 effa4b8f Stavros Sachtouris
        self._run()
671 effa4b8f Stavros Sachtouris
672 effa4b8f Stavros Sachtouris
673 effa4b8f Stavros Sachtouris
@command(file_cmds)
674 effa4b8f Stavros Sachtouris
class file_move(_source_destination):
675 effa4b8f Stavros Sachtouris
    """Move objects, even between different accounts or containers"""
676 2fe2672e Stavros Sachtouris
677 2fe2672e Stavros Sachtouris
    arguments = dict(
678 effa4b8f Stavros Sachtouris
        public=ValueArgument('publish new object', '--public'),
679 761e0cbf Stavros Sachtouris
        content_type=ValueArgument(
680 effa4b8f Stavros Sachtouris
            'change object\'s content type', '--content-type')
681 2fe2672e Stavros Sachtouris
    )
682 7493ccb6 Stavros Sachtouris
683 1395c40e Stavros Sachtouris
    @errors.generic.all
684 1395c40e Stavros Sachtouris
    @errors.pithos.connection
685 1395c40e Stavros Sachtouris
    @errors.pithos.container
686 effa4b8f Stavros Sachtouris
    @errors.pithos.account
687 effa4b8f Stavros Sachtouris
    def _run(self):
688 effa4b8f Stavros Sachtouris
        for src, dst in self._src_dst():
689 de932277 Stavros Sachtouris
            self._report_transfer(src, dst, 'move')
690 de932277 Stavros Sachtouris
            if src and dst:
691 effa4b8f Stavros Sachtouris
                self.dst_client.move_object(
692 effa4b8f Stavros Sachtouris
                    src_container=self.client.container,
693 effa4b8f Stavros Sachtouris
                    src_object=src,
694 effa4b8f Stavros Sachtouris
                    dst_container=self.dst_client.container,
695 effa4b8f Stavros Sachtouris
                    dst_object=dst,
696 effa4b8f Stavros Sachtouris
                    source_account=self.account,
697 effa4b8f Stavros Sachtouris
                    public=self['public'],
698 effa4b8f Stavros Sachtouris
                    content_type=self['content_type'])
699 de932277 Stavros Sachtouris
            elif dst:
700 de932277 Stavros Sachtouris
                self.dst_client.create_directory(dst)
701 effa4b8f Stavros Sachtouris
            else:
702 de932277 Stavros Sachtouris
                self.client.del_object(src)
703 effa4b8f Stavros Sachtouris
704 effa4b8f Stavros Sachtouris
    def main(self, source_path_or_url, destination_path_or_url=None):
705 effa4b8f Stavros Sachtouris
        super(file_move, self)._run(
706 effa4b8f Stavros Sachtouris
            source_path_or_url, destination_path_or_url or '')
707 effa4b8f Stavros Sachtouris
        self._run()
708 edc1182f Stavros Sachtouris
709 edc1182f Stavros Sachtouris
710 edc1182f Stavros Sachtouris
@command(file_cmds)
711 edc1182f Stavros Sachtouris
class file_append(_pithos_container, _optional_output_cmd):
712 aaca2ef4 Stavros Sachtouris
    """Append local file to (existing) remote object
713 aaca2ef4 Stavros Sachtouris
    The remote object should exist.
714 aaca2ef4 Stavros Sachtouris
    If the remote object is a directory, it is transformed into a file.
715 aaca2ef4 Stavros Sachtouris
    In the later case, objects under the directory remain intact.
716 aaca2ef4 Stavros Sachtouris
    """
717 7493ccb6 Stavros Sachtouris
718 2fe2672e Stavros Sachtouris
    arguments = dict(
719 2fe2672e Stavros Sachtouris
        progress_bar=ProgressBarArgument(
720 ff1c0296 Stavros Sachtouris
            'do not show progress bar', ('-N', '--no-progress-bar'),
721 edc1182f Stavros Sachtouris
            default=False),
722 edc1182f Stavros Sachtouris
        max_threads=IntArgument('default: 1', '--threads'),
723 2fe2672e Stavros Sachtouris
    )
724 486f7af1 Stavros Sachtouris
725 ca092af4 Stavros Sachtouris
    @errors.generic.all
726 ca092af4 Stavros Sachtouris
    @errors.pithos.connection
727 ca092af4 Stavros Sachtouris
    @errors.pithos.container
728 ca092af4 Stavros Sachtouris
    @errors.pithos.object_path
729 ca092af4 Stavros Sachtouris
    def _run(self, local_path):
730 edc1182f Stavros Sachtouris
        if self['max_threads'] > 0:
731 edc1182f Stavros Sachtouris
            self.client.MAX_THREADS = int(self['max_threads'])
732 ca092af4 Stavros Sachtouris
        (progress_bar, upload_cb) = self._safe_progress_bar('Appending')
733 7493ccb6 Stavros Sachtouris
        try:
734 ff1c0296 Stavros Sachtouris
            with open(local_path, 'rb') as f:
735 ff1c0296 Stavros Sachtouris
                self._optional_output(
736 ff1c0296 Stavros Sachtouris
                    self.client.append_object(self.path, f, upload_cb))
737 852a22e7 Stavros Sachtouris
        finally:
738 ca092af4 Stavros Sachtouris
            self._safe_progress_bar_finish(progress_bar)
739 ca092af4 Stavros Sachtouris
740 edc1182f Stavros Sachtouris
    def main(self, local_path, remote_path_or_url):
741 edc1182f Stavros Sachtouris
        super(self.__class__, self)._run(remote_path_or_url)
742 ca092af4 Stavros Sachtouris
        self._run(local_path)
743 7493ccb6 Stavros Sachtouris
744 234954d1 Stavros Sachtouris
745 edc1182f Stavros Sachtouris
@command(file_cmds)
746 edc1182f Stavros Sachtouris
class file_truncate(_pithos_container, _optional_output_cmd):
747 edc1182f Stavros Sachtouris
    """Truncate remote file up to size"""
748 edc1182f Stavros Sachtouris
749 edc1182f Stavros Sachtouris
    arguments = dict(
750 edc1182f Stavros Sachtouris
        size_in_bytes=IntArgument('Length of file after truncation', '--size')
751 edc1182f Stavros Sachtouris
    )
752 edc1182f Stavros Sachtouris
    required = ('size_in_bytes', )
753 7493ccb6 Stavros Sachtouris
754 ca092af4 Stavros Sachtouris
    @errors.generic.all
755 ca092af4 Stavros Sachtouris
    @errors.pithos.connection
756 ca092af4 Stavros Sachtouris
    @errors.pithos.container
757 ca092af4 Stavros Sachtouris
    @errors.pithos.object_path
758 ca092af4 Stavros Sachtouris
    @errors.pithos.object_size
759 edc1182f Stavros Sachtouris
    def _run(self, size):
760 915b99b5 Stavros Sachtouris
        self._optional_output(self.client.truncate_object(self.path, size))
761 ca092af4 Stavros Sachtouris
762 edc1182f Stavros Sachtouris
    def main(self, path_or_url):
763 edc1182f Stavros Sachtouris
        super(self.__class__, self)._run(path_or_url)
764 edc1182f Stavros Sachtouris
        self._run(size=self['size_in_bytes'])
765 7493ccb6 Stavros Sachtouris
766 234954d1 Stavros Sachtouris
767 edc1182f Stavros Sachtouris
@command(file_cmds)
768 edc1182f Stavros Sachtouris
class file_overwrite(_pithos_container, _optional_output_cmd):
769 edc1182f Stavros Sachtouris
    """Overwrite part of a remote file"""
770 7493ccb6 Stavros Sachtouris
771 2fe2672e Stavros Sachtouris
    arguments = dict(
772 2fe2672e Stavros Sachtouris
        progress_bar=ProgressBarArgument(
773 ff1c0296 Stavros Sachtouris
            'do not show progress bar', ('-N', '--no-progress-bar'),
774 edc1182f Stavros Sachtouris
            default=False),
775 edc1182f Stavros Sachtouris
        start_position=IntArgument('File position in bytes', '--from'),
776 edc1182f Stavros Sachtouris
        end_position=IntArgument('File position in bytes', '--to')
777 2fe2672e Stavros Sachtouris
    )
778 edc1182f Stavros Sachtouris
    required = ('start_position', 'end_position')
779 486f7af1 Stavros Sachtouris
780 ca092af4 Stavros Sachtouris
    @errors.generic.all
781 ca092af4 Stavros Sachtouris
    @errors.pithos.connection
782 ca092af4 Stavros Sachtouris
    @errors.pithos.container
783 ca092af4 Stavros Sachtouris
    @errors.pithos.object_path
784 ca092af4 Stavros Sachtouris
    @errors.pithos.object_size
785 ca092af4 Stavros Sachtouris
    def _run(self, local_path, start, end):
786 ff1c0296 Stavros Sachtouris
        start, end = int(start), int(end)
787 ca092af4 Stavros Sachtouris
        (progress_bar, upload_cb) = self._safe_progress_bar(
788 ca092af4 Stavros Sachtouris
            'Overwrite %s bytes' % (end - start))
789 aaca2ef4 Stavros Sachtouris
        try:
790 ff1c0296 Stavros Sachtouris
            with open(path.abspath(local_path), 'rb') as f:
791 ff1c0296 Stavros Sachtouris
                self._optional_output(self.client.overwrite_object(
792 ff1c0296 Stavros Sachtouris
                    obj=self.path,
793 ff1c0296 Stavros Sachtouris
                    start=start,
794 ff1c0296 Stavros Sachtouris
                    end=end,
795 ff1c0296 Stavros Sachtouris
                    source_file=f,
796 ff1c0296 Stavros Sachtouris
                    upload_cb=upload_cb))
797 852a22e7 Stavros Sachtouris
        finally:
798 ca092af4 Stavros Sachtouris
            self._safe_progress_bar_finish(progress_bar)
799 ca092af4 Stavros Sachtouris
800 edc1182f Stavros Sachtouris
    def main(self, local_path, path_or_url):
801 edc1182f Stavros Sachtouris
        super(self.__class__, self)._run(path_or_url)
802 52edad0a Stavros Sachtouris
        self.path = self.path or path.basename(local_path)
803 edc1182f Stavros Sachtouris
        self._run(
804 edc1182f Stavros Sachtouris
            local_path=local_path,
805 edc1182f Stavros Sachtouris
            start=self['start_position'],
806 edc1182f Stavros Sachtouris
            end=self['end_position'])
807 68858765 Stavros Sachtouris
808 7493ccb6 Stavros Sachtouris
809 edc1182f Stavros Sachtouris
@command(file_cmds)
810 edc1182f Stavros Sachtouris
class file_upload(_pithos_container, _optional_output_cmd):
811 7493ccb6 Stavros Sachtouris
    """Upload a file"""
812 7493ccb6 Stavros Sachtouris
813 2fe2672e Stavros Sachtouris
    arguments = dict(
814 edc1182f Stavros Sachtouris
        max_threads=IntArgument('default: 5', '--threads'),
815 2fe2672e Stavros Sachtouris
        content_encoding=ValueArgument(
816 201baa17 Stavros Sachtouris
            'set MIME content type', '--content-encoding'),
817 2fe2672e Stavros Sachtouris
        content_disposition=ValueArgument(
818 201baa17 Stavros Sachtouris
            'specify objects presentation style', '--content-disposition'),
819 2fe2672e Stavros Sachtouris
        content_type=ValueArgument('specify content type', '--content-type'),
820 edc1182f Stavros Sachtouris
        uuid_for_read_permission=RepeatableArgument(
821 20206179 Stavros Sachtouris
            'Give read access to a user or group (can be repeated) '
822 20206179 Stavros Sachtouris
            'Use * for all users',
823 edc1182f Stavros Sachtouris
            '--read-permission'),
824 edc1182f Stavros Sachtouris
        uuid_for_write_permission=RepeatableArgument(
825 20206179 Stavros Sachtouris
            'Give write access to a user or group (can be repeated) '
826 20206179 Stavros Sachtouris
            'Use * for all users',
827 edc1182f Stavros Sachtouris
            '--write-permission'),
828 2fe2672e Stavros Sachtouris
        public=FlagArgument('make object publicly accessible', '--public'),
829 2fe2672e Stavros Sachtouris
        progress_bar=ProgressBarArgument(
830 2fe2672e Stavros Sachtouris
            'do not show progress bar',
831 8741c407 Stavros Sachtouris
            ('-N', '--no-progress-bar'),
832 706fc940 Stavros Sachtouris
            default=False),
833 2d158d8b Stavros Sachtouris
        overwrite=FlagArgument('Force (over)write', ('-f', '--force')),
834 2d158d8b Stavros Sachtouris
        recursive=FlagArgument(
835 2d158d8b Stavros Sachtouris
            'Recursively upload directory *contents* + subdirectories',
836 edc1182f Stavros Sachtouris
            ('-r', '--recursive')),
837 edc1182f Stavros Sachtouris
        unchunked=FlagArgument(
838 edc1182f Stavros Sachtouris
            'Upload file as one block (not recommended)', '--unchunked'),
839 edc1182f Stavros Sachtouris
        md5_checksum=ValueArgument(
840 edc1182f Stavros Sachtouris
            'Confirm upload with a custom checksum (MD5)', '--etag'),
841 edc1182f Stavros Sachtouris
        use_hashes=FlagArgument(
842 edc1182f Stavros Sachtouris
            'Source file contains hashmap not data', '--source-is-hashmap'),
843 2fe2672e Stavros Sachtouris
    )
844 7493ccb6 Stavros Sachtouris
845 edc1182f Stavros Sachtouris
    def _sharing(self):
846 edc1182f Stavros Sachtouris
        sharing = dict()
847 edc1182f Stavros Sachtouris
        readlist = self['uuid_for_read_permission']
848 edc1182f Stavros Sachtouris
        if readlist:
849 edc1182f Stavros Sachtouris
            sharing['read'] = self['uuid_for_read_permission']
850 edc1182f Stavros Sachtouris
        writelist = self['uuid_for_write_permission']
851 edc1182f Stavros Sachtouris
        if writelist:
852 edc1182f Stavros Sachtouris
            sharing['write'] = self['uuid_for_write_permission']
853 edc1182f Stavros Sachtouris
        return sharing or None
854 edc1182f Stavros Sachtouris
855 14b25e00 Stavros Sachtouris
    def _check_container_limit(self, path):
856 14b25e00 Stavros Sachtouris
        cl_dict = self.client.get_container_limit()
857 14b25e00 Stavros Sachtouris
        container_limit = int(cl_dict['x-container-policy-quota'])
858 2b74fc1d Stavros Sachtouris
        r = self.client.container_get()
859 2b74fc1d Stavros Sachtouris
        used_bytes = sum(int(o['bytes']) for o in r.json)
860 14b25e00 Stavros Sachtouris
        path_size = get_path_size(path)
861 c8d8c6c9 Stavros Sachtouris
        if container_limit and path_size > (container_limit - used_bytes):
862 edc1182f Stavros Sachtouris
            raise CLIError(
863 edc1182f Stavros Sachtouris
                'Container %s (limit(%s) - used(%s)) < (size(%s) of %s)' % (
864 14b25e00 Stavros Sachtouris
                    self.client.container,
865 14b25e00 Stavros Sachtouris
                    format_size(container_limit),
866 2b74fc1d Stavros Sachtouris
                    format_size(used_bytes),
867 14b25e00 Stavros Sachtouris
                    format_size(path_size),
868 14b25e00 Stavros Sachtouris
                    path),
869 edc1182f Stavros Sachtouris
                details=[
870 14b25e00 Stavros Sachtouris
                    'Check accound limit: /file quota',
871 14b25e00 Stavros Sachtouris
                    'Check container limit:',
872 14b25e00 Stavros Sachtouris
                    '\t/file containerlimit get %s' % self.client.container,
873 14b25e00 Stavros Sachtouris
                    'Increase container limit:',
874 14b25e00 Stavros Sachtouris
                    '\t/file containerlimit set <new limit> %s' % (
875 14b25e00 Stavros Sachtouris
                        self.client.container)])
876 14b25e00 Stavros Sachtouris
877 edc1182f Stavros Sachtouris
    def _src_dst(self, local_path, remote_path, objlist=None):
878 b666ef82 Stavros Sachtouris
        lpath = path.abspath(local_path)
879 edc1182f Stavros Sachtouris
        short_path = path.basename(path.abspath(local_path))
880 b666ef82 Stavros Sachtouris
        rpath = remote_path or short_path
881 b666ef82 Stavros Sachtouris
        if path.isdir(lpath):
882 2d158d8b Stavros Sachtouris
            if not self['recursive']:
883 edc1182f Stavros Sachtouris
                raise CLIError('%s is a directory' % lpath, details=[
884 eb647cfe Stavros Sachtouris
                    'Use %s to upload directories & contents' % (
885 eb647cfe Stavros Sachtouris
                        self.arguments['recursive'].lvalue)])
886 b666ef82 Stavros Sachtouris
            robj = self.client.container_get(path=rpath)
887 b666ef82 Stavros Sachtouris
            if not self['overwrite']:
888 edc1182f Stavros Sachtouris
                if robj.json:
889 edc1182f Stavros Sachtouris
                    raise CLIError(
890 edc1182f Stavros Sachtouris
                        'Objects/files prefixed as %s already exist' % rpath,
891 7b109aa7 Stavros Sachtouris
                        details=['Existing objects:'] + ['\t/%s/\t%s' % (
892 edc1182f Stavros Sachtouris
                            o['name'],
893 edc1182f Stavros Sachtouris
                            o['content_type'][12:]) for o in robj.json] + [
894 edc1182f Stavros Sachtouris
                            'Use -f to add, overwrite or resume'])
895 edc1182f Stavros Sachtouris
                else:
896 edc1182f Stavros Sachtouris
                    try:
897 edc1182f Stavros Sachtouris
                        topobj = self.client.get_object_info(rpath)
898 edc1182f Stavros Sachtouris
                        if not self._is_dir(topobj):
899 edc1182f Stavros Sachtouris
                            raise CLIError(
900 edc1182f Stavros Sachtouris
                                'Object /%s/%s exists but not a directory' % (
901 edc1182f Stavros Sachtouris
                                    self.container, rpath),
902 edc1182f Stavros Sachtouris
                                details=['Use -f to overwrite'])
903 edc1182f Stavros Sachtouris
                    except ClientError as ce:
904 edc1182f Stavros Sachtouris
                        if ce.status not in (404, ):
905 edc1182f Stavros Sachtouris
                            raise
906 2d158d8b Stavros Sachtouris
            self._check_container_limit(lpath)
907 b666ef82 Stavros Sachtouris
            prev = ''
908 b666ef82 Stavros Sachtouris
            for top, subdirs, files in walk(lpath):
909 b666ef82 Stavros Sachtouris
                if top != prev:
910 b666ef82 Stavros Sachtouris
                    prev = top
911 b666ef82 Stavros Sachtouris
                    try:
912 b666ef82 Stavros Sachtouris
                        rel_path = rpath + top.split(lpath)[1]
913 b666ef82 Stavros Sachtouris
                    except IndexError:
914 b666ef82 Stavros Sachtouris
                        rel_path = rpath
915 7b109aa7 Stavros Sachtouris
                    self.error('mkdir /%s/%s' % (
916 ff1c0296 Stavros Sachtouris
                        self.client.container, rel_path))
917 b666ef82 Stavros Sachtouris
                    self.client.create_directory(rel_path)
918 b666ef82 Stavros Sachtouris
                for f in files:
919 b666ef82 Stavros Sachtouris
                    fpath = path.join(top, f)
920 b666ef82 Stavros Sachtouris
                    if path.isfile(fpath):
921 14d15eca Stavros Sachtouris
                        rel_path = rel_path.replace(path.sep, '/')
922 14d15eca Stavros Sachtouris
                        pathfix = f.replace(path.sep, '/')
923 14d15eca Stavros Sachtouris
                        yield open(fpath, 'rb'), '%s/%s' % (rel_path, pathfix)
924 b666ef82 Stavros Sachtouris
                    else:
925 ff1c0296 Stavros Sachtouris
                        self.error('%s is not a regular file' % fpath)
926 b666ef82 Stavros Sachtouris
        else:
927 14b25e00 Stavros Sachtouris
            if not path.isfile(lpath):
928 edc1182f Stavros Sachtouris
                raise CLIError(('%s is not a regular file' % lpath) if (
929 a0a61f30 Stavros Sachtouris
                    path.exists(lpath)) else '%s does not exist' % lpath)
930 b666ef82 Stavros Sachtouris
            try:
931 b666ef82 Stavros Sachtouris
                robj = self.client.get_object_info(rpath)
932 b666ef82 Stavros Sachtouris
                if remote_path and self._is_dir(robj):
933 14d15eca Stavros Sachtouris
                    rpath += '/%s' % (short_path.replace(path.sep, '/'))
934 b666ef82 Stavros Sachtouris
                    self.client.get_object_info(rpath)
935 b666ef82 Stavros Sachtouris
                if not self['overwrite']:
936 edc1182f Stavros Sachtouris
                    raise CLIError(
937 edc1182f Stavros Sachtouris
                        'Object /%s/%s already exists' % (
938 edc1182f Stavros Sachtouris
                            self.container, rpath),
939 edc1182f Stavros Sachtouris
                        details=['use -f to overwrite / resume'])
940 b666ef82 Stavros Sachtouris
            except ClientError as ce:
941 ff1c0296 Stavros Sachtouris
                if ce.status not in (404, ):
942 b666ef82 Stavros Sachtouris
                    raise
943 2d158d8b Stavros Sachtouris
            self._check_container_limit(lpath)
944 b666ef82 Stavros Sachtouris
            yield open(lpath, 'rb'), rpath
945 706fc940 Stavros Sachtouris
946 68858765 Stavros Sachtouris
    def _run(self, local_path, remote_path):
947 ec5d658f Stavros Sachtouris
        self.client.MAX_THREADS = int(self['max_threads'] or 5)
948 72952f4f Stavros Sachtouris
        params = dict(
949 72952f4f Stavros Sachtouris
            content_encoding=self['content_encoding'],
950 2fe2672e Stavros Sachtouris
            content_type=self['content_type'],
951 2fe2672e Stavros Sachtouris
            content_disposition=self['content_disposition'],
952 edc1182f Stavros Sachtouris
            sharing=self._sharing(),
953 2fe2672e Stavros Sachtouris
            public=self['public'])
954 edc1182f Stavros Sachtouris
        uploaded, container_info_cache = list, dict()
955 edc1182f Stavros Sachtouris
        rpref = 'pithos://%s' if self['account'] else ''
956 edc1182f Stavros Sachtouris
        for f, rpath in self._src_dst(local_path, remote_path):
957 edc1182f Stavros Sachtouris
            self.error('%s --> %s/%s/%s' % (
958 edc1182f Stavros Sachtouris
                f.name, rpref, self.client.container, rpath))
959 f8426b5c Stavros Sachtouris
            if not (self['content_type'] and self['content_encoding']):
960 f8426b5c Stavros Sachtouris
                ctype, cenc = guess_mime_type(f.name)
961 f8426b5c Stavros Sachtouris
                params['content_type'] = self['content_type'] or ctype
962 f8426b5c Stavros Sachtouris
                params['content_encoding'] = self['content_encoding'] or cenc
963 68858765 Stavros Sachtouris
            if self['unchunked']:
964 0e728dcb Stavros Sachtouris
                r = self.client.upload_object_unchunked(
965 b666ef82 Stavros Sachtouris
                    rpath, f,
966 edc1182f Stavros Sachtouris
                    etag=self['md5_checksum'], withHashFile=self['use_hashes'],
967 68858765 Stavros Sachtouris
                    **params)
968 8fb0acd2 Stavros Sachtouris
                if self['with_output'] or self['json_output']:
969 edc1182f Stavros Sachtouris
                    r['name'] = '/%s/%s' % (self.client.container, rpath)
970 0e728dcb Stavros Sachtouris
                    uploaded.append(r)
971 68858765 Stavros Sachtouris
            else:
972 68858765 Stavros Sachtouris
                try:
973 68858765 Stavros Sachtouris
                    (progress_bar, upload_cb) = self._safe_progress_bar(
974 b666ef82 Stavros Sachtouris
                        'Uploading %s' % f.name.split(path.sep)[-1])
975 68858765 Stavros Sachtouris
                    if progress_bar:
976 68858765 Stavros Sachtouris
                        hash_bar = progress_bar.clone()
977 68858765 Stavros Sachtouris
                        hash_cb = hash_bar.get_generator(
978 278c9018 Stavros Sachtouris
                            'Calculating block hashes')
979 68858765 Stavros Sachtouris
                    else:
980 68858765 Stavros Sachtouris
                        hash_cb = None
981 0e728dcb Stavros Sachtouris
                    r = self.client.upload_object(
982 b666ef82 Stavros Sachtouris
                        rpath, f,
983 74c65e80 Stavros Sachtouris
                        hash_cb=hash_cb,
984 74c65e80 Stavros Sachtouris
                        upload_cb=upload_cb,
985 74c65e80 Stavros Sachtouris
                        container_info_cache=container_info_cache,
986 852a22e7 Stavros Sachtouris
                        **params)
987 8fb0acd2 Stavros Sachtouris
                    if self['with_output'] or self['json_output']:
988 edc1182f Stavros Sachtouris
                        r['name'] = '/%s/%s' % (self.client.container, rpath)
989 0e728dcb Stavros Sachtouris
                        uploaded.append(r)
990 68858765 Stavros Sachtouris
                except Exception:
991 68858765 Stavros Sachtouris
                    self._safe_progress_bar_finish(progress_bar)
992 68858765 Stavros Sachtouris
                    raise
993 68858765 Stavros Sachtouris
                finally:
994 68858765 Stavros Sachtouris
                    self._safe_progress_bar_finish(progress_bar)
995 d9301a7a Stavros Sachtouris
        self._optional_output(uploaded)
996 ff1c0296 Stavros Sachtouris
        self.error('Upload completed')
997 7493ccb6 Stavros Sachtouris
998 edc1182f Stavros Sachtouris
    def main(self, local_path, remote_path_or_url):
999 edc1182f Stavros Sachtouris
        super(self.__class__, self)._run(remote_path_or_url)
1000 9e0a1a4c Stavros Sachtouris
        remote_path = self.path or path.basename(path.abspath(local_path))
1001 68858765 Stavros Sachtouris
        self._run(local_path=local_path, remote_path=remote_path)
1002 68858765 Stavros Sachtouris
1003 234954d1 Stavros Sachtouris
1004 edc1182f Stavros Sachtouris
class RangeArgument(ValueArgument):
1005 edc1182f Stavros Sachtouris
    """
1006 edc1182f Stavros Sachtouris
    :value type: string of the form <start>-<end> where <start> and <end> are
1007 edc1182f Stavros Sachtouris
        integers
1008 edc1182f Stavros Sachtouris
    :value returns: the input string, after type checking <start> and <end>
1009 edc1182f Stavros Sachtouris
    """
1010 edc1182f Stavros Sachtouris
1011 edc1182f Stavros Sachtouris
    @property
1012 edc1182f Stavros Sachtouris
    def value(self):
1013 edc1182f Stavros Sachtouris
        return getattr(self, '_value', self.default)
1014 edc1182f Stavros Sachtouris
1015 edc1182f Stavros Sachtouris
    @value.setter
1016 edc1182f Stavros Sachtouris
    def value(self, newvalues):
1017 edc1182f Stavros Sachtouris
        if newvalues:
1018 edc1182f Stavros Sachtouris
            self._value = getattr(self, '_value', self.default)
1019 edc1182f Stavros Sachtouris
            for newvalue in newvalues.split(','):
1020 edc1182f Stavros Sachtouris
                self._value = ('%s,' % self._value) if self._value else ''
1021 edc1182f Stavros Sachtouris
                start, sep, end = newvalue.partition('-')
1022 edc1182f Stavros Sachtouris
                if sep:
1023 edc1182f Stavros Sachtouris
                    if start:
1024 edc1182f Stavros Sachtouris
                        start, end = (int(start), int(end))
1025 edc1182f Stavros Sachtouris
                        if start > end:
1026 edc1182f Stavros Sachtouris
                            raise CLIInvalidArgument(
1027 edc1182f Stavros Sachtouris
                                'Invalid range %s' % newvalue, details=[
1028 edc1182f Stavros Sachtouris
                                'Valid range formats',
1029 edc1182f Stavros Sachtouris
                                '  START-END', '  UP_TO', '  -FROM',
1030 c6ea94ac Stavros Sachtouris
                                'where all values are integers',
1031 c6ea94ac Stavros Sachtouris
                                'OR a compination (csv), e.g.,',
1032 c6ea94ac Stavros Sachtouris
                                '  %s=5,10-20,-5' % self.lvalue])
1033 edc1182f Stavros Sachtouris
                        self._value += '%s-%s' % (start, end)
1034 edc1182f Stavros Sachtouris
                    else:
1035 edc1182f Stavros Sachtouris
                        self._value += '-%s' % int(end)
1036 edc1182f Stavros Sachtouris
                else:
1037 edc1182f Stavros Sachtouris
                    self._value += '%s' % int(start)
1038 edc1182f Stavros Sachtouris
1039 edc1182f Stavros Sachtouris
1040 edc1182f Stavros Sachtouris
@command(file_cmds)
1041 edc1182f Stavros Sachtouris
class file_cat(_pithos_container):
1042 edc1182f Stavros Sachtouris
    """Fetch remote file contents"""
1043 9ceec15a Stavros Sachtouris
1044 2fe2672e Stavros Sachtouris
    arguments = dict(
1045 c6ea94ac Stavros Sachtouris
        range=RangeArgument('show range of data e.g., 5,10-20,-5', '--range'),
1046 2fe2672e Stavros Sachtouris
        if_match=ValueArgument('show output if ETags match', '--if-match'),
1047 2fe2672e Stavros Sachtouris
        if_none_match=ValueArgument(
1048 201baa17 Stavros Sachtouris
            'show output if ETags match', '--if-none-match'),
1049 2fe2672e Stavros Sachtouris
        if_modified_since=DateArgument(
1050 201baa17 Stavros Sachtouris
            'show output modified since then', '--if-modified-since'),
1051 2fe2672e Stavros Sachtouris
        if_unmodified_since=DateArgument(
1052 201baa17 Stavros Sachtouris
            'show output unmodified since then', '--if-unmodified-since'),
1053 2fe2672e Stavros Sachtouris
        object_version=ValueArgument(
1054 edc1182f Stavros Sachtouris
            'Get contents of the chosen version', '--object-version')
1055 2fe2672e Stavros Sachtouris
    )
1056 2fe2672e Stavros Sachtouris
1057 68858765 Stavros Sachtouris
    @errors.generic.all
1058 68858765 Stavros Sachtouris
    @errors.pithos.connection
1059 68858765 Stavros Sachtouris
    @errors.pithos.container
1060 68858765 Stavros Sachtouris
    @errors.pithos.object_path
1061 68858765 Stavros Sachtouris
    def _run(self):
1062 c6ea94ac Stavros Sachtouris
        r = self.client.download_object(
1063 ff1c0296 Stavros Sachtouris
            self.path, self._out,
1064 3d568c09 Stavros Sachtouris
            range_str=self['range'],
1065 2fe2672e Stavros Sachtouris
            version=self['object_version'],
1066 2fe2672e Stavros Sachtouris
            if_match=self['if_match'],
1067 2fe2672e Stavros Sachtouris
            if_none_match=self['if_none_match'],
1068 2fe2672e Stavros Sachtouris
            if_modified_since=self['if_modified_since'],
1069 2fe2672e Stavros Sachtouris
            if_unmodified_since=self['if_unmodified_since'])
1070 c6ea94ac Stavros Sachtouris
        print r
1071 68858765 Stavros Sachtouris
1072 edc1182f Stavros Sachtouris
    def main(self, path_or_url):
1073 edc1182f Stavros Sachtouris
        super(self.__class__, self)._run(path_or_url)
1074 68858765 Stavros Sachtouris
        self._run()
1075 9ceec15a Stavros Sachtouris
1076 234954d1 Stavros Sachtouris
1077 74b7c6dc Stavros Sachtouris
@command(file_cmds)
1078 74b7c6dc Stavros Sachtouris
class file_download(_pithos_container):
1079 74b7c6dc Stavros Sachtouris
    """Download a remove file or directory object to local file system"""
1080 7493ccb6 Stavros Sachtouris
1081 2fe2672e Stavros Sachtouris
    arguments = dict(
1082 74b7c6dc Stavros Sachtouris
        resume=FlagArgument(
1083 74b7c6dc Stavros Sachtouris
            'Resume/Overwrite (attempt resume, else overwrite)',
1084 74b7c6dc Stavros Sachtouris
            ('-f', '--resume')),
1085 c6ea94ac Stavros Sachtouris
        range=RangeArgument(
1086 c6ea94ac Stavros Sachtouris
            'Download only that range of data e.g., 5,10-20,-5', '--range'),
1087 74b7c6dc Stavros Sachtouris
        matching_etag=ValueArgument('download iff ETag match', '--if-match'),
1088 74b7c6dc Stavros Sachtouris
        non_matching_etag=ValueArgument(
1089 74b7c6dc Stavros Sachtouris
            'download iff ETags DO NOT match', '--if-none-match'),
1090 74b7c6dc Stavros Sachtouris
        modified_since_date=DateArgument(
1091 74b7c6dc Stavros Sachtouris
            'download iff remote file is modified since then',
1092 74b7c6dc Stavros Sachtouris
            '--if-modified-since'),
1093 74b7c6dc Stavros Sachtouris
        unmodified_since_date=DateArgument(
1094 74b7c6dc Stavros Sachtouris
            'show output iff remote file is unmodified since then',
1095 74b7c6dc Stavros Sachtouris
            '--if-unmodified-since'),
1096 2fe2672e Stavros Sachtouris
        object_version=ValueArgument(
1097 20206179 Stavros Sachtouris
            'download a file of a specific version', '--object-version'),
1098 74b7c6dc Stavros Sachtouris
        max_threads=IntArgument('default: 5', '--threads'),
1099 2fe2672e Stavros Sachtouris
        progress_bar=ProgressBarArgument(
1100 ff1c0296 Stavros Sachtouris
            'do not show progress bar', ('-N', '--no-progress-bar'),
1101 ae99b37d Stavros Sachtouris
            default=False),
1102 f6c09d14 Stavros Sachtouris
        recursive=FlagArgument(
1103 74b7c6dc Stavros Sachtouris
            'Download a remote directory object and its contents',
1104 74b7c6dc Stavros Sachtouris
            ('-r', '--recursive'))
1105 74b7c6dc Stavros Sachtouris
        )
1106 74b7c6dc Stavros Sachtouris
1107 74b7c6dc Stavros Sachtouris
    def _src_dst(self, local_path):
1108 74b7c6dc Stavros Sachtouris
        """Create a list of (src, dst) where src is a remote location and dst
1109 74b7c6dc Stavros Sachtouris
        is an open file descriptor. Directories are denoted as (None, dirpath)
1110 74b7c6dc Stavros Sachtouris
        and they are pretended to other objects in a very strict order (shorter
1111 74b7c6dc Stavros Sachtouris
        to longer path)."""
1112 74b7c6dc Stavros Sachtouris
        ret = []
1113 74b7c6dc Stavros Sachtouris
        try:
1114 a3ba3bce Stavros Sachtouris
            if self.path:
1115 606f5b54 Stavros Sachtouris
                obj = self.client.get_object_info(
1116 606f5b54 Stavros Sachtouris
                    self.path, version=self['object_version'])
1117 606f5b54 Stavros Sachtouris
                obj.setdefault('name', self.path.strip('/'))
1118 606f5b54 Stavros Sachtouris
            else:
1119 606f5b54 Stavros Sachtouris
                obj = None
1120 74b7c6dc Stavros Sachtouris
        except ClientError as ce:
1121 74b7c6dc Stavros Sachtouris
            if ce.status in (404, ):
1122 74b7c6dc Stavros Sachtouris
                raiseCLIError(ce, details=[
1123 606f5b54 Stavros Sachtouris
                    'To download an object, it must exist either as a file or'
1124 606f5b54 Stavros Sachtouris
                    ' as a directory.',
1125 74b7c6dc Stavros Sachtouris
                    'For example, to download everything under prefix/ the '
1126 74b7c6dc Stavros Sachtouris
                    'directory "prefix" must exist.',
1127 74b7c6dc Stavros Sachtouris
                    'To see if an remote object is actually there:',
1128 74b7c6dc Stavros Sachtouris
                    '  /file info [/CONTAINER/]OBJECT',
1129 74b7c6dc Stavros Sachtouris
                    'To create a directory object:',
1130 74b7c6dc Stavros Sachtouris
                    '  /file mkdir [/CONTAINER/]OBJECT'])
1131 74b7c6dc Stavros Sachtouris
            if ce.status in (204, ):
1132 74b7c6dc Stavros Sachtouris
                raise CLIError(
1133 74b7c6dc Stavros Sachtouris
                    'No file or directory objects to download',
1134 a3ba3bce Stavros Sachtouris
                    details=[
1135 74b7c6dc Stavros Sachtouris
                        'To download a container (e.g., %s):' % self.container,
1136 74b7c6dc Stavros Sachtouris
                        '  [kamaki] container download %s [LOCAL_PATH]' % (
1137 74b7c6dc Stavros Sachtouris
                            self.container)])
1138 74b7c6dc Stavros Sachtouris
            raise
1139 74b7c6dc Stavros Sachtouris
        rpath = self.path.strip('/')
1140 606f5b54 Stavros Sachtouris
        if local_path and self.path and local_path.endswith('/'):
1141 606f5b54 Stavros Sachtouris
            local_path = local_path[-1:]
1142 74b7c6dc Stavros Sachtouris
1143 606f5b54 Stavros Sachtouris
        if (not obj) or self._is_dir(obj):
1144 74b7c6dc Stavros Sachtouris
            if self['recursive']:
1145 606f5b54 Stavros Sachtouris
                if not (self.path or local_path.endswith('/')):
1146 606f5b54 Stavros Sachtouris
                    #  Download the whole container
1147 606f5b54 Stavros Sachtouris
                    local_path = '' if local_path in ('.', ) else local_path
1148 606f5b54 Stavros Sachtouris
                    local_path = '%s/' % (local_path or self.container)
1149 606f5b54 Stavros Sachtouris
                obj = obj or dict(
1150 606f5b54 Stavros Sachtouris
                    name='', content_type='application/directory')
1151 74b7c6dc Stavros Sachtouris
                dirs, files = [obj, ], []
1152 74b7c6dc Stavros Sachtouris
                objects = self.client.container_get(
1153 606f5b54 Stavros Sachtouris
                    path=self.path,
1154 74b7c6dc Stavros Sachtouris
                    if_modified_since=self['modified_since_date'],
1155 74b7c6dc Stavros Sachtouris
                    if_unmodified_since=self['unmodified_since_date'])
1156 606f5b54 Stavros Sachtouris
                for o in objects.json:
1157 606f5b54 Stavros Sachtouris
                    (dirs if self._is_dir(o) else files).append(o)
1158 74b7c6dc Stavros Sachtouris
1159 74b7c6dc Stavros Sachtouris
                #  Put the directories on top of the list
1160 74b7c6dc Stavros Sachtouris
                for dpath in sorted(['%s%s' % (
1161 74b7c6dc Stavros Sachtouris
                        local_path, d['name'][len(rpath):]) for d in dirs]):
1162 74b7c6dc Stavros Sachtouris
                    if path.exists(dpath):
1163 74b7c6dc Stavros Sachtouris
                        if path.isdir(dpath):
1164 74b7c6dc Stavros Sachtouris
                            continue
1165 74b7c6dc Stavros Sachtouris
                        raise CLIError(
1166 74b7c6dc Stavros Sachtouris
                            'Cannot replace local file %s with a directory '
1167 74b7c6dc Stavros Sachtouris
                            'of the same name' % dpath,
1168 74b7c6dc Stavros Sachtouris
                            details=[
1169 74b7c6dc Stavros Sachtouris
                                'Either remove the file or specify a'
1170 74b7c6dc Stavros Sachtouris
                                'different target location'])
1171 74b7c6dc Stavros Sachtouris
                    ret.append((None, dpath, None))
1172 74b7c6dc Stavros Sachtouris
1173 74b7c6dc Stavros Sachtouris
                #  Append the file objects
1174 74b7c6dc Stavros Sachtouris
                for opath in [o['name'] for o in files]:
1175 74b7c6dc Stavros Sachtouris
                    lpath = '%s%s' % (local_path, opath[len(rpath):])
1176 74b7c6dc Stavros Sachtouris
                    if self['resume']:
1177 74b7c6dc Stavros Sachtouris
                        fxists = path.exists(lpath)
1178 74b7c6dc Stavros Sachtouris
                        if fxists and path.isdir(lpath):
1179 74b7c6dc Stavros Sachtouris
                            raise CLIError(
1180 74b7c6dc Stavros Sachtouris
                                'Cannot change local dir %s info file' % (
1181 74b7c6dc Stavros Sachtouris
                                    lpath),
1182 74b7c6dc Stavros Sachtouris
                                details=[
1183 74b7c6dc Stavros Sachtouris
                                    'Either remove the file or specify a'
1184 74b7c6dc Stavros Sachtouris
                                    'different target location'])
1185 74b7c6dc Stavros Sachtouris
                        ret.append((opath, lpath, fxists))
1186 74b7c6dc Stavros Sachtouris
                    elif path.exists(lpath):
1187 74b7c6dc Stavros Sachtouris
                        raise CLIError(
1188 74b7c6dc Stavros Sachtouris
                            'Cannot overwrite %s' % lpath,
1189 eb647cfe Stavros Sachtouris
                            details=['To overwrite/resume, use  %s' % (
1190 eb647cfe Stavros Sachtouris
                                self.arguments['resume'].lvalue)])
1191 74b7c6dc Stavros Sachtouris
                    else:
1192 74b7c6dc Stavros Sachtouris
                        ret.append((opath, lpath, None))
1193 606f5b54 Stavros Sachtouris
            elif self.path:
1194 74b7c6dc Stavros Sachtouris
                raise CLIError(
1195 74b7c6dc Stavros Sachtouris
                    'Remote object /%s/%s is a directory' % (
1196 74b7c6dc Stavros Sachtouris
                        self.container, local_path),
1197 eb647cfe Stavros Sachtouris
                    details=['Use %s to download directories' % (
1198 eb647cfe Stavros Sachtouris
                        self.arguments['recursive'].lvalue)])
1199 a3ba3bce Stavros Sachtouris
            else:
1200 eb647cfe Stavros Sachtouris
                parsed_name = self.arguments['recursive'].lvalue
1201 606f5b54 Stavros Sachtouris
                raise CLIError(
1202 606f5b54 Stavros Sachtouris
                    'Cannot download container %s' % self.container,
1203 606f5b54 Stavros Sachtouris
                    details=[
1204 606f5b54 Stavros Sachtouris
                        'Use %s to download containers' % parsed_name,
1205 606f5b54 Stavros Sachtouris
                        '  [kamaki] file download %s /%s [LOCAL_PATH]' % (
1206 606f5b54 Stavros Sachtouris
                            parsed_name, self.container)])
1207 a3ba3bce Stavros Sachtouris
        else:
1208 74b7c6dc Stavros Sachtouris
            #  Remote object is just a file
1209 60bcb377 Stavros Sachtouris
            if path.exists(local_path):
1210 60bcb377 Stavros Sachtouris
                if not self['resume']:
1211 60bcb377 Stavros Sachtouris
                    raise CLIError(
1212 60bcb377 Stavros Sachtouris
                        'Cannot overwrite local file %s' % (local_path),
1213 60bcb377 Stavros Sachtouris
                        details=['To overwrite/resume, use  %s' % (
1214 60bcb377 Stavros Sachtouris
                            self.arguments['resume'].lvalue)])
1215 60bcb377 Stavros Sachtouris
            elif '/' in local_path[1:-1]:
1216 60bcb377 Stavros Sachtouris
                dirs = [p for p in local_path.split('/') if p]
1217 60bcb377 Stavros Sachtouris
                pref = '/' if local_path.startswith('/') else ''
1218 60bcb377 Stavros Sachtouris
                for d in dirs[:-1]:
1219 60bcb377 Stavros Sachtouris
                    pref += d
1220 60bcb377 Stavros Sachtouris
                    if not path.exists(pref):
1221 60bcb377 Stavros Sachtouris
                        ret.append((None, d, None))
1222 60bcb377 Stavros Sachtouris
                    elif not path.isdir(pref):
1223 60bcb377 Stavros Sachtouris
                        raise CLIError(
1224 60bcb377 Stavros Sachtouris
                            'Failed to use %s as a destination' % local_path,
1225 60bcb377 Stavros Sachtouris
                            importance=3,
1226 60bcb377 Stavros Sachtouris
                            details=[
1227 60bcb377 Stavros Sachtouris
                                'Local file %s is not a directory' % pref,
1228 60bcb377 Stavros Sachtouris
                                'Destination prefix must consist of '
1229 60bcb377 Stavros Sachtouris
                                'directories or non-existing names',
1230 60bcb377 Stavros Sachtouris
                                'Either remove the file, or choose another '
1231 60bcb377 Stavros Sachtouris
                                'destination'])
1232 74b7c6dc Stavros Sachtouris
            ret.append((rpath, local_path, self['resume']))
1233 74b7c6dc Stavros Sachtouris
        for r, l, resume in ret:
1234 74b7c6dc Stavros Sachtouris
            if r:
1235 74b7c6dc Stavros Sachtouris
                with open(l, 'rwb+' if resume else 'wb+') as f:
1236 74b7c6dc Stavros Sachtouris
                    yield (r, f)
1237 74b7c6dc Stavros Sachtouris
            else:
1238 74b7c6dc Stavros Sachtouris
                yield (r, l)
1239 7493ccb6 Stavros Sachtouris
1240 68858765 Stavros Sachtouris
    @errors.generic.all
1241 68858765 Stavros Sachtouris
    @errors.pithos.connection
1242 68858765 Stavros Sachtouris
    @errors.pithos.container
1243 68858765 Stavros Sachtouris
    @errors.pithos.object_path
1244 68858765 Stavros Sachtouris
    @errors.pithos.local_path
1245 606f5b54 Stavros Sachtouris
    @errors.pithos.local_path_download
1246 68858765 Stavros Sachtouris
    def _run(self, local_path):
1247 ec5d658f Stavros Sachtouris
        self.client.MAX_THREADS = int(self['max_threads'] or 5)
1248 f6c09d14 Stavros Sachtouris
        progress_bar = None
1249 ae99b37d Stavros Sachtouris
        try:
1250 74b7c6dc Stavros Sachtouris
            for rpath, output_file in self._src_dst(local_path):
1251 74b7c6dc Stavros Sachtouris
                if not rpath:
1252 74b7c6dc Stavros Sachtouris
                    self.error('Create local directory %s' % output_file)
1253 74b7c6dc Stavros Sachtouris
                    makedirs(output_file)
1254 74b7c6dc Stavros Sachtouris
                    continue
1255 74b7c6dc Stavros Sachtouris
                self.error('/%s/%s --> %s' % (
1256 74b7c6dc Stavros Sachtouris
                    self.container, rpath, output_file.name))
1257 74b7c6dc Stavros Sachtouris
                progress_bar, download_cb = self._safe_progress_bar(
1258 74b7c6dc Stavros Sachtouris
                    '  download')
1259 ae99b37d Stavros Sachtouris
                self.client.download_object(
1260 74b7c6dc Stavros Sachtouris
                    rpath, output_file,
1261 ae99b37d Stavros Sachtouris
                    download_cb=download_cb,
1262 3d568c09 Stavros Sachtouris
                    range_str=self['range'],
1263 ae99b37d Stavros Sachtouris
                    version=self['object_version'],
1264 74b7c6dc Stavros Sachtouris
                    if_match=self['matching_etag'],
1265 ae99b37d Stavros Sachtouris
                    resume=self['resume'],
1266 74b7c6dc Stavros Sachtouris
                    if_none_match=self['non_matching_etag'],
1267 74b7c6dc Stavros Sachtouris
                    if_modified_since=self['modified_since_date'],
1268 74b7c6dc Stavros Sachtouris
                    if_unmodified_since=self['unmodified_since_date'])
1269 7493ccb6 Stavros Sachtouris
        except KeyboardInterrupt:
1270 b78ee581 Stavros Sachtouris
            from threading import activeCount, enumerate as activethreads
1271 cae76f25 Stavros Sachtouris
            timeout = 0.5
1272 b78ee581 Stavros Sachtouris
            while activeCount() > 1:
1273 1757c616 Stavros Sachtouris
                self._out.write('\nCancel %s threads: ' % (activeCount() - 1))
1274 1757c616 Stavros Sachtouris
                self._out.flush()
1275 b78ee581 Stavros Sachtouris
                for thread in activethreads():
1276 b78ee581 Stavros Sachtouris
                    try:
1277 cae76f25 Stavros Sachtouris
                        thread.join(timeout)
1278 1757c616 Stavros Sachtouris
                        self._out.write('.' if thread.isAlive() else '*')
1279 b78ee581 Stavros Sachtouris
                    except RuntimeError:
1280 b78ee581 Stavros Sachtouris
                        continue
1281 cae76f25 Stavros Sachtouris
                    finally:
1282 1757c616 Stavros Sachtouris
                        self._out.flush()
1283 cae76f25 Stavros Sachtouris
                        timeout += 0.1
1284 ff1c0296 Stavros Sachtouris
            self.error('\nDownload canceled by user')
1285 7493ccb6 Stavros Sachtouris
            if local_path is not None:
1286 ff1c0296 Stavros Sachtouris
                self.error('to resume, re-run with --resume')
1287 68858765 Stavros Sachtouris
        finally:
1288 68858765 Stavros Sachtouris
            self._safe_progress_bar_finish(progress_bar)
1289 68858765 Stavros Sachtouris
1290 74b7c6dc Stavros Sachtouris
    def main(self, remote_path_or_url, local_path=None):
1291 74b7c6dc Stavros Sachtouris
        super(self.__class__, self)._run(remote_path_or_url)
1292 74b7c6dc Stavros Sachtouris
        local_path = local_path or self.path or '.'
1293 68858765 Stavros Sachtouris
        self._run(local_path=local_path)
1294 7493ccb6 Stavros Sachtouris
1295 234954d1 Stavros Sachtouris
1296 7b109aa7 Stavros Sachtouris
@command(container_cmds)
1297 7b109aa7 Stavros Sachtouris
class container_info(_pithos_account, _optional_json):
1298 7b109aa7 Stavros Sachtouris
    """Get information about a container"""
1299 7493ccb6 Stavros Sachtouris
1300 2fe2672e Stavros Sachtouris
    arguments = dict(
1301 7b109aa7 Stavros Sachtouris
        until_date=DateArgument('show metadata until then', '--until'),
1302 7b109aa7 Stavros Sachtouris
        metadata=FlagArgument('Show only container metadata', '--metadata'),
1303 7b109aa7 Stavros Sachtouris
        sizelimit=FlagArgument(
1304 7b109aa7 Stavros Sachtouris
            'Show the maximum size limit for container', '--size-limit'),
1305 7b109aa7 Stavros Sachtouris
        in_bytes=FlagArgument('Show size limit in bytes', ('-b', '--bytes'))
1306 2fe2672e Stavros Sachtouris
    )
1307 7493ccb6 Stavros Sachtouris
1308 b4cf92b8 Stavros Sachtouris
    @errors.generic.all
1309 b4cf92b8 Stavros Sachtouris
    @errors.pithos.connection
1310 b4cf92b8 Stavros Sachtouris
    @errors.pithos.container
1311 b4cf92b8 Stavros Sachtouris
    @errors.pithos.object_path
1312 b4cf92b8 Stavros Sachtouris
    def _run(self):
1313 7b109aa7 Stavros Sachtouris
        if self['metadata']:
1314 7b109aa7 Stavros Sachtouris
            r, preflen = dict(), len('x-container-meta-')
1315 7b109aa7 Stavros Sachtouris
            for k, v in self.client.get_container_meta(
1316 7b109aa7 Stavros Sachtouris
                    until=self['until_date']).items():
1317 7b109aa7 Stavros Sachtouris
                r[k[preflen:]] = v
1318 7b109aa7 Stavros Sachtouris
        elif self['sizelimit']:
1319 7b109aa7 Stavros Sachtouris
            r = self.client.get_container_limit(
1320 7b109aa7 Stavros Sachtouris
                self.container)['x-container-policy-quota']
1321 7b109aa7 Stavros Sachtouris
            r = {'size limit': 'unlimited' if r in ('0', ) else (
1322 7b109aa7 Stavros Sachtouris
                int(r) if self['in_bytes'] else format_size(r))}
1323 76f58e2e Stavros Sachtouris
        else:
1324 7b109aa7 Stavros Sachtouris
            r = self.client.get_container_info(self.container)
1325 7b109aa7 Stavros Sachtouris
        self._print(r, self.print_dict)
1326 b4cf92b8 Stavros Sachtouris
1327 7b109aa7 Stavros Sachtouris
    def main(self, container):
1328 7b109aa7 Stavros Sachtouris
        super(self.__class__, self)._run()
1329 7b109aa7 Stavros Sachtouris
        self.container, self.client.container = container, container
1330 b4cf92b8 Stavros Sachtouris
        self._run()
1331 7493ccb6 Stavros Sachtouris
1332 234954d1 Stavros Sachtouris
1333 7b109aa7 Stavros Sachtouris
class VersioningArgument(ValueArgument):
1334 7493ccb6 Stavros Sachtouris
1335 7b109aa7 Stavros Sachtouris
    schemes = ('auto', 'none')
1336 7493ccb6 Stavros Sachtouris
1337 7b109aa7 Stavros Sachtouris
    @property
1338 7b109aa7 Stavros Sachtouris
    def value(self):
1339 7b109aa7 Stavros Sachtouris
        return getattr(self, '_value', None)
1340 b4cf92b8 Stavros Sachtouris
1341 7b109aa7 Stavros Sachtouris
    @value.setter
1342 7b109aa7 Stavros Sachtouris
    def value(self, new_scheme):
1343 7b109aa7 Stavros Sachtouris
        if new_scheme:
1344 7b109aa7 Stavros Sachtouris
            new_scheme = new_scheme.lower()
1345 7b109aa7 Stavros Sachtouris
            if new_scheme not in self.schemes:
1346 7b109aa7 Stavros Sachtouris
                raise CLIInvalidArgument('Invalid versioning value', details=[
1347 7b109aa7 Stavros Sachtouris
                    'Valid versioning values are %s' % ', '.join(
1348 7b109aa7 Stavros Sachtouris
                        self.schemes)])
1349 7b109aa7 Stavros Sachtouris
            self._value = new_scheme
1350 7493ccb6 Stavros Sachtouris
1351 234954d1 Stavros Sachtouris
1352 7b109aa7 Stavros Sachtouris
@command(container_cmds)
1353 7b109aa7 Stavros Sachtouris
class container_modify(_pithos_account, _optional_json):
1354 7b109aa7 Stavros Sachtouris
    """Modify the properties of a container"""
1355 7493ccb6 Stavros Sachtouris
1356 2fe2672e Stavros Sachtouris
    arguments = dict(
1357 7b109aa7 Stavros Sachtouris
        metadata_to_add=KeyValueArgument(
1358 7b109aa7 Stavros Sachtouris
            'Add metadata in the form KEY=VALUE (can be repeated)',
1359 7b109aa7 Stavros Sachtouris
            '--metadata-add'),
1360 7b109aa7 Stavros Sachtouris
        metadata_to_delete=RepeatableArgument(
1361 7b109aa7 Stavros Sachtouris
            'Delete metadata by KEY (can be repeated)', '--metadata-del'),
1362 7b109aa7 Stavros Sachtouris
        sizelimit=DataSizeArgument(
1363 7b109aa7 Stavros Sachtouris
            'Set max size limit (0 for unlimited, '
1364 7b109aa7 Stavros Sachtouris
            'use units B, KiB, KB, etc.)', '--size-limit'),
1365 7b109aa7 Stavros Sachtouris
        versioning=VersioningArgument(
1366 7b109aa7 Stavros Sachtouris
            'Set a versioning scheme (%s)' % ', '.join(
1367 7b109aa7 Stavros Sachtouris
                VersioningArgument.schemes), '--versioning')
1368 2fe2672e Stavros Sachtouris
    )
1369 f084bdc8 Stavros Sachtouris
    required = [
1370 f084bdc8 Stavros Sachtouris
        'metadata_to_add', 'metadata_to_delete', 'sizelimit', 'versioning']
1371 6ac7f90f Stavros Sachtouris
1372 b4cf92b8 Stavros Sachtouris
    @errors.generic.all
1373 b4cf92b8 Stavros Sachtouris
    @errors.pithos.connection
1374 b4cf92b8 Stavros Sachtouris
    @errors.pithos.container
1375 7b109aa7 Stavros Sachtouris
    def _run(self, container):
1376 7b109aa7 Stavros Sachtouris
        metadata = self['metadata_to_add']
1377 f084bdc8 Stavros Sachtouris
        for k in (self['metadata_to_delete'] or []):
1378 7b109aa7 Stavros Sachtouris
            metadata[k] = ''
1379 7b109aa7 Stavros Sachtouris
        if metadata:
1380 7b109aa7 Stavros Sachtouris
            self.client.set_container_meta(metadata)
1381 7b109aa7 Stavros Sachtouris
            self._print(self.client.get_container_meta(), self.print_dict)
1382 7b109aa7 Stavros Sachtouris
        if self['sizelimit'] is not None:
1383 7b109aa7 Stavros Sachtouris
            self.client.set_container_limit(self['sizelimit'])
1384 7b109aa7 Stavros Sachtouris
            r = self.client.get_container_limit()['x-container-policy-quota']
1385 7b109aa7 Stavros Sachtouris
            r = 'unlimited' if r in ('0', ) else format_size(r)
1386 7b109aa7 Stavros Sachtouris
            self.writeln('new size limit: %s' % r)
1387 7b109aa7 Stavros Sachtouris
        if self['versioning']:
1388 7b109aa7 Stavros Sachtouris
            self.client.set_container_versioning(self['versioning'])
1389 7b109aa7 Stavros Sachtouris
            self.writeln('new versioning scheme: %s' % (
1390 7b109aa7 Stavros Sachtouris
                self.client.get_container_versioning(self.container)[
1391 7b109aa7 Stavros Sachtouris
                    'x-container-policy-versioning']))
1392 234954d1 Stavros Sachtouris
1393 7b109aa7 Stavros Sachtouris
    def main(self, container):
1394 7b109aa7 Stavros Sachtouris
        super(self.__class__, self)._run()
1395 7b109aa7 Stavros Sachtouris
        self.client.container, self.container = container, container
1396 7b109aa7 Stavros Sachtouris
        self._run(container=container)
1397 5655d560 Stavros Sachtouris
1398 5655d560 Stavros Sachtouris
1399 7b109aa7 Stavros Sachtouris
@command(container_cmds)
1400 7b109aa7 Stavros Sachtouris
class container_list(_pithos_account, _optional_json, _name_filter):
1401 7b109aa7 Stavros Sachtouris
    """List all containers, or their contents"""
1402 7493ccb6 Stavros Sachtouris
1403 2fe2672e Stavros Sachtouris
    arguments = dict(
1404 7b109aa7 Stavros Sachtouris
        detail=FlagArgument('Containers with details', ('-l', '--list')),
1405 7b109aa7 Stavros Sachtouris
        limit=IntArgument('limit number of listed items', ('-n', '--number')),
1406 7b109aa7 Stavros Sachtouris
        marker=ValueArgument('output greater that marker', '--marker'),
1407 7b109aa7 Stavros Sachtouris
        modified_since_date=ValueArgument(
1408 7b109aa7 Stavros Sachtouris
            'show output modified since then', '--if-modified-since'),
1409 7b109aa7 Stavros Sachtouris
        unmodified_since_date=ValueArgument(
1410 7b109aa7 Stavros Sachtouris
            'show output not modified since then', '--if-unmodified-since'),
1411 7b109aa7 Stavros Sachtouris
        until_date=DateArgument('show metadata until then', '--until'),
1412 7b109aa7 Stavros Sachtouris
        shared=FlagArgument('show only shared', '--shared'),
1413 7b109aa7 Stavros Sachtouris
        more=FlagArgument('read long results', '--more'),
1414 7b109aa7 Stavros Sachtouris
        enum=FlagArgument('Enumerate results', '--enumerate'),
1415 7b109aa7 Stavros Sachtouris
        recursive=FlagArgument(
1416 7b109aa7 Stavros Sachtouris
            'Recursively list containers and their contents',
1417 1c366ac9 Stavros Sachtouris
            ('-r', '--recursive')),
1418 1c366ac9 Stavros Sachtouris
        shared_by_me=FlagArgument(
1419 1c366ac9 Stavros Sachtouris
            'show only files shared to other users', '--shared-by-me'),
1420 1c366ac9 Stavros Sachtouris
        public=FlagArgument('show only published objects', '--public'),
1421 2fe2672e Stavros Sachtouris
    )
1422 7493ccb6 Stavros Sachtouris
1423 7b109aa7 Stavros Sachtouris
    def print_containers(self, container_list):
1424 7b109aa7 Stavros Sachtouris
        for index, container in enumerate(container_list):
1425 7b109aa7 Stavros Sachtouris
            if 'bytes' in container:
1426 7b109aa7 Stavros Sachtouris
                size = format_size(container['bytes'])
1427 7b109aa7 Stavros Sachtouris
            prfx = ('%s. ' % (index + 1)) if self['enum'] else ''
1428 7b109aa7 Stavros Sachtouris
            _cname = container['name'] if (
1429 7b109aa7 Stavros Sachtouris
                self['more']) else bold(container['name'])
1430 7b109aa7 Stavros Sachtouris
            cname = u'%s%s' % (prfx, _cname)
1431 b4cf92b8 Stavros Sachtouris
            if self['detail']:
1432 7b109aa7 Stavros Sachtouris
                self.writeln(cname)
1433 7b109aa7 Stavros Sachtouris
                pretty_c = container.copy()
1434 7b109aa7 Stavros Sachtouris
                if 'bytes' in container:
1435 7b109aa7 Stavros Sachtouris
                    pretty_c['bytes'] = '%s (%s)' % (container['bytes'], size)
1436 7b109aa7 Stavros Sachtouris
                self.print_dict(pretty_c, exclude=('name'))
1437 7b109aa7 Stavros Sachtouris
                self.writeln()
1438 b4cf92b8 Stavros Sachtouris
            else:
1439 7b109aa7 Stavros Sachtouris
                if 'count' in container and 'bytes' in container:
1440 7b109aa7 Stavros Sachtouris
                    self.writeln('%s (%s, %s objects)' % (
1441 7b109aa7 Stavros Sachtouris
                        cname, size, container['count']))
1442 7b109aa7 Stavros Sachtouris
                else:
1443 7b109aa7 Stavros Sachtouris
                    self.writeln(cname)
1444 7b109aa7 Stavros Sachtouris
            objects = container.get('objects', [])
1445 7b109aa7 Stavros Sachtouris
            if objects:
1446 7b109aa7 Stavros Sachtouris
                self.print_objects(objects)
1447 7b109aa7 Stavros Sachtouris
                self.writeln('')
1448 234954d1 Stavros Sachtouris
1449 7b109aa7 Stavros Sachtouris
    def _create_object_forest(self, container_list):
1450 7b109aa7 Stavros Sachtouris
        try:
1451 7b109aa7 Stavros Sachtouris
            for container in container_list:
1452 7b109aa7 Stavros Sachtouris
                self.client.container = container['name']
1453 7b109aa7 Stavros Sachtouris
                objects = self.client.container_get(
1454 7b109aa7 Stavros Sachtouris
                    limit=False if self['more'] else self['limit'],
1455 7b109aa7 Stavros Sachtouris
                    if_modified_since=self['modified_since_date'],
1456 7b109aa7 Stavros Sachtouris
                    if_unmodified_since=self['unmodified_since_date'],
1457 7b109aa7 Stavros Sachtouris
                    until=self['until_date'],
1458 1c366ac9 Stavros Sachtouris
                    show_only_shared=self['shared_by_me'],
1459 1c366ac9 Stavros Sachtouris
                    public=self['public'])
1460 7b109aa7 Stavros Sachtouris
                container['objects'] = objects.json
1461 7b109aa7 Stavros Sachtouris
        finally:
1462 7b109aa7 Stavros Sachtouris
            self.client.container = None
1463 7493ccb6 Stavros Sachtouris
1464 b4cf92b8 Stavros Sachtouris
    @errors.generic.all
1465 b4cf92b8 Stavros Sachtouris
    @errors.pithos.connection
1466 b4cf92b8 Stavros Sachtouris
    @errors.pithos.object_path
1467 b4cf92b8 Stavros Sachtouris
    @errors.pithos.container
1468 7b109aa7 Stavros Sachtouris
    def _run(self, container):
1469 7b109aa7 Stavros Sachtouris
        if container:
1470 7b109aa7 Stavros Sachtouris
            r = self.client.container_get(
1471 7b109aa7 Stavros Sachtouris
                limit=False if self['more'] else self['limit'],
1472 7b109aa7 Stavros Sachtouris
                marker=self['marker'],
1473 7b109aa7 Stavros Sachtouris
                if_modified_since=self['modified_since_date'],
1474 7b109aa7 Stavros Sachtouris
                if_unmodified_since=self['unmodified_since_date'],
1475 7b109aa7 Stavros Sachtouris
                until=self['until_date'],
1476 1c366ac9 Stavros Sachtouris
                show_only_shared=self['shared_by_me'],
1477 1c366ac9 Stavros Sachtouris
                public=self['public'])
1478 b4cf92b8 Stavros Sachtouris
        else:
1479 7b109aa7 Stavros Sachtouris
            r = self.client.account_get(
1480 7b109aa7 Stavros Sachtouris
                limit=False if self['more'] else self['limit'],
1481 7b109aa7 Stavros Sachtouris
                marker=self['marker'],
1482 7b109aa7 Stavros Sachtouris
                if_modified_since=self['modified_since_date'],
1483 7b109aa7 Stavros Sachtouris
                if_unmodified_since=self['unmodified_since_date'],
1484 7b109aa7 Stavros Sachtouris
                until=self['until_date'],
1485 1c366ac9 Stavros Sachtouris
                show_only_shared=self['shared_by_me'],
1486 1c366ac9 Stavros Sachtouris
                public=self['public'])
1487 7b109aa7 Stavros Sachtouris
        files = self._filter_by_name(r.json)
1488 7b109aa7 Stavros Sachtouris
        if self['recursive'] and not container:
1489 7b109aa7 Stavros Sachtouris
            self._create_object_forest(files)
1490 7b109aa7 Stavros Sachtouris
        if self['more']:
1491 7b109aa7 Stavros Sachtouris
            outbu, self._out = self._out, StringIO()
1492 7b109aa7 Stavros Sachtouris
        try:
1493 7b109aa7 Stavros Sachtouris
            if self['json_output'] or self['output_format']:
1494 7b109aa7 Stavros Sachtouris
                self._print(files)
1495 7b109aa7 Stavros Sachtouris
            else:
1496 7b109aa7 Stavros Sachtouris
                (self.print_objects if container else self.print_containers)(
1497 7b109aa7 Stavros Sachtouris
                    files)
1498 7b109aa7 Stavros Sachtouris
        finally:
1499 7b109aa7 Stavros Sachtouris
            if self['more']:
1500 7b109aa7 Stavros Sachtouris
                pager(self._out.getvalue())
1501 7b109aa7 Stavros Sachtouris
                self._out = outbu
1502 b4cf92b8 Stavros Sachtouris
1503 7b109aa7 Stavros Sachtouris
    def main(self, container=None):
1504 7b109aa7 Stavros Sachtouris
        super(self.__class__, self)._run()
1505 7b109aa7 Stavros Sachtouris
        self.client.container, self.container = container, container
1506 7b109aa7 Stavros Sachtouris
        self._run(container)
1507 7493ccb6 Stavros Sachtouris
1508 234954d1 Stavros Sachtouris
1509 7b109aa7 Stavros Sachtouris
@command(container_cmds)
1510 7b109aa7 Stavros Sachtouris
class container_create(_pithos_account):
1511 7b109aa7 Stavros Sachtouris
    """Create a new container"""
1512 001200c3 Stavros Sachtouris
1513 001200c3 Stavros Sachtouris
    arguments = dict(
1514 7b109aa7 Stavros Sachtouris
        versioning=ValueArgument(
1515 7b109aa7 Stavros Sachtouris
            'set container versioning (auto/none)', '--versioning'),
1516 7b109aa7 Stavros Sachtouris
        limit=IntArgument('set default container limit', '--limit'),
1517 7b109aa7 Stavros Sachtouris
        meta=KeyValueArgument(
1518 7b109aa7 Stavros Sachtouris
            'set container metadata (can be repeated)', '--meta')
1519 de73876b Stavros Sachtouris
    )
1520 7493ccb6 Stavros Sachtouris
1521 b4cf92b8 Stavros Sachtouris
    @errors.generic.all
1522 b4cf92b8 Stavros Sachtouris
    @errors.pithos.connection
1523 7b109aa7 Stavros Sachtouris
    @errors.pithos.container
1524 7b109aa7 Stavros Sachtouris
    def _run(self, container):
1525 7b109aa7 Stavros Sachtouris
        try:
1526 7b109aa7 Stavros Sachtouris
            self.client.create_container(
1527 7b109aa7 Stavros Sachtouris
                container=container,
1528 7b109aa7 Stavros Sachtouris
                sizelimit=self['limit'],
1529 7b109aa7 Stavros Sachtouris
                versioning=self['versioning'],
1530 7b109aa7 Stavros Sachtouris
                metadata=self['meta'],
1531 7b109aa7 Stavros Sachtouris
                success=(201, ))
1532 7b109aa7 Stavros Sachtouris
        except ClientError as ce:
1533 7b109aa7 Stavros Sachtouris
            if ce.status in (202, ):
1534 7b109aa7 Stavros Sachtouris
                raise CLIError(
1535 7b109aa7 Stavros Sachtouris
                    'Container %s alread exists' % container, details=[
1536 7b109aa7 Stavros Sachtouris
                    'Either delete %s or choose another name' % (container)])
1537 7b109aa7 Stavros Sachtouris
            raise
1538 234954d1 Stavros Sachtouris
1539 7b109aa7 Stavros Sachtouris
    def main(self, new_container):
1540 7b109aa7 Stavros Sachtouris
        super(self.__class__, self)._run()
1541 7b109aa7 Stavros Sachtouris
        self._run(container=new_container)
1542 326a79b9 Stavros Sachtouris
1543 326a79b9 Stavros Sachtouris
1544 7b109aa7 Stavros Sachtouris
@command(container_cmds)
1545 7b109aa7 Stavros Sachtouris
class container_delete(_pithos_account):
1546 7b109aa7 Stavros Sachtouris
    """Delete a container"""
1547 3ed6dbde Stavros Sachtouris
1548 3ed6dbde Stavros Sachtouris
    arguments = dict(
1549 7b109aa7 Stavros Sachtouris
        yes=FlagArgument('Do not prompt for permission', '--yes'),
1550 7b109aa7 Stavros Sachtouris
        recursive=FlagArgument(
1551 7b109aa7 Stavros Sachtouris
            'delete container even if not empty', ('-r', '--recursive'))
1552 3ed6dbde Stavros Sachtouris
    )
1553 3ed6dbde Stavros Sachtouris
1554 3ed6dbde Stavros Sachtouris
    @errors.generic.all
1555 7b109aa7 Stavros Sachtouris
    @errors.pithos.connection
1556 3ed6dbde Stavros Sachtouris
    @errors.pithos.container
1557 7b109aa7 Stavros Sachtouris
    def _run(self, container):
1558 7b109aa7 Stavros Sachtouris
        num_of_contents = int(self.client.get_container_info(container)[
1559 7b109aa7 Stavros Sachtouris
            'x-container-object-count'])
1560 7b109aa7 Stavros Sachtouris
        delimiter, msg = None, 'Delete container %s ?' % container
1561 7b109aa7 Stavros Sachtouris
        if self['recursive']:
1562 7b109aa7 Stavros Sachtouris
            delimiter, msg = '/', 'Empty and d%s' % msg[1:]
1563 7b109aa7 Stavros Sachtouris
        elif num_of_contents:
1564 7b109aa7 Stavros Sachtouris
            raise CLIError('Container %s is not empty' % container, details=[
1565 eb647cfe Stavros Sachtouris
                'Use %s to delete non-empty containers' % (
1566 eb647cfe Stavros Sachtouris
                    self.arguments['recursive'].lvalue)])
1567 7b109aa7 Stavros Sachtouris
        if self['yes'] or self.ask_user(msg):
1568 7b109aa7 Stavros Sachtouris
            if num_of_contents:
1569 7b109aa7 Stavros Sachtouris
                self.client.del_container(delimiter=delimiter)
1570 7b109aa7 Stavros Sachtouris
            self.client.purge_container()
1571 3ed6dbde Stavros Sachtouris
1572 7b109aa7 Stavros Sachtouris
    def main(self, container):
1573 3ed6dbde Stavros Sachtouris
        super(self.__class__, self)._run()
1574 7b109aa7 Stavros Sachtouris
        self.container, self.client.container = container, container
1575 7b109aa7 Stavros Sachtouris
        self._run(container)
1576 3ed6dbde Stavros Sachtouris
1577 3ed6dbde Stavros Sachtouris
1578 7b109aa7 Stavros Sachtouris
@command(container_cmds)
1579 7b109aa7 Stavros Sachtouris
class container_empty(_pithos_account):
1580 7b109aa7 Stavros Sachtouris
    """Empty a container"""
1581 001200c3 Stavros Sachtouris
1582 7b109aa7 Stavros Sachtouris
    arguments = dict(yes=FlagArgument('Do not prompt for permission', '--yes'))
1583 7493ccb6 Stavros Sachtouris
1584 b4cf92b8 Stavros Sachtouris
    @errors.generic.all
1585 b4cf92b8 Stavros Sachtouris
    @errors.pithos.connection
1586 b4cf92b8 Stavros Sachtouris
    @errors.pithos.container
1587 7b109aa7 Stavros Sachtouris
    def _run(self, container):
1588 7b109aa7 Stavros Sachtouris
        if self['yes'] or self.ask_user('Empty container %s ?' % container):
1589 7b109aa7 Stavros Sachtouris
            self.client.del_container(delimiter='/')
1590 b4cf92b8 Stavros Sachtouris
1591 7b109aa7 Stavros Sachtouris
    def main(self, container):
1592 b4cf92b8 Stavros Sachtouris
        super(self.__class__, self)._run()
1593 7b109aa7 Stavros Sachtouris
        self.container, self.client.container = container, container
1594 7b109aa7 Stavros Sachtouris
        self._run(container)
1595 234954d1 Stavros Sachtouris
1596 915b99b5 Stavros Sachtouris
1597 bfd0f8db Stavros Sachtouris
@command(sharer_cmds)
1598 bfd0f8db Stavros Sachtouris
class sharer_list(_pithos_account, _optional_json):
1599 bfd0f8db Stavros Sachtouris
    """List accounts who share file objects with current user"""
1600 915b99b5 Stavros Sachtouris
1601 bfd0f8db Stavros Sachtouris
    arguments = dict(
1602 bfd0f8db Stavros Sachtouris
        detail=FlagArgument('show detailed output', ('-l', '--details')),
1603 bfd0f8db Stavros Sachtouris
        marker=ValueArgument('show output greater then marker', '--marker')
1604 bfd0f8db Stavros Sachtouris
    )
1605 7493ccb6 Stavros Sachtouris
1606 b4cf92b8 Stavros Sachtouris
    @errors.generic.all
1607 b4cf92b8 Stavros Sachtouris
    @errors.pithos.connection
1608 b4cf92b8 Stavros Sachtouris
    def _run(self):
1609 bfd0f8db Stavros Sachtouris
        accounts = self.client.get_sharing_accounts(marker=self['marker'])
1610 bfd0f8db Stavros Sachtouris
        if not (self['json_output'] or self['output_format']):
1611 bfd0f8db Stavros Sachtouris
            usernames = self._uuids2usernames(
1612 bfd0f8db Stavros Sachtouris
                [acc['name'] for acc in accounts])
1613 bfd0f8db Stavros Sachtouris
            for item in accounts:
1614 bfd0f8db Stavros Sachtouris
                uuid = item['name']
1615 bfd0f8db Stavros Sachtouris
                item['id'], item['name'] = uuid, usernames[uuid]
1616 bfd0f8db Stavros Sachtouris
                if not self['detail']:
1617 bfd0f8db Stavros Sachtouris
                    item.pop('last_modified')
1618 bfd0f8db Stavros Sachtouris
        self._print(accounts)
1619 b4cf92b8 Stavros Sachtouris
1620 bfd0f8db Stavros Sachtouris
    def main(self):
1621 b4cf92b8 Stavros Sachtouris
        super(self.__class__, self)._run()
1622 b4cf92b8 Stavros Sachtouris
        self._run()
1623 7493ccb6 Stavros Sachtouris
1624 234954d1 Stavros Sachtouris
1625 bfd0f8db Stavros Sachtouris
@command(sharer_cmds)
1626 bfd0f8db Stavros Sachtouris
class sharer_info(_pithos_account, _optional_json):
1627 bfd0f8db Stavros Sachtouris
    """Details on a Pithos+ sharer account (default: current account)"""
1628 b4cf92b8 Stavros Sachtouris
1629 b4cf92b8 Stavros Sachtouris
    @errors.generic.all
1630 b4cf92b8 Stavros Sachtouris
    @errors.pithos.connection
1631 bfd0f8db Stavros Sachtouris
    def _run(self):
1632 bfd0f8db Stavros Sachtouris
        self._print(self.client.get_account_info(), self.print_dict)
1633 b4cf92b8 Stavros Sachtouris
1634 bfd0f8db Stavros Sachtouris
    def main(self, account_uuid=None):
1635 b4cf92b8 Stavros Sachtouris
        super(self.__class__, self)._run()
1636 bfd0f8db Stavros Sachtouris
        if account_uuid:
1637 bfd0f8db Stavros Sachtouris
            self.client.account, self.account = account_uuid, account_uuid
1638 bfd0f8db Stavros Sachtouris
        self._run()
1639 7493ccb6 Stavros Sachtouris
1640 234954d1 Stavros Sachtouris
1641 bfd0f8db Stavros Sachtouris
class _pithos_group(_pithos_account):
1642 bfd0f8db Stavros Sachtouris
    prefix = 'x-account-group-'
1643 bfd0f8db Stavros Sachtouris
    preflen = len(prefix)
1644 915b99b5 Stavros Sachtouris
1645 bfd0f8db Stavros Sachtouris
    def _groups(self):
1646 bfd0f8db Stavros Sachtouris
        groups = dict()
1647 bfd0f8db Stavros Sachtouris
        for k, v in self.client.get_account_group().items():
1648 bfd0f8db Stavros Sachtouris
            groups[k[self.preflen:]] = v
1649 bfd0f8db Stavros Sachtouris
        return groups
1650 915b99b5 Stavros Sachtouris
1651 bfd0f8db Stavros Sachtouris
1652 bfd0f8db Stavros Sachtouris
@command(group_cmds)
1653 bfd0f8db Stavros Sachtouris
class group_list(_pithos_group, _optional_json):
1654 545c6c29 Stavros Sachtouris
    """list all groups and group members"""
1655 7493ccb6 Stavros Sachtouris
1656 b4cf92b8 Stavros Sachtouris
    @errors.generic.all
1657 b4cf92b8 Stavros Sachtouris
    @errors.pithos.connection
1658 b4cf92b8 Stavros Sachtouris
    def _run(self):
1659 bfd0f8db Stavros Sachtouris
        self._print(self._groups(), self.print_dict)
1660 b4cf92b8 Stavros Sachtouris
1661 7493ccb6 Stavros Sachtouris
    def main(self):
1662 b4cf92b8 Stavros Sachtouris
        super(self.__class__, self)._run()
1663 b4cf92b8 Stavros Sachtouris
        self._run()
1664 7493ccb6 Stavros Sachtouris
1665 234954d1 Stavros Sachtouris
1666 bfd0f8db Stavros Sachtouris
@command(group_cmds)
1667 bfd0f8db Stavros Sachtouris
class group_create(_pithos_group, _optional_json):
1668 bfd0f8db Stavros Sachtouris
    """Create a group of users"""
1669 bfd0f8db Stavros Sachtouris
1670 bfd0f8db Stavros Sachtouris
    arguments = dict(
1671 bfd0f8db Stavros Sachtouris
        user_uuid=RepeatableArgument('Add a user to the group', '--uuid'),
1672 bfd0f8db Stavros Sachtouris
        username=RepeatableArgument('Add a user to the group', '--username')
1673 bfd0f8db Stavros Sachtouris
    )
1674 4193b902 Stavros Sachtouris
    required = ['user_uuid', 'username']
1675 7493ccb6 Stavros Sachtouris
1676 b4cf92b8 Stavros Sachtouris
    @errors.generic.all
1677 b4cf92b8 Stavros Sachtouris
    @errors.pithos.connection
1678 b4cf92b8 Stavros Sachtouris
    def _run(self, groupname, *users):
1679 bfd0f8db Stavros Sachtouris
        if groupname in self._groups() and not self.ask_user(
1680 bfd0f8db Stavros Sachtouris
                'Group %s already exists, overwrite?' % groupname):
1681 bfd0f8db Stavros Sachtouris
            self.error('Aborted')
1682 bfd0f8db Stavros Sachtouris
            return
1683 bfd0f8db Stavros Sachtouris
        self.client.set_account_group(groupname, users)
1684 bfd0f8db Stavros Sachtouris
        self._print(self._groups(), self.print_dict)
1685 b4cf92b8 Stavros Sachtouris
1686 bfd0f8db Stavros Sachtouris
    def main(self, groupname):
1687 b4cf92b8 Stavros Sachtouris
        super(self.__class__, self)._run()
1688 f084bdc8 Stavros Sachtouris
        users = (self['user_uuid'] or []) + self._usernames2uuids(
1689 f084bdc8 Stavros Sachtouris
            self['username'] or []).values()
1690 b4cf92b8 Stavros Sachtouris
        if users:
1691 b4cf92b8 Stavros Sachtouris
            self._run(groupname, *users)
1692 b4cf92b8 Stavros Sachtouris
        else:
1693 bfd0f8db Stavros Sachtouris
            raise CLISyntaxError(
1694 bfd0f8db Stavros Sachtouris
                'No valid users specified, use %s or %s' % (
1695 eb647cfe Stavros Sachtouris
                    self.arguments['user_uuid'].lvalue,
1696 eb647cfe Stavros Sachtouris
                    self.arguments['username'].lvalue),
1697 bfd0f8db Stavros Sachtouris
                details=[
1698 f3a239f6 Stavros Sachtouris
                    'Check if a username or uuid is valid with',
1699 f3a239f6 Stavros Sachtouris
                    '  user uuid2username', 'OR', '  user username2uuid'])
1700 7493ccb6 Stavros Sachtouris
1701 234954d1 Stavros Sachtouris
1702 bfd0f8db Stavros Sachtouris
@command(group_cmds)
1703 bfd0f8db Stavros Sachtouris
class group_delete(_pithos_group, _optional_json):
1704 4fcc38a2 Stavros Sachtouris
    """Delete a user group"""
1705 7493ccb6 Stavros Sachtouris
1706 b4cf92b8 Stavros Sachtouris
    @errors.generic.all
1707 b4cf92b8 Stavros Sachtouris
    @errors.pithos.connection
1708 b4cf92b8 Stavros Sachtouris
    def _run(self, groupname):
1709 bfd0f8db Stavros Sachtouris
        self.client.del_account_group(groupname)
1710 bfd0f8db Stavros Sachtouris
        self._print(self._groups(), self.print_dict)
1711 b4cf92b8 Stavros Sachtouris
1712 7493ccb6 Stavros Sachtouris
    def main(self, groupname):
1713 b4cf92b8 Stavros Sachtouris
        super(self.__class__, self)._run()
1714 b4cf92b8 Stavros Sachtouris
        self._run(groupname)
1715 1c366ac9 Stavros Sachtouris
1716 1c366ac9 Stavros Sachtouris
1717 1c366ac9 Stavros Sachtouris
#  Deprecated commands
1718 1c366ac9 Stavros Sachtouris
1719 1c366ac9 Stavros Sachtouris
@command(file_cmds)
1720 1c366ac9 Stavros Sachtouris
class file_publish(_pithos_init):
1721 1c366ac9 Stavros Sachtouris
    """DEPRECATED, replaced by [kamaki] file modify OBJECT --publish"""
1722 1c366ac9 Stavros Sachtouris
1723 1c366ac9 Stavros Sachtouris
    def main(self, *args):
1724 1c366ac9 Stavros Sachtouris
        raise CLISyntaxError('DEPRECATED', details=[
1725 1c366ac9 Stavros Sachtouris
            'This command is replaced by:',
1726 1c366ac9 Stavros Sachtouris
            '  [kamaki] file modify OBJECT --publish'])
1727 1c366ac9 Stavros Sachtouris
1728 1c366ac9 Stavros Sachtouris
1729 1c366ac9 Stavros Sachtouris
@command(file_cmds)
1730 1c366ac9 Stavros Sachtouris
class file_unpublish(_pithos_init):
1731 1c366ac9 Stavros Sachtouris
    """DEPRECATED, replaced by [kamaki] file modify OBJECT --unpublish"""
1732 1c366ac9 Stavros Sachtouris
1733 1c366ac9 Stavros Sachtouris
    def main(self, *args):
1734 1c366ac9 Stavros Sachtouris
        raise CLISyntaxError('DEPRECATED', details=[
1735 1c366ac9 Stavros Sachtouris
            'This command is replaced by:',
1736 1c366ac9 Stavros Sachtouris
            '  [kamaki] file modify OBJECT --unpublish'])