Statistics
| Branch: | Tag: | Revision:

root / kamaki / cli / commands / pithos.py @ 362adf50

History | View | Annotate | Download (76.9 kB)

1 7493ccb6 Stavros Sachtouris
# Copyright 2011-2012 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 sys import stdout
35 1395c40e Stavros Sachtouris
from time import localtime, strftime
36 b666ef82 Stavros Sachtouris
from os import path, makedirs, walk
37 1395c40e Stavros Sachtouris
38 234954d1 Stavros Sachtouris
from kamaki.cli import command
39 d486baec Stavros Sachtouris
from kamaki.cli.command_tree import CommandTree
40 8cec3671 Stavros Sachtouris
from kamaki.cli.errors import raiseCLIError, CLISyntaxError, CLIBaseUrlError
41 7147e1ca Stavros Sachtouris
from kamaki.cli.utils import (
42 545c6c29 Stavros Sachtouris
    format_size, to_bytes, print_dict, print_items, pretty_keys, pretty_dict,
43 0399ac7e Stavros Sachtouris
    page_hold, bold, ask_user, get_path_size, print_json)
44 e3d4d442 Stavros Sachtouris
from kamaki.cli.argument import FlagArgument, ValueArgument, IntArgument
45 04d01cd4 Stavros Sachtouris
from kamaki.cli.argument import KeyValueArgument, DateArgument
46 fd1f1d96 Stavros Sachtouris
from kamaki.cli.argument import ProgressBarArgument
47 545c6c29 Stavros Sachtouris
from kamaki.cli.commands import _command_init, errors
48 545c6c29 Stavros Sachtouris
from kamaki.cli.commands import _optional_output_cmd, _optional_json
49 7493ccb6 Stavros Sachtouris
from kamaki.clients.pithos import PithosClient, ClientError
50 1f5debf7 Stavros Sachtouris
from kamaki.clients.astakos import AstakosClient
51 1395c40e Stavros Sachtouris
52 a29d2f88 Stavros Sachtouris
pithos_cmds = CommandTree('file', 'Pithos+/Storage API commands')
53 d486baec Stavros Sachtouris
_commands = [pithos_cmds]
54 234954d1 Stavros Sachtouris
55 7493ccb6 Stavros Sachtouris
56 234954d1 Stavros Sachtouris
# Argument functionality
57 234954d1 Stavros Sachtouris
58 efdee310 Stavros Sachtouris
class DelimiterArgument(ValueArgument):
59 befed235 Stavros Sachtouris
    """
60 befed235 Stavros Sachtouris
    :value type: string
61 befed235 Stavros Sachtouris
    :value returns: given string or /
62 03d661d8 Stavros Sachtouris
    """
63 03d661d8 Stavros Sachtouris
64 efdee310 Stavros Sachtouris
    def __init__(self, caller_obj, help='', parsed_name=None, default=None):
65 efdee310 Stavros Sachtouris
        super(DelimiterArgument, self).__init__(help, parsed_name, default)
66 efdee310 Stavros Sachtouris
        self.caller_obj = caller_obj
67 efdee310 Stavros Sachtouris
68 234954d1 Stavros Sachtouris
    @property
69 efdee310 Stavros Sachtouris
    def value(self):
70 2fe2672e Stavros Sachtouris
        if self.caller_obj['recursive']:
71 efdee310 Stavros Sachtouris
            return '/'
72 efdee310 Stavros Sachtouris
        return getattr(self, '_value', self.default)
73 234954d1 Stavros Sachtouris
74 234954d1 Stavros Sachtouris
    @value.setter
75 efdee310 Stavros Sachtouris
    def value(self, newvalue):
76 efdee310 Stavros Sachtouris
        self._value = newvalue
77 efdee310 Stavros Sachtouris
78 234954d1 Stavros Sachtouris
79 befed235 Stavros Sachtouris
class SharingArgument(ValueArgument):
80 befed235 Stavros Sachtouris
    """Set sharing (read and/or write) groups
81 439826ec Stavros Sachtouris
    .
82 befed235 Stavros Sachtouris
    :value type: "read=term1,term2,... write=term1,term2,..."
83 439826ec Stavros Sachtouris
    .
84 befed235 Stavros Sachtouris
    :value returns: {'read':['term1', 'term2', ...],
85 439826ec Stavros Sachtouris
    .   'write':['term1', 'term2', ...]}
86 befed235 Stavros Sachtouris
    """
87 234954d1 Stavros Sachtouris
88 234954d1 Stavros Sachtouris
    @property
89 e3d4d442 Stavros Sachtouris
    def value(self):
90 e3d4d442 Stavros Sachtouris
        return getattr(self, '_value', self.default)
91 234954d1 Stavros Sachtouris
92 e3d4d442 Stavros Sachtouris
    @value.setter
93 e3d4d442 Stavros Sachtouris
    def value(self, newvalue):
94 e3d4d442 Stavros Sachtouris
        perms = {}
95 e3d4d442 Stavros Sachtouris
        try:
96 e3d4d442 Stavros Sachtouris
            permlist = newvalue.split(' ')
97 e3d4d442 Stavros Sachtouris
        except AttributeError:
98 e3d4d442 Stavros Sachtouris
            return
99 e3d4d442 Stavros Sachtouris
        for p in permlist:
100 e3d4d442 Stavros Sachtouris
            try:
101 234954d1 Stavros Sachtouris
                (key, val) = p.split('=')
102 83ba5545 Stavros Sachtouris
            except ValueError as err:
103 24ff0a35 Stavros Sachtouris
                raiseCLIError(
104 24ff0a35 Stavros Sachtouris
                    err,
105 24ff0a35 Stavros Sachtouris
                    'Error in --sharing',
106 234954d1 Stavros Sachtouris
                    details='Incorrect format',
107 234954d1 Stavros Sachtouris
                    importance=1)
108 e3d4d442 Stavros Sachtouris
            if key.lower() not in ('read', 'write'):
109 de73876b Stavros Sachtouris
                msg = 'Error in --sharing'
110 de73876b Stavros Sachtouris
                raiseCLIError(err, msg, importance=1, details=[
111 de73876b Stavros Sachtouris
                    'Invalid permission key %s' % key])
112 e3d4d442 Stavros Sachtouris
            val_list = val.split(',')
113 234954d1 Stavros Sachtouris
            if not key in perms:
114 234954d1 Stavros Sachtouris
                perms[key] = []
115 e3d4d442 Stavros Sachtouris
            for item in val_list:
116 e3d4d442 Stavros Sachtouris
                if item not in perms[key]:
117 e3d4d442 Stavros Sachtouris
                    perms[key].append(item)
118 e3d4d442 Stavros Sachtouris
        self._value = perms
119 e3d4d442 Stavros Sachtouris
120 234954d1 Stavros Sachtouris
121 e3d4d442 Stavros Sachtouris
class RangeArgument(ValueArgument):
122 befed235 Stavros Sachtouris
    """
123 439826ec Stavros Sachtouris
    :value type: string of the form <start>-<end> where <start> and <end> are
124 439826ec Stavros Sachtouris
        integers
125 befed235 Stavros Sachtouris
    :value returns: the input string, after type checking <start> and <end>
126 befed235 Stavros Sachtouris
    """
127 befed235 Stavros Sachtouris
128 234954d1 Stavros Sachtouris
    @property
129 e3d4d442 Stavros Sachtouris
    def value(self):
130 e3d4d442 Stavros Sachtouris
        return getattr(self, '_value', self.default)
131 234954d1 Stavros Sachtouris
132 e3d4d442 Stavros Sachtouris
    @value.setter
133 e3d4d442 Stavros Sachtouris
    def value(self, newvalue):
134 e3d4d442 Stavros Sachtouris
        if newvalue is None:
135 e3d4d442 Stavros Sachtouris
            self._value = self.default
136 e3d4d442 Stavros Sachtouris
            return
137 9ceec15a Stavros Sachtouris
        (start, end) = newvalue.split('-')
138 e3d4d442 Stavros Sachtouris
        (start, end) = (int(start), int(end))
139 234954d1 Stavros Sachtouris
        self._value = '%s-%s' % (start, end)
140 234954d1 Stavros Sachtouris
141 201baa17 Stavros Sachtouris
142 234954d1 Stavros Sachtouris
# Command specs
143 234954d1 Stavros Sachtouris
144 e3d4d442 Stavros Sachtouris
145 5eae854d Stavros Sachtouris
class _pithos_init(_command_init):
146 befed235 Stavros Sachtouris
    """Initialize a pithos+ kamaki client"""
147 befed235 Stavros Sachtouris
148 ece4ae4b Stavros Sachtouris
    @staticmethod
149 ece4ae4b Stavros Sachtouris
    def _is_dir(remote_dict):
150 ece4ae4b Stavros Sachtouris
        return 'application/directory' == remote_dict.get(
151 8cec3671 Stavros Sachtouris
            'content_type', remote_dict.get('content-type', ''))
152 ece4ae4b Stavros Sachtouris
153 1395c40e Stavros Sachtouris
    @errors.generic.all
154 1395c40e Stavros Sachtouris
    def _run(self):
155 3ae60112 Stavros Sachtouris
        self.token = self.config.get('file', 'token')\
156 234954d1 Stavros Sachtouris
            or self.config.get('global', 'token')
157 8cec3671 Stavros Sachtouris
158 8cec3671 Stavros Sachtouris
        if getattr(self, 'auth_base', False):
159 8cec3671 Stavros Sachtouris
            pithos_endpoints = self.auth_base.get_service_endpoints(
160 8cec3671 Stavros Sachtouris
                self.config.get('pithos', 'type'),
161 8cec3671 Stavros Sachtouris
                self.config.get('pithos', 'version'))
162 8cec3671 Stavros Sachtouris
            self.base_url = pithos_endpoints['publicURL']
163 8cec3671 Stavros Sachtouris
        else:
164 f5c28bfa Stavros Sachtouris
            self.base_url = self.config.get('file', 'url')\
165 f5c28bfa Stavros Sachtouris
                or self.config.get('store', 'url')\
166 f5c28bfa Stavros Sachtouris
                or self.config.get('pithos', 'url')
167 8cec3671 Stavros Sachtouris
        if not self.base_url:
168 8cec3671 Stavros Sachtouris
            raise CLIBaseUrlError(service='pithos')
169 8cec3671 Stavros Sachtouris
170 1f5debf7 Stavros Sachtouris
        self._set_account()
171 3ae60112 Stavros Sachtouris
        self.container = self.config.get('file', 'container')\
172 f5c28bfa Stavros Sachtouris
            or self.config.get('store', 'container')\
173 f5c28bfa Stavros Sachtouris
            or self.config.get('pithos', 'container')\
174 234954d1 Stavros Sachtouris
            or self.config.get('global', 'container')
175 de73876b Stavros Sachtouris
        self.client = PithosClient(
176 de73876b Stavros Sachtouris
            base_url=self.base_url,
177 234954d1 Stavros Sachtouris
            token=self.token,
178 234954d1 Stavros Sachtouris
            account=self.account,
179 7493ccb6 Stavros Sachtouris
            container=self.container)
180 f47417e7 Stavros Sachtouris
        self._set_log_params()
181 c5b9380c Stavros Sachtouris
        self._update_max_threads()
182 7493ccb6 Stavros Sachtouris
183 1395c40e Stavros Sachtouris
    def main(self):
184 1395c40e Stavros Sachtouris
        self._run()
185 1395c40e Stavros Sachtouris
186 1f5debf7 Stavros Sachtouris
    def _set_account(self):
187 f5c28bfa Stavros Sachtouris
        if getattr(self, 'auth_base', False):
188 819311d3 Stavros Sachtouris
            self.account = self.auth_base.user_term('id', self.token)
189 ef00bc31 Stavros Sachtouris
        else:
190 f5c28bfa Stavros Sachtouris
            astakos_url = self.config.get('user', 'url')\
191 f5c28bfa Stavros Sachtouris
                or self.config.get('astakos', 'url')
192 ef00bc31 Stavros Sachtouris
            if not astakos_url:
193 ef00bc31 Stavros Sachtouris
                raise CLIBaseUrlError(service='astakos')
194 ef00bc31 Stavros Sachtouris
            astakos = AstakosClient(astakos_url, self.token)
195 819311d3 Stavros Sachtouris
            self.account = astakos.user_term('id')
196 1f5debf7 Stavros Sachtouris
197 234954d1 Stavros Sachtouris
198 3ae60112 Stavros Sachtouris
class _file_account_command(_pithos_init):
199 7493ccb6 Stavros Sachtouris
    """Base class for account level storage commands"""
200 7493ccb6 Stavros Sachtouris
201 f724cd35 Stavros Sachtouris
    def __init__(self, arguments={}, auth_base=None):
202 f724cd35 Stavros Sachtouris
        super(_file_account_command, self).__init__(arguments, auth_base)
203 439826ec Stavros Sachtouris
        self['account'] = ValueArgument(
204 201baa17 Stavros Sachtouris
            'Set user account (not permanent)', ('-A', '--account'))
205 7493ccb6 Stavros Sachtouris
206 3ed6dbde Stavros Sachtouris
    def _run(self, custom_account=None):
207 3ae60112 Stavros Sachtouris
        super(_file_account_command, self)._run()
208 3ed6dbde Stavros Sachtouris
        if custom_account:
209 3ed6dbde Stavros Sachtouris
            self.client.account = custom_account
210 3ed6dbde Stavros Sachtouris
        elif self['account']:
211 47ae7577 Stavros Sachtouris
            self.client.account = self['account']
212 7493ccb6 Stavros Sachtouris
213 1395c40e Stavros Sachtouris
    @errors.generic.all
214 1395c40e Stavros Sachtouris
    def main(self):
215 1395c40e Stavros Sachtouris
        self._run()
216 1395c40e Stavros Sachtouris
217 234954d1 Stavros Sachtouris
218 3ae60112 Stavros Sachtouris
class _file_container_command(_file_account_command):
219 7493ccb6 Stavros Sachtouris
    """Base class for container level storage commands"""
220 7493ccb6 Stavros Sachtouris
221 47ae7577 Stavros Sachtouris
    container = None
222 47ae7577 Stavros Sachtouris
    path = None
223 2d7ce81e Stavros Sachtouris
224 f724cd35 Stavros Sachtouris
    def __init__(self, arguments={}, auth_base=None):
225 f724cd35 Stavros Sachtouris
        super(_file_container_command, self).__init__(arguments, auth_base)
226 439826ec Stavros Sachtouris
        self['container'] = ValueArgument(
227 201baa17 Stavros Sachtouris
            'Set container to work with (temporary)', ('-C', '--container'))
228 7493ccb6 Stavros Sachtouris
229 de73876b Stavros Sachtouris
    def extract_container_and_path(
230 24ff0a35 Stavros Sachtouris
            self,
231 24ff0a35 Stavros Sachtouris
            container_with_path,
232 24ff0a35 Stavros Sachtouris
            path_is_optional=True):
233 1395c40e Stavros Sachtouris
        """Contains all heuristics for deciding what should be used as
234 1395c40e Stavros Sachtouris
        container or path. Options are:
235 1395c40e Stavros Sachtouris
        * user string of the form container:path
236 1395c40e Stavros Sachtouris
        * self.container, self.path variables set by super constructor, or
237 1395c40e Stavros Sachtouris
        explicitly by the caller application
238 1395c40e Stavros Sachtouris
        Error handling is explicit as these error cases happen only here
239 1395c40e Stavros Sachtouris
        """
240 83ba5545 Stavros Sachtouris
        try:
241 83ba5545 Stavros Sachtouris
            assert isinstance(container_with_path, str)
242 83ba5545 Stavros Sachtouris
        except AssertionError as err:
243 edab7ba7 Stavros Sachtouris
            if self['container'] and path_is_optional:
244 edab7ba7 Stavros Sachtouris
                self.container = self['container']
245 edab7ba7 Stavros Sachtouris
                self.client.container = self['container']
246 edab7ba7 Stavros Sachtouris
                return
247 83ba5545 Stavros Sachtouris
            raiseCLIError(err)
248 447c9568 Stavros Sachtouris
249 1395c40e Stavros Sachtouris
        user_cont, sep, userpath = container_with_path.partition(':')
250 447c9568 Stavros Sachtouris
251 447c9568 Stavros Sachtouris
        if sep:
252 1395c40e Stavros Sachtouris
            if not user_cont:
253 2005b18e Stavros Sachtouris
                raiseCLIError(CLISyntaxError(
254 2005b18e Stavros Sachtouris
                    'Container is missing\n',
255 1395c40e Stavros Sachtouris
                    details=errors.pithos.container_howto))
256 47ae7577 Stavros Sachtouris
            alt_cont = self['container']
257 1395c40e Stavros Sachtouris
            if alt_cont and user_cont != alt_cont:
258 c1558584 Stavros Sachtouris
                raiseCLIError(CLISyntaxError(
259 1395c40e Stavros Sachtouris
                    'Conflict: 2 containers (%s, %s)' % (user_cont, alt_cont),
260 1395c40e Stavros Sachtouris
                    details=errors.pithos.container_howto)
261 47ae7577 Stavros Sachtouris
                )
262 1395c40e Stavros Sachtouris
            self.container = user_cont
263 1395c40e Stavros Sachtouris
            if not userpath:
264 c1558584 Stavros Sachtouris
                raiseCLIError(CLISyntaxError(
265 1395c40e Stavros Sachtouris
                    'Path is missing for object in container %s' % user_cont,
266 1395c40e Stavros Sachtouris
                    details=errors.pithos.container_howto)
267 47ae7577 Stavros Sachtouris
                )
268 1395c40e Stavros Sachtouris
            self.path = userpath
269 447c9568 Stavros Sachtouris
        else:
270 47ae7577 Stavros Sachtouris
            alt_cont = self['container'] or self.client.container
271 447c9568 Stavros Sachtouris
            if alt_cont:
272 447c9568 Stavros Sachtouris
                self.container = alt_cont
273 1395c40e Stavros Sachtouris
                self.path = user_cont
274 447c9568 Stavros Sachtouris
            elif path_is_optional:
275 1395c40e Stavros Sachtouris
                self.container = user_cont
276 7493ccb6 Stavros Sachtouris
                self.path = None
277 7493ccb6 Stavros Sachtouris
            else:
278 1395c40e Stavros Sachtouris
                self.container = user_cont
279 447c9568 Stavros Sachtouris
                raiseCLIError(CLISyntaxError(
280 c1558584 Stavros Sachtouris
                    'Both container and path are required',
281 1395c40e Stavros Sachtouris
                    details=errors.pithos.container_howto)
282 47ae7577 Stavros Sachtouris
                )
283 7493ccb6 Stavros Sachtouris
284 1395c40e Stavros Sachtouris
    @errors.generic.all
285 1395c40e Stavros Sachtouris
    def _run(self, container_with_path=None, path_is_optional=True):
286 3ae60112 Stavros Sachtouris
        super(_file_container_command, self)._run()
287 edab7ba7 Stavros Sachtouris
        if self['container']:
288 edab7ba7 Stavros Sachtouris
            self.client.container = self['container']
289 edab7ba7 Stavros Sachtouris
            if container_with_path:
290 edab7ba7 Stavros Sachtouris
                self.path = container_with_path
291 edab7ba7 Stavros Sachtouris
            elif not path_is_optional:
292 edab7ba7 Stavros Sachtouris
                raise CLISyntaxError(
293 edab7ba7 Stavros Sachtouris
                    'Both container and path are required',
294 edab7ba7 Stavros Sachtouris
                    details=errors.pithos.container_howto)
295 edab7ba7 Stavros Sachtouris
        elif container_with_path:
296 47ae7577 Stavros Sachtouris
            self.extract_container_and_path(
297 47ae7577 Stavros Sachtouris
                container_with_path,
298 234954d1 Stavros Sachtouris
                path_is_optional)
299 7493ccb6 Stavros Sachtouris
            self.client.container = self.container
300 7493ccb6 Stavros Sachtouris
        self.container = self.client.container
301 7493ccb6 Stavros Sachtouris
302 1395c40e Stavros Sachtouris
    def main(self, container_with_path=None, path_is_optional=True):
303 1395c40e Stavros Sachtouris
        self._run(container_with_path, path_is_optional)
304 1395c40e Stavros Sachtouris
305 7493ccb6 Stavros Sachtouris
306 d486baec Stavros Sachtouris
@command(pithos_cmds)
307 545c6c29 Stavros Sachtouris
class file_list(_file_container_command, _optional_json):
308 7493ccb6 Stavros Sachtouris
    """List containers, object trees or objects in a directory
309 2703cceb Stavros Sachtouris
    Use with:
310 7ae842c2 Stavros Sachtouris
    1 no parameters : containers in current account
311 2703cceb Stavros Sachtouris
    2. one parameter (container) or --container : contents of container
312 2703cceb Stavros Sachtouris
    3. <container>:<prefix> or --container=<container> <prefix>: objects in
313 439826ec Stavros Sachtouris
    .   container starting with prefix
314 7493ccb6 Stavros Sachtouris
    """
315 7493ccb6 Stavros Sachtouris
316 47ae7577 Stavros Sachtouris
    arguments = dict(
317 f40f0cb7 Stavros Sachtouris
        detail=FlagArgument('detailed output', ('-l', '--list')),
318 f40f0cb7 Stavros Sachtouris
        limit=IntArgument('limit number of listed items', ('-n', '--number')),
319 f40f0cb7 Stavros Sachtouris
        marker=ValueArgument('output greater that marker', '--marker'),
320 f40f0cb7 Stavros Sachtouris
        prefix=ValueArgument('output starting with prefix', '--prefix'),
321 47ae7577 Stavros Sachtouris
        delimiter=ValueArgument('show output up to delimiter', '--delimiter'),
322 47ae7577 Stavros Sachtouris
        path=ValueArgument(
323 201baa17 Stavros Sachtouris
            'show output starting with prefix up to /', '--path'),
324 47ae7577 Stavros Sachtouris
        meta=ValueArgument(
325 201baa17 Stavros Sachtouris
            'show output with specified meta keys', '--meta',
326 47ae7577 Stavros Sachtouris
            default=[]),
327 47ae7577 Stavros Sachtouris
        if_modified_since=ValueArgument(
328 201baa17 Stavros Sachtouris
            'show output modified since then', '--if-modified-since'),
329 47ae7577 Stavros Sachtouris
        if_unmodified_since=ValueArgument(
330 201baa17 Stavros Sachtouris
            'show output not modified since then', '--if-unmodified-since'),
331 47ae7577 Stavros Sachtouris
        until=DateArgument('show metadata until then', '--until'),
332 47ae7577 Stavros Sachtouris
        format=ValueArgument(
333 201baa17 Stavros Sachtouris
            'format to parse until data (default: d/m/Y H:M:S )', '--format'),
334 47ae7577 Stavros Sachtouris
        shared=FlagArgument('show only shared', '--shared'),
335 439826ec Stavros Sachtouris
        more=FlagArgument(
336 439826ec Stavros Sachtouris
            'output results in pages (-n to set items per page, default 10)',
337 eb18b8a7 Stavros Sachtouris
            '--more'),
338 eb18b8a7 Stavros Sachtouris
        exact_match=FlagArgument(
339 eb18b8a7 Stavros Sachtouris
            'Show only objects that match exactly with path',
340 ed9af02c Stavros Sachtouris
            '--exact-match'),
341 545c6c29 Stavros Sachtouris
        enum=FlagArgument('Enumerate results', '--enumerate')
342 47ae7577 Stavros Sachtouris
    )
343 c41a86b2 Stavros Sachtouris
344 7493ccb6 Stavros Sachtouris
    def print_objects(self, object_list):
345 0399ac7e Stavros Sachtouris
        if self['json_output']:
346 0399ac7e Stavros Sachtouris
            print_json(object_list)
347 0399ac7e Stavros Sachtouris
            return
348 439826ec Stavros Sachtouris
        limit = int(self['limit']) if self['limit'] > 0 else len(object_list)
349 234954d1 Stavros Sachtouris
        for index, obj in enumerate(object_list):
350 24ff0a35 Stavros Sachtouris
            if self['exact_match'] and self.path and not (
351 2005b18e Stavros Sachtouris
                    obj['name'] == self.path or 'content_type' in obj):
352 2005b18e Stavros Sachtouris
                continue
353 7493ccb6 Stavros Sachtouris
            pretty_obj = obj.copy()
354 7493ccb6 Stavros Sachtouris
            index += 1
355 234954d1 Stavros Sachtouris
            empty_space = ' ' * (len(str(len(object_list))) - len(str(index)))
356 7493ccb6 Stavros Sachtouris
            if obj['content_type'] == 'application/directory':
357 7493ccb6 Stavros Sachtouris
                isDir = True
358 7493ccb6 Stavros Sachtouris
                size = 'D'
359 7493ccb6 Stavros Sachtouris
            else:
360 7493ccb6 Stavros Sachtouris
                isDir = False
361 7493ccb6 Stavros Sachtouris
                size = format_size(obj['bytes'])
362 234954d1 Stavros Sachtouris
                pretty_obj['bytes'] = '%s (%s)' % (obj['bytes'], size)
363 7493ccb6 Stavros Sachtouris
            oname = bold(obj['name'])
364 ed9af02c Stavros Sachtouris
            prfx = ('%s%s. ' % (empty_space, index)) if self['enum'] else ''
365 47ae7577 Stavros Sachtouris
            if self['detail']:
366 ed9af02c Stavros Sachtouris
                print('%s%s' % (prfx, oname))
367 7493ccb6 Stavros Sachtouris
                print_dict(pretty_keys(pretty_obj), exclude=('name'))
368 7493ccb6 Stavros Sachtouris
                print
369 7493ccb6 Stavros Sachtouris
            else:
370 ed9af02c Stavros Sachtouris
                oname = '%s%9s %s' % (prfx, size, oname)
371 7493ccb6 Stavros Sachtouris
                oname += '/' if isDir else ''
372 7493ccb6 Stavros Sachtouris
                print(oname)
373 439826ec Stavros Sachtouris
            if self['more']:
374 439826ec Stavros Sachtouris
                page_hold(index, limit, len(object_list))
375 7493ccb6 Stavros Sachtouris
376 7493ccb6 Stavros Sachtouris
    def print_containers(self, container_list):
377 0399ac7e Stavros Sachtouris
        if self['json_output']:
378 0399ac7e Stavros Sachtouris
            print_json(container_list)
379 0399ac7e Stavros Sachtouris
            return
380 439826ec Stavros Sachtouris
        limit = int(self['limit']) if self['limit'] > 0\
381 439826ec Stavros Sachtouris
            else len(container_list)
382 234954d1 Stavros Sachtouris
        for index, container in enumerate(container_list):
383 234954d1 Stavros Sachtouris
            if 'bytes' in container:
384 234954d1 Stavros Sachtouris
                size = format_size(container['bytes'])
385 ed9af02c Stavros Sachtouris
            prfx = ('%s. ' % (index + 1)) if self['enum'] else ''
386 ed9af02c Stavros Sachtouris
            cname = '%s%s' % (prfx, bold(container['name']))
387 47ae7577 Stavros Sachtouris
            if self['detail']:
388 7493ccb6 Stavros Sachtouris
                print(cname)
389 7493ccb6 Stavros Sachtouris
                pretty_c = container.copy()
390 234954d1 Stavros Sachtouris
                if 'bytes' in container:
391 234954d1 Stavros Sachtouris
                    pretty_c['bytes'] = '%s (%s)' % (container['bytes'], size)
392 7493ccb6 Stavros Sachtouris
                print_dict(pretty_keys(pretty_c), exclude=('name'))
393 7493ccb6 Stavros Sachtouris
                print
394 7493ccb6 Stavros Sachtouris
            else:
395 234954d1 Stavros Sachtouris
                if 'count' in container and 'bytes' in container:
396 de73876b Stavros Sachtouris
                    print('%s (%s, %s objects)' % (
397 de73876b Stavros Sachtouris
                        cname,
398 de73876b Stavros Sachtouris
                        size,
399 de73876b Stavros Sachtouris
                        container['count']))
400 7493ccb6 Stavros Sachtouris
                else:
401 7493ccb6 Stavros Sachtouris
                    print(cname)
402 439826ec Stavros Sachtouris
            if self['more']:
403 439826ec Stavros Sachtouris
                page_hold(index + 1, limit, len(container_list))
404 7493ccb6 Stavros Sachtouris
405 1395c40e Stavros Sachtouris
    @errors.generic.all
406 1395c40e Stavros Sachtouris
    @errors.pithos.connection
407 1395c40e Stavros Sachtouris
    @errors.pithos.object_path
408 1395c40e Stavros Sachtouris
    @errors.pithos.container
409 1395c40e Stavros Sachtouris
    def _run(self):
410 1395c40e Stavros Sachtouris
        if self.container is None:
411 1395c40e Stavros Sachtouris
            r = self.client.account_get(
412 1395c40e Stavros Sachtouris
                limit=False if self['more'] else self['limit'],
413 1395c40e Stavros Sachtouris
                marker=self['marker'],
414 1395c40e Stavros Sachtouris
                if_modified_since=self['if_modified_since'],
415 1395c40e Stavros Sachtouris
                if_unmodified_since=self['if_unmodified_since'],
416 1395c40e Stavros Sachtouris
                until=self['until'],
417 1395c40e Stavros Sachtouris
                show_only_shared=self['shared'])
418 545c6c29 Stavros Sachtouris
            self._print(r.json, self.print_containers)
419 1395c40e Stavros Sachtouris
        else:
420 52edad0a Stavros Sachtouris
            prefix = self.path or self['prefix']
421 1395c40e Stavros Sachtouris
            r = self.client.container_get(
422 1395c40e Stavros Sachtouris
                limit=False if self['more'] else self['limit'],
423 1395c40e Stavros Sachtouris
                marker=self['marker'],
424 1395c40e Stavros Sachtouris
                prefix=prefix,
425 1395c40e Stavros Sachtouris
                delimiter=self['delimiter'],
426 1395c40e Stavros Sachtouris
                path=self['path'],
427 1395c40e Stavros Sachtouris
                if_modified_since=self['if_modified_since'],
428 1395c40e Stavros Sachtouris
                if_unmodified_since=self['if_unmodified_since'],
429 1395c40e Stavros Sachtouris
                until=self['until'],
430 1395c40e Stavros Sachtouris
                meta=self['meta'],
431 1395c40e Stavros Sachtouris
                show_only_shared=self['shared'])
432 545c6c29 Stavros Sachtouris
            self._print(r.json, self.print_objects)
433 1395c40e Stavros Sachtouris
434 7493ccb6 Stavros Sachtouris
    def main(self, container____path__=None):
435 1395c40e Stavros Sachtouris
        super(self.__class__, self)._run(container____path__)
436 1395c40e Stavros Sachtouris
        self._run()
437 7493ccb6 Stavros Sachtouris
438 234954d1 Stavros Sachtouris
439 d486baec Stavros Sachtouris
@command(pithos_cmds)
440 915b99b5 Stavros Sachtouris
class file_mkdir(_file_container_command, _optional_output_cmd):
441 f5d9bc54 Stavros Sachtouris
    """Create a directory"""
442 1e29b9f6 Stavros Sachtouris
443 3d568c09 Stavros Sachtouris
    __doc__ += '\n. '.join([
444 3d568c09 Stavros Sachtouris
        'Kamaki hanldes directories the same way as OOS Storage and Pithos+:',
445 3d568c09 Stavros Sachtouris
        'A   directory  is   an  object  with  type  "application/directory"',
446 3d568c09 Stavros Sachtouris
        'An object with path  dir/name can exist even if  dir does not exist',
447 3d568c09 Stavros Sachtouris
        'or even if dir  is  a non  directory  object.  Users can modify dir',
448 3d568c09 Stavros Sachtouris
        'without affecting the dir/name object in any way.'])
449 7493ccb6 Stavros Sachtouris
450 1395c40e Stavros Sachtouris
    @errors.generic.all
451 1395c40e Stavros Sachtouris
    @errors.pithos.connection
452 1395c40e Stavros Sachtouris
    @errors.pithos.container
453 1395c40e Stavros Sachtouris
    def _run(self):
454 915b99b5 Stavros Sachtouris
        self._optional_output(self.client.create_directory(self.path))
455 1395c40e Stavros Sachtouris
456 7493ccb6 Stavros Sachtouris
    def main(self, container___directory):
457 1395c40e Stavros Sachtouris
        super(self.__class__, self)._run(
458 1395c40e Stavros Sachtouris
            container___directory,
459 1395c40e Stavros Sachtouris
            path_is_optional=False)
460 1395c40e Stavros Sachtouris
        self._run()
461 7493ccb6 Stavros Sachtouris
462 234954d1 Stavros Sachtouris
463 d486baec Stavros Sachtouris
@command(pithos_cmds)
464 915b99b5 Stavros Sachtouris
class file_touch(_file_container_command, _optional_output_cmd):
465 1e29b9f6 Stavros Sachtouris
    """Create an empty object (file)
466 1e29b9f6 Stavros Sachtouris
    If object exists, this command will reset it to 0 length
467 1e29b9f6 Stavros Sachtouris
    """
468 1e29b9f6 Stavros Sachtouris
469 1e29b9f6 Stavros Sachtouris
    arguments = dict(
470 1e29b9f6 Stavros Sachtouris
        content_type=ValueArgument(
471 1e29b9f6 Stavros Sachtouris
            'Set content type (default: application/octet-stream)',
472 1e29b9f6 Stavros Sachtouris
            '--content-type',
473 915b99b5 Stavros Sachtouris
            default='application/octet-stream')
474 1e29b9f6 Stavros Sachtouris
    )
475 1e29b9f6 Stavros Sachtouris
476 1395c40e Stavros Sachtouris
    @errors.generic.all
477 1395c40e Stavros Sachtouris
    @errors.pithos.connection
478 1395c40e Stavros Sachtouris
    @errors.pithos.container
479 1395c40e Stavros Sachtouris
    def _run(self):
480 915b99b5 Stavros Sachtouris
        self._optional_output(
481 915b99b5 Stavros Sachtouris
            self.client.create_object(self.path, self['content_type']))
482 1395c40e Stavros Sachtouris
483 1e29b9f6 Stavros Sachtouris
    def main(self, container___path):
484 3ae60112 Stavros Sachtouris
        super(file_touch, self)._run(
485 52edad0a Stavros Sachtouris
            container___path,
486 52edad0a Stavros Sachtouris
            path_is_optional=False)
487 1395c40e Stavros Sachtouris
        self._run()
488 1e29b9f6 Stavros Sachtouris
489 1e29b9f6 Stavros Sachtouris
490 1e29b9f6 Stavros Sachtouris
@command(pithos_cmds)
491 915b99b5 Stavros Sachtouris
class file_create(_file_container_command, _optional_output_cmd):
492 1e29b9f6 Stavros Sachtouris
    """Create a container"""
493 7493ccb6 Stavros Sachtouris
494 2fe2672e Stavros Sachtouris
    arguments = dict(
495 2fe2672e Stavros Sachtouris
        versioning=ValueArgument(
496 201baa17 Stavros Sachtouris
            'set container versioning (auto/none)', '--versioning'),
497 3ed6dbde Stavros Sachtouris
        limit=IntArgument('set default container limit', '--limit'),
498 2fe2672e Stavros Sachtouris
        meta=KeyValueArgument(
499 201baa17 Stavros Sachtouris
            'set container metadata (can be repeated)', '--meta')
500 2fe2672e Stavros Sachtouris
    )
501 7493ccb6 Stavros Sachtouris
502 1395c40e Stavros Sachtouris
    @errors.generic.all
503 1395c40e Stavros Sachtouris
    @errors.pithos.connection
504 1395c40e Stavros Sachtouris
    @errors.pithos.container
505 94bedc5b Stavros Sachtouris
    def _run(self, container):
506 915b99b5 Stavros Sachtouris
        self._optional_output(self.client.create_container(
507 915b99b5 Stavros Sachtouris
            container=container,
508 915b99b5 Stavros Sachtouris
            sizelimit=self['limit'],
509 915b99b5 Stavros Sachtouris
            versioning=self['versioning'],
510 915b99b5 Stavros Sachtouris
            metadata=self['meta']))
511 1395c40e Stavros Sachtouris
512 edab7ba7 Stavros Sachtouris
    def main(self, container=None):
513 1395c40e Stavros Sachtouris
        super(self.__class__, self)._run(container)
514 edab7ba7 Stavros Sachtouris
        if container and self.container != container:
515 1395c40e Stavros Sachtouris
            raiseCLIError('Invalid container name %s' % container, details=[
516 edab7ba7 Stavros Sachtouris
                'Did you mean "%s" ?' % self.container,
517 52edad0a Stavros Sachtouris
                'Use --container for names containing :'])
518 94bedc5b Stavros Sachtouris
        self._run(container)
519 7493ccb6 Stavros Sachtouris
520 234954d1 Stavros Sachtouris
521 3ae60112 Stavros Sachtouris
class _source_destination_command(_file_container_command):
522 2fe2672e Stavros Sachtouris
523 2fe2672e Stavros Sachtouris
    arguments = dict(
524 f40f0cb7 Stavros Sachtouris
        destination_account=ValueArgument('', ('a', '--dst-account')),
525 f40f0cb7 Stavros Sachtouris
        recursive=FlagArgument('', ('-R', '--recursive')),
526 761e0cbf Stavros Sachtouris
        prefix=FlagArgument('', '--with-prefix', default=''),
527 761e0cbf Stavros Sachtouris
        suffix=ValueArgument('', '--with-suffix', default=''),
528 761e0cbf Stavros Sachtouris
        add_prefix=ValueArgument('', '--add-prefix', default=''),
529 761e0cbf Stavros Sachtouris
        add_suffix=ValueArgument('', '--add-suffix', default=''),
530 761e0cbf Stavros Sachtouris
        prefix_replace=ValueArgument('', '--prefix-to-replace', default=''),
531 1d3f006b Stavros Sachtouris
        suffix_replace=ValueArgument('', '--suffix-to-replace', default=''),
532 2fe2672e Stavros Sachtouris
    )
533 7493ccb6 Stavros Sachtouris
534 f724cd35 Stavros Sachtouris
    def __init__(self, arguments={}, auth_base=None):
535 761e0cbf Stavros Sachtouris
        self.arguments.update(arguments)
536 f724cd35 Stavros Sachtouris
        super(_source_destination_command, self).__init__(
537 f724cd35 Stavros Sachtouris
            self.arguments, auth_base)
538 761e0cbf Stavros Sachtouris
539 300da0fb Stavros Sachtouris
    def _run(self, source_container___path, path_is_optional=False):
540 300da0fb Stavros Sachtouris
        super(_source_destination_command, self)._run(
541 300da0fb Stavros Sachtouris
            source_container___path,
542 300da0fb Stavros Sachtouris
            path_is_optional)
543 300da0fb Stavros Sachtouris
        self.dst_client = PithosClient(
544 300da0fb Stavros Sachtouris
            base_url=self.client.base_url,
545 300da0fb Stavros Sachtouris
            token=self.client.token,
546 300da0fb Stavros Sachtouris
            account=self['destination_account'] or self.client.account)
547 300da0fb Stavros Sachtouris
548 300da0fb Stavros Sachtouris
    @errors.generic.all
549 300da0fb Stavros Sachtouris
    @errors.pithos.account
550 300da0fb Stavros Sachtouris
    def _dest_container_path(self, dest_container_path):
551 300da0fb Stavros Sachtouris
        if self['destination_container']:
552 300da0fb Stavros Sachtouris
            self.dst_client.container = self['destination_container']
553 300da0fb Stavros Sachtouris
            return (self['destination_container'], dest_container_path)
554 300da0fb Stavros Sachtouris
        if dest_container_path:
555 300da0fb Stavros Sachtouris
            dst = dest_container_path.split(':')
556 300da0fb Stavros Sachtouris
            if len(dst) > 1:
557 300da0fb Stavros Sachtouris
                try:
558 300da0fb Stavros Sachtouris
                    self.dst_client.container = dst[0]
559 300da0fb Stavros Sachtouris
                    self.dst_client.get_container_info(dst[0])
560 300da0fb Stavros Sachtouris
                except ClientError as err:
561 300da0fb Stavros Sachtouris
                    if err.status in (404, 204):
562 300da0fb Stavros Sachtouris
                        raiseCLIError(
563 300da0fb Stavros Sachtouris
                            'Destination container %s not found' % dst[0])
564 300da0fb Stavros Sachtouris
                    raise
565 300da0fb Stavros Sachtouris
                else:
566 300da0fb Stavros Sachtouris
                    self.dst_client.container = dst[0]
567 300da0fb Stavros Sachtouris
                return (dst[0], dst[1])
568 300da0fb Stavros Sachtouris
            return(None, dst[0])
569 300da0fb Stavros Sachtouris
        raiseCLIError('No destination container:path provided')
570 300da0fb Stavros Sachtouris
571 ece4ae4b Stavros Sachtouris
    def _get_all(self, prefix):
572 ece4ae4b Stavros Sachtouris
        return self.client.container_get(prefix=prefix).json
573 ece4ae4b Stavros Sachtouris
574 1d3f006b Stavros Sachtouris
    def _get_src_objects(self, src_path, source_version=None):
575 6736f171 Stavros Sachtouris
        """Get a list of the source objects to be called
576 6736f171 Stavros Sachtouris

577 6736f171 Stavros Sachtouris
        :param src_path: (str) source path
578 6736f171 Stavros Sachtouris

579 6736f171 Stavros Sachtouris
        :returns: (method, params) a method that returns a list when called
580 6736f171 Stavros Sachtouris
        or (object) if it is a single object
581 6736f171 Stavros Sachtouris
        """
582 ece4ae4b Stavros Sachtouris
        if src_path and src_path[-1] == '/':
583 ece4ae4b Stavros Sachtouris
            src_path = src_path[:-1]
584 ece4ae4b Stavros Sachtouris
585 6736f171 Stavros Sachtouris
        if self['prefix']:
586 6736f171 Stavros Sachtouris
            return (self._get_all, dict(prefix=src_path))
587 ece4ae4b Stavros Sachtouris
        try:
588 1d3f006b Stavros Sachtouris
            srcobj = self.client.get_object_info(
589 1d3f006b Stavros Sachtouris
                src_path, version=source_version)
590 ece4ae4b Stavros Sachtouris
        except ClientError as srcerr:
591 6736f171 Stavros Sachtouris
            if srcerr.status == 404:
592 6736f171 Stavros Sachtouris
                raiseCLIError(
593 300da0fb Stavros Sachtouris
                    'Source object %s not in source container %s' % (
594 201baa17 Stavros Sachtouris
                        src_path, self.client.container),
595 6736f171 Stavros Sachtouris
                    details=['Hint: --with-prefix to match multiple objects'])
596 6736f171 Stavros Sachtouris
            elif srcerr.status not in (204,):
597 ece4ae4b Stavros Sachtouris
                raise
598 6736f171 Stavros Sachtouris
            return (self.client.list_objects, {})
599 300da0fb Stavros Sachtouris
600 6736f171 Stavros Sachtouris
        if self._is_dir(srcobj):
601 6736f171 Stavros Sachtouris
            if not self['recursive']:
602 6736f171 Stavros Sachtouris
                raiseCLIError(
603 300da0fb Stavros Sachtouris
                    'Object %s of cont. %s is a dir' % (
604 201baa17 Stavros Sachtouris
                        src_path, self.client.container),
605 6736f171 Stavros Sachtouris
                    details=['Use --recursive to access directories'])
606 6736f171 Stavros Sachtouris
            return (self._get_all, dict(prefix=src_path))
607 6736f171 Stavros Sachtouris
        srcobj['name'] = src_path
608 6736f171 Stavros Sachtouris
        return srcobj
609 6736f171 Stavros Sachtouris
610 1d3f006b Stavros Sachtouris
    def src_dst_pairs(self, dst_path, source_version=None):
611 1d3f006b Stavros Sachtouris
        src_iter = self._get_src_objects(self.path, source_version)
612 6736f171 Stavros Sachtouris
        src_N = isinstance(src_iter, tuple)
613 6736f171 Stavros Sachtouris
        add_prefix = self['add_prefix'].strip('/')
614 6736f171 Stavros Sachtouris
615 6736f171 Stavros Sachtouris
        if dst_path and dst_path.endswith('/'):
616 ece4ae4b Stavros Sachtouris
            dst_path = dst_path[:-1]
617 ece4ae4b Stavros Sachtouris
618 6736f171 Stavros Sachtouris
        try:
619 300da0fb Stavros Sachtouris
            dstobj = self.dst_client.get_object_info(dst_path)
620 6736f171 Stavros Sachtouris
        except ClientError as trgerr:
621 6736f171 Stavros Sachtouris
            if trgerr.status in (404,):
622 6736f171 Stavros Sachtouris
                if src_N:
623 6736f171 Stavros Sachtouris
                    raiseCLIError(
624 6736f171 Stavros Sachtouris
                        'Cannot merge multiple paths to path %s' % dst_path,
625 6736f171 Stavros Sachtouris
                        details=[
626 6736f171 Stavros Sachtouris
                            'Try to use / or a directory as destination',
627 3ae60112 Stavros Sachtouris
                            'or create the destination dir (/file mkdir)',
628 6736f171 Stavros Sachtouris
                            'or use a single object as source'])
629 6736f171 Stavros Sachtouris
            elif trgerr.status not in (204,):
630 6736f171 Stavros Sachtouris
                raise
631 6736f171 Stavros Sachtouris
        else:
632 6736f171 Stavros Sachtouris
            if self._is_dir(dstobj):
633 6736f171 Stavros Sachtouris
                add_prefix = '%s/%s' % (dst_path.strip('/'), add_prefix)
634 6736f171 Stavros Sachtouris
            elif src_N:
635 6736f171 Stavros Sachtouris
                raiseCLIError(
636 6736f171 Stavros Sachtouris
                    'Cannot merge multiple paths to path' % dst_path,
637 6736f171 Stavros Sachtouris
                    details=[
638 6736f171 Stavros Sachtouris
                        'Try to use / or a directory as destination',
639 3ae60112 Stavros Sachtouris
                        'or create the destination dir (/file mkdir)',
640 6736f171 Stavros Sachtouris
                        'or use a single object as source'])
641 6736f171 Stavros Sachtouris
642 6736f171 Stavros Sachtouris
        if src_N:
643 6736f171 Stavros Sachtouris
            (method, kwargs) = src_iter
644 6736f171 Stavros Sachtouris
            for obj in method(**kwargs):
645 6736f171 Stavros Sachtouris
                name = obj['name']
646 6736f171 Stavros Sachtouris
                if name.endswith(self['suffix']):
647 6736f171 Stavros Sachtouris
                    yield (name, self._get_new_object(name, add_prefix))
648 6736f171 Stavros Sachtouris
        elif src_iter['name'].endswith(self['suffix']):
649 6736f171 Stavros Sachtouris
            name = src_iter['name']
650 6736f171 Stavros Sachtouris
            yield (name, self._get_new_object(dst_path or name, add_prefix))
651 6736f171 Stavros Sachtouris
        else:
652 6736f171 Stavros Sachtouris
            raiseCLIError('Source path %s conflicts with suffix %s' % (
653 201baa17 Stavros Sachtouris
                src_iter['name'], self['suffix']))
654 ece4ae4b Stavros Sachtouris
655 6736f171 Stavros Sachtouris
    def _get_new_object(self, obj, add_prefix):
656 6736f171 Stavros Sachtouris
        if self['prefix_replace'] and obj.startswith(self['prefix_replace']):
657 6736f171 Stavros Sachtouris
            obj = obj[len(self['prefix_replace']):]
658 6736f171 Stavros Sachtouris
        if self['suffix_replace'] and obj.endswith(self['suffix_replace']):
659 6736f171 Stavros Sachtouris
            obj = obj[:-len(self['suffix_replace'])]
660 6736f171 Stavros Sachtouris
        return add_prefix + obj + self['add_suffix']
661 efdee310 Stavros Sachtouris
662 761e0cbf Stavros Sachtouris
663 761e0cbf Stavros Sachtouris
@command(pithos_cmds)
664 915b99b5 Stavros Sachtouris
class file_copy(_source_destination_command, _optional_output_cmd):
665 761e0cbf Stavros Sachtouris
    """Copy objects from container to (another) container
666 761e0cbf Stavros Sachtouris
    Semantics:
667 761e0cbf Stavros Sachtouris
    copy cont:path dir
668 761e0cbf Stavros Sachtouris
    .   transfer path as dir/path
669 761e0cbf Stavros Sachtouris
    copy cont:path cont2:
670 761e0cbf Stavros Sachtouris
    .   trasnfer all <obj> prefixed with path to container cont2
671 761e0cbf Stavros Sachtouris
    copy cont:path [cont2:]path2
672 761e0cbf Stavros Sachtouris
    .   transfer path to path2
673 761e0cbf Stavros Sachtouris
    Use options:
674 761e0cbf Stavros Sachtouris
    1. <container1>:<path1> [container2:]<path2> : if container2 is not given,
675 761e0cbf Stavros Sachtouris
    destination is container1:path2
676 761e0cbf Stavros Sachtouris
    2. <container>:<path1> <path2> : make a copy in the same container
677 761e0cbf Stavros Sachtouris
    3. Can use --container= instead of <container1>
678 761e0cbf Stavros Sachtouris
    """
679 761e0cbf Stavros Sachtouris
680 761e0cbf Stavros Sachtouris
    arguments = dict(
681 300da0fb Stavros Sachtouris
        destination_account=ValueArgument(
682 201baa17 Stavros Sachtouris
            'Account to copy to', ('-a', '--dst-account')),
683 761e0cbf Stavros Sachtouris
        destination_container=ValueArgument(
684 761e0cbf Stavros Sachtouris
            'use it if destination container name contains a : character',
685 f40f0cb7 Stavros Sachtouris
            ('-D', '--dst-container')),
686 761e0cbf Stavros Sachtouris
        public=ValueArgument('make object publicly accessible', '--public'),
687 761e0cbf Stavros Sachtouris
        content_type=ValueArgument(
688 201baa17 Stavros Sachtouris
            'change object\'s content type', '--content-type'),
689 761e0cbf Stavros Sachtouris
        recursive=FlagArgument(
690 201baa17 Stavros Sachtouris
            'copy directory and contents', ('-R', '--recursive')),
691 761e0cbf Stavros Sachtouris
        prefix=FlagArgument(
692 761e0cbf Stavros Sachtouris
            'Match objects prefixed with src path (feels like src_path*)',
693 761e0cbf Stavros Sachtouris
            '--with-prefix',
694 761e0cbf Stavros Sachtouris
            default=''),
695 761e0cbf Stavros Sachtouris
        suffix=ValueArgument(
696 201baa17 Stavros Sachtouris
            'Suffix of source objects (feels like *suffix)', '--with-suffix',
697 761e0cbf Stavros Sachtouris
            default=''),
698 761e0cbf Stavros Sachtouris
        add_prefix=ValueArgument('Prefix targets', '--add-prefix', default=''),
699 761e0cbf Stavros Sachtouris
        add_suffix=ValueArgument('Suffix targets', '--add-suffix', default=''),
700 761e0cbf Stavros Sachtouris
        prefix_replace=ValueArgument(
701 761e0cbf Stavros Sachtouris
            'Prefix of src to replace with dst path + add_prefix, if matched',
702 761e0cbf Stavros Sachtouris
            '--prefix-to-replace',
703 761e0cbf Stavros Sachtouris
            default=''),
704 761e0cbf Stavros Sachtouris
        suffix_replace=ValueArgument(
705 761e0cbf Stavros Sachtouris
            'Suffix of src to replace with add_suffix, if matched',
706 761e0cbf Stavros Sachtouris
            '--suffix-to-replace',
707 1d3f006b Stavros Sachtouris
            default=''),
708 1d3f006b Stavros Sachtouris
        source_version=ValueArgument(
709 201baa17 Stavros Sachtouris
            'copy specific version', ('-S', '--source-version'))
710 761e0cbf Stavros Sachtouris
    )
711 761e0cbf Stavros Sachtouris
712 1395c40e Stavros Sachtouris
    @errors.generic.all
713 1395c40e Stavros Sachtouris
    @errors.pithos.connection
714 1395c40e Stavros Sachtouris
    @errors.pithos.container
715 300da0fb Stavros Sachtouris
    @errors.pithos.account
716 300da0fb Stavros Sachtouris
    def _run(self, dst_path):
717 1395c40e Stavros Sachtouris
        no_source_object = True
718 300da0fb Stavros Sachtouris
        src_account = self.client.account if (
719 300da0fb Stavros Sachtouris
            self['destination_account']) else None
720 1d3f006b Stavros Sachtouris
        for src_obj, dst_obj in self.src_dst_pairs(
721 1d3f006b Stavros Sachtouris
                dst_path, self['source_version']):
722 1395c40e Stavros Sachtouris
            no_source_object = False
723 55c75058 Stavros Sachtouris
            r = self.dst_client.copy_object(
724 300da0fb Stavros Sachtouris
                src_container=self.client.container,
725 3a066af4 Stavros Sachtouris
                src_object=src_obj,
726 300da0fb Stavros Sachtouris
                dst_container=self.dst_client.container,
727 300da0fb Stavros Sachtouris
                dst_object=dst_obj,
728 300da0fb Stavros Sachtouris
                source_account=src_account,
729 1395c40e Stavros Sachtouris
                source_version=self['source_version'],
730 1395c40e Stavros Sachtouris
                public=self['public'],
731 1395c40e Stavros Sachtouris
                content_type=self['content_type'])
732 1395c40e Stavros Sachtouris
        if no_source_object:
733 1395c40e Stavros Sachtouris
            raiseCLIError('No object %s in container %s' % (
734 201baa17 Stavros Sachtouris
                self.path, self.container))
735 915b99b5 Stavros Sachtouris
        self._optional_output(r)
736 1395c40e Stavros Sachtouris
737 de73876b Stavros Sachtouris
    def main(
738 e2d7b883 Stavros Sachtouris
            self, source_container___path,
739 24ff0a35 Stavros Sachtouris
            destination_container___path=None):
740 3ae60112 Stavros Sachtouris
        super(file_copy, self)._run(
741 1395c40e Stavros Sachtouris
            source_container___path,
742 1395c40e Stavros Sachtouris
            path_is_optional=False)
743 1395c40e Stavros Sachtouris
        (dst_cont, dst_path) = self._dest_container_path(
744 0e4ee6d1 Stavros Sachtouris
            destination_container___path)
745 300da0fb Stavros Sachtouris
        self.dst_client.container = dst_cont or self.container
746 300da0fb Stavros Sachtouris
        self._run(dst_path=dst_path or '')
747 7493ccb6 Stavros Sachtouris
748 234954d1 Stavros Sachtouris
749 d486baec Stavros Sachtouris
@command(pithos_cmds)
750 915b99b5 Stavros Sachtouris
class file_move(_source_destination_command, _optional_output_cmd):
751 761e0cbf Stavros Sachtouris
    """Move/rename objects from container to (another) container
752 8249ee0f Stavros Sachtouris
    Semantics:
753 761e0cbf Stavros Sachtouris
    move cont:path dir
754 761e0cbf Stavros Sachtouris
    .   rename path as dir/path
755 8249ee0f Stavros Sachtouris
    move cont:path cont2:
756 761e0cbf Stavros Sachtouris
    .   trasnfer all <obj> prefixed with path to container cont2
757 761e0cbf Stavros Sachtouris
    move cont:path [cont2:]path2
758 761e0cbf Stavros Sachtouris
    .   transfer path to path2
759 2fe2672e Stavros Sachtouris
    Use options:
760 761e0cbf Stavros Sachtouris
    1. <container1>:<path1> [container2:]<path2> : if container2 is not given,
761 795bf206 Stavros Sachtouris
    destination is container1:path2
762 761e0cbf Stavros Sachtouris
    2. <container>:<path1> <path2> : move in the same container
763 8249ee0f Stavros Sachtouris
    3. Can use --container= instead of <container1>
764 2fe2672e Stavros Sachtouris
    """
765 2fe2672e Stavros Sachtouris
766 2fe2672e Stavros Sachtouris
    arguments = dict(
767 300da0fb Stavros Sachtouris
        destination_account=ValueArgument(
768 201baa17 Stavros Sachtouris
            'Account to move to', ('-a', '--dst-account')),
769 0e4ee6d1 Stavros Sachtouris
        destination_container=ValueArgument(
770 0e4ee6d1 Stavros Sachtouris
            'use it if destination container name contains a : character',
771 f40f0cb7 Stavros Sachtouris
            ('-D', '--dst-container')),
772 761e0cbf Stavros Sachtouris
        public=ValueArgument('make object publicly accessible', '--public'),
773 761e0cbf Stavros Sachtouris
        content_type=ValueArgument(
774 201baa17 Stavros Sachtouris
            'change object\'s content type', '--content-type'),
775 761e0cbf Stavros Sachtouris
        recursive=FlagArgument(
776 201baa17 Stavros Sachtouris
            'copy directory and contents', ('-R', '--recursive')),
777 761e0cbf Stavros Sachtouris
        prefix=FlagArgument(
778 761e0cbf Stavros Sachtouris
            'Match objects prefixed with src path (feels like src_path*)',
779 761e0cbf Stavros Sachtouris
            '--with-prefix',
780 761e0cbf Stavros Sachtouris
            default=''),
781 761e0cbf Stavros Sachtouris
        suffix=ValueArgument(
782 201baa17 Stavros Sachtouris
            'Suffix of source objects (feels like *suffix)', '--with-suffix',
783 761e0cbf Stavros Sachtouris
            default=''),
784 761e0cbf Stavros Sachtouris
        add_prefix=ValueArgument('Prefix targets', '--add-prefix', default=''),
785 761e0cbf Stavros Sachtouris
        add_suffix=ValueArgument('Suffix targets', '--add-suffix', default=''),
786 761e0cbf Stavros Sachtouris
        prefix_replace=ValueArgument(
787 761e0cbf Stavros Sachtouris
            'Prefix of src to replace with dst path + add_prefix, if matched',
788 761e0cbf Stavros Sachtouris
            '--prefix-to-replace',
789 761e0cbf Stavros Sachtouris
            default=''),
790 761e0cbf Stavros Sachtouris
        suffix_replace=ValueArgument(
791 761e0cbf Stavros Sachtouris
            'Suffix of src to replace with add_suffix, if matched',
792 761e0cbf Stavros Sachtouris
            '--suffix-to-replace',
793 915b99b5 Stavros Sachtouris
            default='')
794 2fe2672e Stavros Sachtouris
    )
795 7493ccb6 Stavros Sachtouris
796 1395c40e Stavros Sachtouris
    @errors.generic.all
797 1395c40e Stavros Sachtouris
    @errors.pithos.connection
798 1395c40e Stavros Sachtouris
    @errors.pithos.container
799 300da0fb Stavros Sachtouris
    def _run(self, dst_path):
800 1395c40e Stavros Sachtouris
        no_source_object = True
801 300da0fb Stavros Sachtouris
        src_account = self.client.account if (
802 300da0fb Stavros Sachtouris
            self['destination_account']) else None
803 300da0fb Stavros Sachtouris
        for src_obj, dst_obj in self.src_dst_pairs(dst_path):
804 1395c40e Stavros Sachtouris
            no_source_object = False
805 55c75058 Stavros Sachtouris
            r = self.dst_client.move_object(
806 1395c40e Stavros Sachtouris
                src_container=self.container,
807 300da0fb Stavros Sachtouris
                src_object=src_obj,
808 300da0fb Stavros Sachtouris
                dst_container=self.dst_client.container,
809 300da0fb Stavros Sachtouris
                dst_object=dst_obj,
810 4f266635 Stavros Sachtouris
                source_account=src_account,
811 1395c40e Stavros Sachtouris
                public=self['public'],
812 1395c40e Stavros Sachtouris
                content_type=self['content_type'])
813 1395c40e Stavros Sachtouris
        if no_source_object:
814 1395c40e Stavros Sachtouris
            raiseCLIError('No object %s in container %s' % (
815 1395c40e Stavros Sachtouris
                self.path,
816 1395c40e Stavros Sachtouris
                self.container))
817 915b99b5 Stavros Sachtouris
        self._optional_output(r)
818 1395c40e Stavros Sachtouris
819 de73876b Stavros Sachtouris
    def main(
820 e2d7b883 Stavros Sachtouris
            self, source_container___path,
821 24ff0a35 Stavros Sachtouris
            destination_container___path=None):
822 1395c40e Stavros Sachtouris
        super(self.__class__, self)._run(
823 1395c40e Stavros Sachtouris
            source_container___path,
824 1395c40e Stavros Sachtouris
            path_is_optional=False)
825 45b70023 Stavros Sachtouris
        (dst_cont, dst_path) = self._dest_container_path(
826 1395c40e Stavros Sachtouris
            destination_container___path)
827 300da0fb Stavros Sachtouris
        (dst_cont, dst_path) = self._dest_container_path(
828 300da0fb Stavros Sachtouris
            destination_container___path)
829 300da0fb Stavros Sachtouris
        self.dst_client.container = dst_cont or self.container
830 300da0fb Stavros Sachtouris
        self._run(dst_path=dst_path or '')
831 7493ccb6 Stavros Sachtouris
832 234954d1 Stavros Sachtouris
833 d486baec Stavros Sachtouris
@command(pithos_cmds)
834 915b99b5 Stavros Sachtouris
class file_append(_file_container_command, _optional_output_cmd):
835 aaca2ef4 Stavros Sachtouris
    """Append local file to (existing) remote object
836 aaca2ef4 Stavros Sachtouris
    The remote object should exist.
837 aaca2ef4 Stavros Sachtouris
    If the remote object is a directory, it is transformed into a file.
838 aaca2ef4 Stavros Sachtouris
    In the later case, objects under the directory remain intact.
839 aaca2ef4 Stavros Sachtouris
    """
840 7493ccb6 Stavros Sachtouris
841 2fe2672e Stavros Sachtouris
    arguments = dict(
842 2fe2672e Stavros Sachtouris
        progress_bar=ProgressBarArgument(
843 2fe2672e Stavros Sachtouris
            'do not show progress bar',
844 f40f0cb7 Stavros Sachtouris
            ('-N', '--no-progress-bar'),
845 915b99b5 Stavros Sachtouris
            default=False)
846 2fe2672e Stavros Sachtouris
    )
847 486f7af1 Stavros Sachtouris
848 ca092af4 Stavros Sachtouris
    @errors.generic.all
849 ca092af4 Stavros Sachtouris
    @errors.pithos.connection
850 ca092af4 Stavros Sachtouris
    @errors.pithos.container
851 ca092af4 Stavros Sachtouris
    @errors.pithos.object_path
852 ca092af4 Stavros Sachtouris
    def _run(self, local_path):
853 ca092af4 Stavros Sachtouris
        (progress_bar, upload_cb) = self._safe_progress_bar('Appending')
854 7493ccb6 Stavros Sachtouris
        try:
855 e876ac62 Stavros Sachtouris
            f = open(local_path, 'rb')
856 915b99b5 Stavros Sachtouris
            self._optional_output(
857 915b99b5 Stavros Sachtouris
                self.client.append_object(self.path, f, upload_cb))
858 ca092af4 Stavros Sachtouris
        except Exception:
859 ca092af4 Stavros Sachtouris
            self._safe_progress_bar_finish(progress_bar)
860 ca092af4 Stavros Sachtouris
            raise
861 852a22e7 Stavros Sachtouris
        finally:
862 ca092af4 Stavros Sachtouris
            self._safe_progress_bar_finish(progress_bar)
863 ca092af4 Stavros Sachtouris
864 ca092af4 Stavros Sachtouris
    def main(self, local_path, container___path):
865 ca092af4 Stavros Sachtouris
        super(self.__class__, self)._run(
866 201baa17 Stavros Sachtouris
            container___path, path_is_optional=False)
867 ca092af4 Stavros Sachtouris
        self._run(local_path)
868 7493ccb6 Stavros Sachtouris
869 234954d1 Stavros Sachtouris
870 d486baec Stavros Sachtouris
@command(pithos_cmds)
871 915b99b5 Stavros Sachtouris
class file_truncate(_file_container_command, _optional_output_cmd):
872 aaca2ef4 Stavros Sachtouris
    """Truncate remote file up to a size (default is 0)"""
873 7493ccb6 Stavros Sachtouris
874 ca092af4 Stavros Sachtouris
    @errors.generic.all
875 ca092af4 Stavros Sachtouris
    @errors.pithos.connection
876 ca092af4 Stavros Sachtouris
    @errors.pithos.container
877 ca092af4 Stavros Sachtouris
    @errors.pithos.object_path
878 ca092af4 Stavros Sachtouris
    @errors.pithos.object_size
879 ca092af4 Stavros Sachtouris
    def _run(self, size=0):
880 915b99b5 Stavros Sachtouris
        self._optional_output(self.client.truncate_object(self.path, size))
881 ca092af4 Stavros Sachtouris
882 7493ccb6 Stavros Sachtouris
    def main(self, container___path, size=0):
883 ca092af4 Stavros Sachtouris
        super(self.__class__, self)._run(container___path)
884 ca092af4 Stavros Sachtouris
        self._run(size=size)
885 7493ccb6 Stavros Sachtouris
886 234954d1 Stavros Sachtouris
887 d486baec Stavros Sachtouris
@command(pithos_cmds)
888 915b99b5 Stavros Sachtouris
class file_overwrite(_file_container_command, _optional_output_cmd):
889 aaca2ef4 Stavros Sachtouris
    """Overwrite part (from start to end) of a remote file
890 aaca2ef4 Stavros Sachtouris
    overwrite local-path container 10 20
891 aaca2ef4 Stavros Sachtouris
    .   will overwrite bytes from 10 to 20 of a remote file with the same name
892 aaca2ef4 Stavros Sachtouris
    .   as local-path basename
893 aaca2ef4 Stavros Sachtouris
    overwrite local-path container:path 10 20
894 aaca2ef4 Stavros Sachtouris
    .   will overwrite as above, but the remote file is named path
895 aaca2ef4 Stavros Sachtouris
    """
896 7493ccb6 Stavros Sachtouris
897 2fe2672e Stavros Sachtouris
    arguments = dict(
898 2fe2672e Stavros Sachtouris
        progress_bar=ProgressBarArgument(
899 2fe2672e Stavros Sachtouris
            'do not show progress bar',
900 f40f0cb7 Stavros Sachtouris
            ('-N', '--no-progress-bar'),
901 2fe2672e Stavros Sachtouris
            default=False)
902 2fe2672e Stavros Sachtouris
    )
903 486f7af1 Stavros Sachtouris
904 ca092af4 Stavros Sachtouris
    def _open_file(self, local_path, start):
905 ca092af4 Stavros Sachtouris
        f = open(path.abspath(local_path), 'rb')
906 ca092af4 Stavros Sachtouris
        f.seek(0, 2)
907 ca092af4 Stavros Sachtouris
        f_size = f.tell()
908 ca092af4 Stavros Sachtouris
        f.seek(start, 0)
909 ca092af4 Stavros Sachtouris
        return (f, f_size)
910 ca092af4 Stavros Sachtouris
911 ca092af4 Stavros Sachtouris
    @errors.generic.all
912 ca092af4 Stavros Sachtouris
    @errors.pithos.connection
913 ca092af4 Stavros Sachtouris
    @errors.pithos.container
914 ca092af4 Stavros Sachtouris
    @errors.pithos.object_path
915 ca092af4 Stavros Sachtouris
    @errors.pithos.object_size
916 ca092af4 Stavros Sachtouris
    def _run(self, local_path, start, end):
917 ca092af4 Stavros Sachtouris
        (start, end) = (int(start), int(end))
918 ca092af4 Stavros Sachtouris
        (f, f_size) = self._open_file(local_path, start)
919 ca092af4 Stavros Sachtouris
        (progress_bar, upload_cb) = self._safe_progress_bar(
920 ca092af4 Stavros Sachtouris
            'Overwrite %s bytes' % (end - start))
921 aaca2ef4 Stavros Sachtouris
        try:
922 915b99b5 Stavros Sachtouris
            self._optional_output(self.client.overwrite_object(
923 8fa6ef6a Stavros Sachtouris
                obj=self.path,
924 234954d1 Stavros Sachtouris
                start=start,
925 234954d1 Stavros Sachtouris
                end=end,
926 234954d1 Stavros Sachtouris
                source_file=f,
927 915b99b5 Stavros Sachtouris
                upload_cb=upload_cb))
928 852a22e7 Stavros Sachtouris
        finally:
929 ca092af4 Stavros Sachtouris
            self._safe_progress_bar_finish(progress_bar)
930 ca092af4 Stavros Sachtouris
931 52edad0a Stavros Sachtouris
    def main(self, local_path, container___path, start, end):
932 52edad0a Stavros Sachtouris
        super(self.__class__, self)._run(
933 201baa17 Stavros Sachtouris
            container___path, path_is_optional=None)
934 52edad0a Stavros Sachtouris
        self.path = self.path or path.basename(local_path)
935 ca092af4 Stavros Sachtouris
        self._run(local_path=local_path, start=start, end=end)
936 7493ccb6 Stavros Sachtouris
937 234954d1 Stavros Sachtouris
938 d486baec Stavros Sachtouris
@command(pithos_cmds)
939 915b99b5 Stavros Sachtouris
class file_manifest(_file_container_command, _optional_output_cmd):
940 8fa6ef6a Stavros Sachtouris
    """Create a remote file of uploaded parts by manifestation
941 35b52b0e Stavros Sachtouris
    Remains functional for compatibility with OOS Storage. Users are advised
942 35b52b0e Stavros Sachtouris
    to use the upload command instead.
943 35b52b0e Stavros Sachtouris
    Manifestation is a compliant process for uploading large files. The files
944 35b52b0e Stavros Sachtouris
    have to be chunked in smalled files and uploaded as <prefix><increment>
945 35b52b0e Stavros Sachtouris
    where increment is 1, 2, ...
946 35b52b0e Stavros Sachtouris
    Finally, the manifest command glues partial files together in one file
947 35b52b0e Stavros Sachtouris
    named <prefix>
948 35b52b0e Stavros Sachtouris
    The upload command is faster, easier and more intuitive than manifest
949 8fa6ef6a Stavros Sachtouris
    """
950 486f7af1 Stavros Sachtouris
951 2fe2672e Stavros Sachtouris
    arguments = dict(
952 2fe2672e Stavros Sachtouris
        etag=ValueArgument('check written data', '--etag'),
953 2fe2672e Stavros Sachtouris
        content_encoding=ValueArgument(
954 201baa17 Stavros Sachtouris
            'set MIME content type', '--content-encoding'),
955 2fe2672e Stavros Sachtouris
        content_disposition=ValueArgument(
956 201baa17 Stavros Sachtouris
            'the presentation style of the object', '--content-disposition'),
957 8fa6ef6a Stavros Sachtouris
        content_type=ValueArgument(
958 201baa17 Stavros Sachtouris
            'specify content type', '--content-type',
959 8fa6ef6a Stavros Sachtouris
            default='application/octet-stream'),
960 2fe2672e Stavros Sachtouris
        sharing=SharingArgument(
961 de73876b Stavros Sachtouris
            '\n'.join([
962 de73876b Stavros Sachtouris
                'define object sharing policy',
963 de73876b Stavros Sachtouris
                '    ( "read=user1,grp1,user2,... write=user1,grp2,..." )']),
964 2fe2672e Stavros Sachtouris
            '--sharing'),
965 2fe2672e Stavros Sachtouris
        public=FlagArgument('make object publicly accessible', '--public')
966 2fe2672e Stavros Sachtouris
    )
967 234954d1 Stavros Sachtouris
968 68858765 Stavros Sachtouris
    @errors.generic.all
969 68858765 Stavros Sachtouris
    @errors.pithos.connection
970 68858765 Stavros Sachtouris
    @errors.pithos.container
971 68858765 Stavros Sachtouris
    @errors.pithos.object_path
972 68858765 Stavros Sachtouris
    def _run(self):
973 915b99b5 Stavros Sachtouris
        self._optional_output(self.client.create_object_by_manifestation(
974 68858765 Stavros Sachtouris
            self.path,
975 68858765 Stavros Sachtouris
            content_encoding=self['content_encoding'],
976 68858765 Stavros Sachtouris
            content_disposition=self['content_disposition'],
977 68858765 Stavros Sachtouris
            content_type=self['content_type'],
978 68858765 Stavros Sachtouris
            sharing=self['sharing'],
979 915b99b5 Stavros Sachtouris
            public=self['public']))
980 68858765 Stavros Sachtouris
981 7493ccb6 Stavros Sachtouris
    def main(self, container___path):
982 68858765 Stavros Sachtouris
        super(self.__class__, self)._run(
983 201baa17 Stavros Sachtouris
            container___path, path_is_optional=False)
984 68858765 Stavros Sachtouris
        self.run()
985 7493ccb6 Stavros Sachtouris
986 234954d1 Stavros Sachtouris
987 d486baec Stavros Sachtouris
@command(pithos_cmds)
988 915b99b5 Stavros Sachtouris
class file_upload(_file_container_command, _optional_output_cmd):
989 7493ccb6 Stavros Sachtouris
    """Upload a file"""
990 7493ccb6 Stavros Sachtouris
991 2fe2672e Stavros Sachtouris
    arguments = dict(
992 2fe2672e Stavros Sachtouris
        use_hashes=FlagArgument(
993 201baa17 Stavros Sachtouris
            'provide hashmap file instead of data', '--use-hashes'),
994 2fe2672e Stavros Sachtouris
        etag=ValueArgument('check written data', '--etag'),
995 2fe2672e Stavros Sachtouris
        unchunked=FlagArgument('avoid chunked transfer mode', '--unchunked'),
996 2fe2672e Stavros Sachtouris
        content_encoding=ValueArgument(
997 201baa17 Stavros Sachtouris
            'set MIME content type', '--content-encoding'),
998 2fe2672e Stavros Sachtouris
        content_disposition=ValueArgument(
999 201baa17 Stavros Sachtouris
            'specify objects presentation style', '--content-disposition'),
1000 2fe2672e Stavros Sachtouris
        content_type=ValueArgument('specify content type', '--content-type'),
1001 2fe2672e Stavros Sachtouris
        sharing=SharingArgument(
1002 de73876b Stavros Sachtouris
            help='\n'.join([
1003 de73876b Stavros Sachtouris
                'define sharing object policy',
1004 de73876b Stavros Sachtouris
                '( "read=user1,grp1,user2,... write=user1,grp2,... )']),
1005 2fe2672e Stavros Sachtouris
            parsed_name='--sharing'),
1006 2fe2672e Stavros Sachtouris
        public=FlagArgument('make object publicly accessible', '--public'),
1007 2fe2672e Stavros Sachtouris
        poolsize=IntArgument('set pool size', '--with-pool-size'),
1008 2fe2672e Stavros Sachtouris
        progress_bar=ProgressBarArgument(
1009 2fe2672e Stavros Sachtouris
            'do not show progress bar',
1010 8741c407 Stavros Sachtouris
            ('-N', '--no-progress-bar'),
1011 706fc940 Stavros Sachtouris
            default=False),
1012 2d158d8b Stavros Sachtouris
        overwrite=FlagArgument('Force (over)write', ('-f', '--force')),
1013 2d158d8b Stavros Sachtouris
        recursive=FlagArgument(
1014 2d158d8b Stavros Sachtouris
            'Recursively upload directory *contents* + subdirectories',
1015 915b99b5 Stavros Sachtouris
            ('-R', '--recursive'))
1016 2fe2672e Stavros Sachtouris
    )
1017 7493ccb6 Stavros Sachtouris
1018 14b25e00 Stavros Sachtouris
    def _check_container_limit(self, path):
1019 14b25e00 Stavros Sachtouris
        cl_dict = self.client.get_container_limit()
1020 14b25e00 Stavros Sachtouris
        container_limit = int(cl_dict['x-container-policy-quota'])
1021 2b74fc1d Stavros Sachtouris
        r = self.client.container_get()
1022 2b74fc1d Stavros Sachtouris
        used_bytes = sum(int(o['bytes']) for o in r.json)
1023 14b25e00 Stavros Sachtouris
        path_size = get_path_size(path)
1024 c8d8c6c9 Stavros Sachtouris
        if container_limit and path_size > (container_limit - used_bytes):
1025 2b74fc1d Stavros Sachtouris
            raiseCLIError(
1026 2b74fc1d Stavros Sachtouris
                'Container(%s) (limit(%s) - used(%s)) < size(%s) of %s' % (
1027 14b25e00 Stavros Sachtouris
                    self.client.container,
1028 14b25e00 Stavros Sachtouris
                    format_size(container_limit),
1029 2b74fc1d Stavros Sachtouris
                    format_size(used_bytes),
1030 14b25e00 Stavros Sachtouris
                    format_size(path_size),
1031 14b25e00 Stavros Sachtouris
                    path),
1032 14b25e00 Stavros Sachtouris
                importance=1, details=[
1033 14b25e00 Stavros Sachtouris
                    'Check accound limit: /file quota',
1034 14b25e00 Stavros Sachtouris
                    'Check container limit:',
1035 14b25e00 Stavros Sachtouris
                    '\t/file containerlimit get %s' % self.client.container,
1036 14b25e00 Stavros Sachtouris
                    'Increase container limit:',
1037 14b25e00 Stavros Sachtouris
                    '\t/file containerlimit set <new limit> %s' % (
1038 14b25e00 Stavros Sachtouris
                        self.client.container)])
1039 14b25e00 Stavros Sachtouris
1040 b666ef82 Stavros Sachtouris
    def _path_pairs(self, local_path, remote_path):
1041 b666ef82 Stavros Sachtouris
        """Get pairs of local and remote paths"""
1042 b666ef82 Stavros Sachtouris
        lpath = path.abspath(local_path)
1043 b666ef82 Stavros Sachtouris
        short_path = lpath.split(path.sep)[-1]
1044 b666ef82 Stavros Sachtouris
        rpath = remote_path or short_path
1045 b666ef82 Stavros Sachtouris
        if path.isdir(lpath):
1046 2d158d8b Stavros Sachtouris
            if not self['recursive']:
1047 2d158d8b Stavros Sachtouris
                raiseCLIError('%s is a directory' % lpath, details=[
1048 2d158d8b Stavros Sachtouris
                    'Use -R to upload directory contents'])
1049 b666ef82 Stavros Sachtouris
            robj = self.client.container_get(path=rpath)
1050 b666ef82 Stavros Sachtouris
            if robj.json and not self['overwrite']:
1051 b666ef82 Stavros Sachtouris
                raiseCLIError(
1052 b666ef82 Stavros Sachtouris
                    'Objects prefixed with %s already exist' % rpath,
1053 b666ef82 Stavros Sachtouris
                    importance=1,
1054 b666ef82 Stavros Sachtouris
                    details=['Existing objects:'] + ['\t%s:\t%s' % (
1055 b666ef82 Stavros Sachtouris
                        o['content_type'][12:],
1056 b666ef82 Stavros Sachtouris
                        o['name']) for o in robj.json] + [
1057 b666ef82 Stavros Sachtouris
                        'Use -f to add, overwrite or resume'])
1058 b666ef82 Stavros Sachtouris
            if not self['overwrite']:
1059 b666ef82 Stavros Sachtouris
                try:
1060 b666ef82 Stavros Sachtouris
                    topobj = self.client.get_object_info(rpath)
1061 b666ef82 Stavros Sachtouris
                    if not self._is_dir(topobj):
1062 b666ef82 Stavros Sachtouris
                        raiseCLIError(
1063 b666ef82 Stavros Sachtouris
                            'Object %s exists but it is not a dir' % rpath,
1064 b666ef82 Stavros Sachtouris
                            importance=1, details=['Use -f to overwrite'])
1065 b666ef82 Stavros Sachtouris
                except ClientError as ce:
1066 b666ef82 Stavros Sachtouris
                    if ce.status != 404:
1067 b666ef82 Stavros Sachtouris
                        raise
1068 2d158d8b Stavros Sachtouris
            self._check_container_limit(lpath)
1069 b666ef82 Stavros Sachtouris
            prev = ''
1070 b666ef82 Stavros Sachtouris
            for top, subdirs, files in walk(lpath):
1071 b666ef82 Stavros Sachtouris
                if top != prev:
1072 b666ef82 Stavros Sachtouris
                    prev = top
1073 b666ef82 Stavros Sachtouris
                    try:
1074 b666ef82 Stavros Sachtouris
                        rel_path = rpath + top.split(lpath)[1]
1075 b666ef82 Stavros Sachtouris
                    except IndexError:
1076 b666ef82 Stavros Sachtouris
                        rel_path = rpath
1077 b666ef82 Stavros Sachtouris
                    print('mkdir %s:%s' % (self.client.container, rel_path))
1078 b666ef82 Stavros Sachtouris
                    self.client.create_directory(rel_path)
1079 b666ef82 Stavros Sachtouris
                for f in files:
1080 b666ef82 Stavros Sachtouris
                    fpath = path.join(top, f)
1081 b666ef82 Stavros Sachtouris
                    if path.isfile(fpath):
1082 14b25e00 Stavros Sachtouris
                        yield open(fpath, 'rb'), '%s/%s' % (rel_path, f)
1083 b666ef82 Stavros Sachtouris
                    else:
1084 b666ef82 Stavros Sachtouris
                        print('%s is not a regular file' % fpath)
1085 b666ef82 Stavros Sachtouris
        else:
1086 14b25e00 Stavros Sachtouris
            if not path.isfile(lpath):
1087 14b25e00 Stavros Sachtouris
                raiseCLIError('%s is not a regular file' % lpath)
1088 b666ef82 Stavros Sachtouris
            try:
1089 b666ef82 Stavros Sachtouris
                robj = self.client.get_object_info(rpath)
1090 b666ef82 Stavros Sachtouris
                if remote_path and self._is_dir(robj):
1091 b666ef82 Stavros Sachtouris
                    rpath += '/%s' % short_path
1092 b666ef82 Stavros Sachtouris
                    self.client.get_object_info(rpath)
1093 b666ef82 Stavros Sachtouris
                if not self['overwrite']:
1094 b666ef82 Stavros Sachtouris
                    raiseCLIError(
1095 b666ef82 Stavros Sachtouris
                        'Object %s already exists' % rpath,
1096 b666ef82 Stavros Sachtouris
                        importance=1,
1097 b666ef82 Stavros Sachtouris
                        details=['use -f to overwrite or resume'])
1098 b666ef82 Stavros Sachtouris
            except ClientError as ce:
1099 b666ef82 Stavros Sachtouris
                if ce.status != 404:
1100 b666ef82 Stavros Sachtouris
                    raise
1101 2d158d8b Stavros Sachtouris
            self._check_container_limit(lpath)
1102 b666ef82 Stavros Sachtouris
            yield open(lpath, 'rb'), rpath
1103 706fc940 Stavros Sachtouris
1104 68858765 Stavros Sachtouris
    @errors.generic.all
1105 68858765 Stavros Sachtouris
    @errors.pithos.connection
1106 68858765 Stavros Sachtouris
    @errors.pithos.container
1107 68858765 Stavros Sachtouris
    @errors.pithos.object_path
1108 68858765 Stavros Sachtouris
    @errors.pithos.local_path
1109 68858765 Stavros Sachtouris
    def _run(self, local_path, remote_path):
1110 2fe2672e Stavros Sachtouris
        poolsize = self['poolsize']
1111 72952f4f Stavros Sachtouris
        if poolsize > 0:
1112 16b0afe6 Stavros Sachtouris
            self.client.MAX_THREADS = int(poolsize)
1113 72952f4f Stavros Sachtouris
        params = dict(
1114 72952f4f Stavros Sachtouris
            content_encoding=self['content_encoding'],
1115 2fe2672e Stavros Sachtouris
            content_type=self['content_type'],
1116 2fe2672e Stavros Sachtouris
            content_disposition=self['content_disposition'],
1117 2fe2672e Stavros Sachtouris
            sharing=self['sharing'],
1118 2fe2672e Stavros Sachtouris
            public=self['public'])
1119 0e728dcb Stavros Sachtouris
        uploaded = []
1120 74c65e80 Stavros Sachtouris
        container_info_cache = dict()
1121 b666ef82 Stavros Sachtouris
        for f, rpath in self._path_pairs(local_path, remote_path):
1122 b666ef82 Stavros Sachtouris
            print('%s --> %s:%s' % (f.name, self.client.container, rpath))
1123 68858765 Stavros Sachtouris
            if self['unchunked']:
1124 0e728dcb Stavros Sachtouris
                r = self.client.upload_object_unchunked(
1125 b666ef82 Stavros Sachtouris
                    rpath, f,
1126 b666ef82 Stavros Sachtouris
                    etag=self['etag'], withHashFile=self['use_hashes'],
1127 68858765 Stavros Sachtouris
                    **params)
1128 5655d560 Stavros Sachtouris
                if self['with_output'] or self['json_output']:
1129 0e728dcb Stavros Sachtouris
                    r['name'] = '%s: %s' % (self.client.container, rpath)
1130 0e728dcb Stavros Sachtouris
                    uploaded.append(r)
1131 68858765 Stavros Sachtouris
            else:
1132 68858765 Stavros Sachtouris
                try:
1133 68858765 Stavros Sachtouris
                    (progress_bar, upload_cb) = self._safe_progress_bar(
1134 b666ef82 Stavros Sachtouris
                        'Uploading %s' % f.name.split(path.sep)[-1])
1135 68858765 Stavros Sachtouris
                    if progress_bar:
1136 68858765 Stavros Sachtouris
                        hash_bar = progress_bar.clone()
1137 68858765 Stavros Sachtouris
                        hash_cb = hash_bar.get_generator(
1138 278c9018 Stavros Sachtouris
                            'Calculating block hashes')
1139 68858765 Stavros Sachtouris
                    else:
1140 68858765 Stavros Sachtouris
                        hash_cb = None
1141 0e728dcb Stavros Sachtouris
                    r = self.client.upload_object(
1142 b666ef82 Stavros Sachtouris
                        rpath, f,
1143 74c65e80 Stavros Sachtouris
                        hash_cb=hash_cb,
1144 74c65e80 Stavros Sachtouris
                        upload_cb=upload_cb,
1145 74c65e80 Stavros Sachtouris
                        container_info_cache=container_info_cache,
1146 852a22e7 Stavros Sachtouris
                        **params)
1147 5655d560 Stavros Sachtouris
                    if self['with_output'] or self['json_output']:
1148 0e728dcb Stavros Sachtouris
                        r['name'] = '%s: %s' % (self.client.container, rpath)
1149 0e728dcb Stavros Sachtouris
                        uploaded.append(r)
1150 68858765 Stavros Sachtouris
                except Exception:
1151 68858765 Stavros Sachtouris
                    self._safe_progress_bar_finish(progress_bar)
1152 68858765 Stavros Sachtouris
                    raise
1153 68858765 Stavros Sachtouris
                finally:
1154 68858765 Stavros Sachtouris
                    self._safe_progress_bar_finish(progress_bar)
1155 d9301a7a Stavros Sachtouris
        self._optional_output(uploaded)
1156 915b99b5 Stavros Sachtouris
        print('Upload completed')
1157 7493ccb6 Stavros Sachtouris
1158 68858765 Stavros Sachtouris
    def main(self, local_path, container____path__=None):
1159 68858765 Stavros Sachtouris
        super(self.__class__, self)._run(container____path__)
1160 52edad0a Stavros Sachtouris
        remote_path = self.path or path.basename(local_path)
1161 68858765 Stavros Sachtouris
        self._run(local_path=local_path, remote_path=remote_path)
1162 68858765 Stavros Sachtouris
1163 234954d1 Stavros Sachtouris
1164 d486baec Stavros Sachtouris
@command(pithos_cmds)
1165 3ae60112 Stavros Sachtouris
class file_cat(_file_container_command):
1166 72952f4f Stavros Sachtouris
    """Print remote file contents to console"""
1167 9ceec15a Stavros Sachtouris
1168 2fe2672e Stavros Sachtouris
    arguments = dict(
1169 2fe2672e Stavros Sachtouris
        range=RangeArgument('show range of data', '--range'),
1170 2fe2672e Stavros Sachtouris
        if_match=ValueArgument('show output if ETags match', '--if-match'),
1171 2fe2672e Stavros Sachtouris
        if_none_match=ValueArgument(
1172 201baa17 Stavros Sachtouris
            'show output if ETags match', '--if-none-match'),
1173 2fe2672e Stavros Sachtouris
        if_modified_since=DateArgument(
1174 201baa17 Stavros Sachtouris
            'show output modified since then', '--if-modified-since'),
1175 2fe2672e Stavros Sachtouris
        if_unmodified_since=DateArgument(
1176 201baa17 Stavros Sachtouris
            'show output unmodified since then', '--if-unmodified-since'),
1177 2fe2672e Stavros Sachtouris
        object_version=ValueArgument(
1178 201baa17 Stavros Sachtouris
            'get the specific version', ('-O', '--object-version'))
1179 2fe2672e Stavros Sachtouris
    )
1180 2fe2672e Stavros Sachtouris
1181 68858765 Stavros Sachtouris
    @errors.generic.all
1182 68858765 Stavros Sachtouris
    @errors.pithos.connection
1183 68858765 Stavros Sachtouris
    @errors.pithos.container
1184 68858765 Stavros Sachtouris
    @errors.pithos.object_path
1185 68858765 Stavros Sachtouris
    def _run(self):
1186 68858765 Stavros Sachtouris
        self.client.download_object(
1187 68858765 Stavros Sachtouris
            self.path,
1188 68858765 Stavros Sachtouris
            stdout,
1189 3d568c09 Stavros Sachtouris
            range_str=self['range'],
1190 2fe2672e Stavros Sachtouris
            version=self['object_version'],
1191 2fe2672e Stavros Sachtouris
            if_match=self['if_match'],
1192 2fe2672e Stavros Sachtouris
            if_none_match=self['if_none_match'],
1193 2fe2672e Stavros Sachtouris
            if_modified_since=self['if_modified_since'],
1194 2fe2672e Stavros Sachtouris
            if_unmodified_since=self['if_unmodified_since'])
1195 68858765 Stavros Sachtouris
1196 68858765 Stavros Sachtouris
    def main(self, container___path):
1197 68858765 Stavros Sachtouris
        super(self.__class__, self)._run(
1198 201baa17 Stavros Sachtouris
            container___path, path_is_optional=False)
1199 68858765 Stavros Sachtouris
        self._run()
1200 9ceec15a Stavros Sachtouris
1201 234954d1 Stavros Sachtouris
1202 d486baec Stavros Sachtouris
@command(pithos_cmds)
1203 3ae60112 Stavros Sachtouris
class file_download(_file_container_command):
1204 ae99b37d Stavros Sachtouris
    """Download remote object as local file
1205 ae99b37d Stavros Sachtouris
    If local destination is a directory:
1206 f40f0cb7 Stavros Sachtouris
    *   download <container>:<path> <local dir> -R
1207 ae99b37d Stavros Sachtouris
    will download all files on <container> prefixed as <path>,
1208 ae99b37d Stavros Sachtouris
    to <local dir>/<full path>
1209 ae99b37d Stavros Sachtouris
    *   download <container>:<path> <local dir> --exact-match
1210 ae99b37d Stavros Sachtouris
    will download only one file, exactly matching <path>
1211 f6c09d14 Stavros Sachtouris
    ATTENTION: to download cont:dir1/dir2/file there must exist objects
1212 f6c09d14 Stavros Sachtouris
    cont:dir1 and cont:dir1/dir2 of type application/directory
1213 3ae60112 Stavros Sachtouris
    To create directory objects, use /file mkdir
1214 ae99b37d Stavros Sachtouris
    """
1215 7493ccb6 Stavros Sachtouris
1216 2fe2672e Stavros Sachtouris
    arguments = dict(
1217 f40f0cb7 Stavros Sachtouris
        resume=FlagArgument('Resume instead of overwrite', ('-r', '--resume')),
1218 2fe2672e Stavros Sachtouris
        range=RangeArgument('show range of data', '--range'),
1219 2fe2672e Stavros Sachtouris
        if_match=ValueArgument('show output if ETags match', '--if-match'),
1220 2fe2672e Stavros Sachtouris
        if_none_match=ValueArgument(
1221 201baa17 Stavros Sachtouris
            'show output if ETags match', '--if-none-match'),
1222 2fe2672e Stavros Sachtouris
        if_modified_since=DateArgument(
1223 201baa17 Stavros Sachtouris
            'show output modified since then', '--if-modified-since'),
1224 2fe2672e Stavros Sachtouris
        if_unmodified_since=DateArgument(
1225 201baa17 Stavros Sachtouris
            'show output unmodified since then', '--if-unmodified-since'),
1226 2fe2672e Stavros Sachtouris
        object_version=ValueArgument(
1227 201baa17 Stavros Sachtouris
            'get the specific version', ('-O', '--object-version')),
1228 2fe2672e Stavros Sachtouris
        poolsize=IntArgument('set pool size', '--with-pool-size'),
1229 2fe2672e Stavros Sachtouris
        progress_bar=ProgressBarArgument(
1230 2fe2672e Stavros Sachtouris
            'do not show progress bar',
1231 f40f0cb7 Stavros Sachtouris
            ('-N', '--no-progress-bar'),
1232 ae99b37d Stavros Sachtouris
            default=False),
1233 f6c09d14 Stavros Sachtouris
        recursive=FlagArgument(
1234 a3ba3bce Stavros Sachtouris
            'Download a remote path and all its contents',
1235 f40f0cb7 Stavros Sachtouris
            ('-R', '--recursive'))
1236 2fe2672e Stavros Sachtouris
    )
1237 7493ccb6 Stavros Sachtouris
1238 f6c09d14 Stavros Sachtouris
    def _outputs(self, local_path):
1239 a3ba3bce Stavros Sachtouris
        """:returns: (local_file, remote_path)"""
1240 a3ba3bce Stavros Sachtouris
        remotes = []
1241 a3ba3bce Stavros Sachtouris
        if self['recursive']:
1242 a3ba3bce Stavros Sachtouris
            r = self.client.container_get(
1243 a3ba3bce Stavros Sachtouris
                prefix=self.path or '/',
1244 f6c09d14 Stavros Sachtouris
                if_modified_since=self['if_modified_since'],
1245 f6c09d14 Stavros Sachtouris
                if_unmodified_since=self['if_unmodified_since'])
1246 a3ba3bce Stavros Sachtouris
            dirlist = dict()
1247 a3ba3bce Stavros Sachtouris
            for remote in r.json:
1248 a3ba3bce Stavros Sachtouris
                rname = remote['name'].strip('/')
1249 a3ba3bce Stavros Sachtouris
                tmppath = ''
1250 a3ba3bce Stavros Sachtouris
                for newdir in rname.strip('/').split('/')[:-1]:
1251 a3ba3bce Stavros Sachtouris
                    tmppath = '/'.join([tmppath, newdir])
1252 a3ba3bce Stavros Sachtouris
                    dirlist.update({tmppath.strip('/'): True})
1253 3ae60112 Stavros Sachtouris
                remotes.append((rname, file_download._is_dir(remote)))
1254 a3ba3bce Stavros Sachtouris
            dir_remotes = [r[0] for r in remotes if r[1]]
1255 a3ba3bce Stavros Sachtouris
            if not set(dirlist).issubset(dir_remotes):
1256 a3ba3bce Stavros Sachtouris
                badguys = [bg.strip('/') for bg in set(
1257 a3ba3bce Stavros Sachtouris
                    dirlist).difference(dir_remotes)]
1258 a3ba3bce Stavros Sachtouris
                raiseCLIError(
1259 a3ba3bce Stavros Sachtouris
                    'Some remote paths contain non existing directories',
1260 a3ba3bce Stavros Sachtouris
                    details=['Missing remote directories:'] + badguys)
1261 a3ba3bce Stavros Sachtouris
        elif self.path:
1262 a3ba3bce Stavros Sachtouris
            r = self.client.get_object_info(
1263 a3ba3bce Stavros Sachtouris
                self.path,
1264 a3ba3bce Stavros Sachtouris
                version=self['object_version'])
1265 3ae60112 Stavros Sachtouris
            if file_download._is_dir(r):
1266 a3ba3bce Stavros Sachtouris
                raiseCLIError(
1267 a3ba3bce Stavros Sachtouris
                    'Illegal download: Remote object %s is a directory' % (
1268 a3ba3bce Stavros Sachtouris
                        self.path),
1269 a3ba3bce Stavros Sachtouris
                    details=['To download a directory, try --recursive'])
1270 a3ba3bce Stavros Sachtouris
            if '/' in self.path.strip('/') and not local_path:
1271 a3ba3bce Stavros Sachtouris
                raiseCLIError(
1272 a3ba3bce Stavros Sachtouris
                    'Illegal download: remote object %s contains "/"' % (
1273 a3ba3bce Stavros Sachtouris
                        self.path),
1274 a3ba3bce Stavros Sachtouris
                    details=[
1275 a3ba3bce Stavros Sachtouris
                        'To download an object containing "/" characters',
1276 a3ba3bce Stavros Sachtouris
                        'either create the remote directories or',
1277 a3ba3bce Stavros Sachtouris
                        'specify a non-directory local path for this object'])
1278 a3ba3bce Stavros Sachtouris
            remotes = [(self.path, False)]
1279 a3ba3bce Stavros Sachtouris
        if not remotes:
1280 a3ba3bce Stavros Sachtouris
            if self.path:
1281 a3ba3bce Stavros Sachtouris
                raiseCLIError(
1282 a3ba3bce Stavros Sachtouris
                    'No matching path %s on container %s' % (
1283 a3ba3bce Stavros Sachtouris
                        self.path,
1284 a3ba3bce Stavros Sachtouris
                        self.container),
1285 a3ba3bce Stavros Sachtouris
                    details=[
1286 a3ba3bce Stavros Sachtouris
                        'To list the contents of %s, try:' % self.container,
1287 3ae60112 Stavros Sachtouris
                        '   /file list %s' % self.container])
1288 a3ba3bce Stavros Sachtouris
            raiseCLIError(
1289 a3ba3bce Stavros Sachtouris
                'Illegal download of container %s' % self.container,
1290 a3ba3bce Stavros Sachtouris
                details=[
1291 a3ba3bce Stavros Sachtouris
                    'To download a whole container, try:',
1292 3ae60112 Stavros Sachtouris
                    '   /file download --recursive <container>'])
1293 a3ba3bce Stavros Sachtouris
1294 a3ba3bce Stavros Sachtouris
        lprefix = path.abspath(local_path or path.curdir)
1295 a3ba3bce Stavros Sachtouris
        if path.isdir(lprefix):
1296 a3ba3bce Stavros Sachtouris
            for rpath, remote_is_dir in remotes:
1297 a3ba3bce Stavros Sachtouris
                lpath = '/%s/%s' % (lprefix.strip('/'), rpath.strip('/'))
1298 a3ba3bce Stavros Sachtouris
                if remote_is_dir:
1299 a3ba3bce Stavros Sachtouris
                    if path.exists(lpath) and path.isdir(lpath):
1300 a3ba3bce Stavros Sachtouris
                        continue
1301 a3ba3bce Stavros Sachtouris
                    makedirs(lpath)
1302 a3ba3bce Stavros Sachtouris
                elif path.exists(lpath):
1303 a3ba3bce Stavros Sachtouris
                    if not self['resume']:
1304 a3ba3bce Stavros Sachtouris
                        print('File %s exists, aborting...' % lpath)
1305 a3ba3bce Stavros Sachtouris
                        continue
1306 a3ba3bce Stavros Sachtouris
                    with open(lpath, 'rwb+') as f:
1307 a3ba3bce Stavros Sachtouris
                        yield (f, rpath)
1308 a3ba3bce Stavros Sachtouris
                else:
1309 a3ba3bce Stavros Sachtouris
                    with open(lpath, 'wb+') as f:
1310 a3ba3bce Stavros Sachtouris
                        yield (f, rpath)
1311 a3ba3bce Stavros Sachtouris
        elif path.exists(lprefix):
1312 a3ba3bce Stavros Sachtouris
            if len(remotes) > 1:
1313 a3ba3bce Stavros Sachtouris
                raiseCLIError(
1314 a3ba3bce Stavros Sachtouris
                    '%s remote objects cannot be merged in local file %s' % (
1315 a3ba3bce Stavros Sachtouris
                        len(remotes),
1316 a3ba3bce Stavros Sachtouris
                        local_path),
1317 a3ba3bce Stavros Sachtouris
                    details=[
1318 a3ba3bce Stavros Sachtouris
                        'To download multiple objects, local path should be',
1319 a3ba3bce Stavros Sachtouris
                        'a directory, or use download without a local path'])
1320 a3ba3bce Stavros Sachtouris
            (rpath, remote_is_dir) = remotes[0]
1321 a3ba3bce Stavros Sachtouris
            if remote_is_dir:
1322 a3ba3bce Stavros Sachtouris
                raiseCLIError(
1323 a3ba3bce Stavros Sachtouris
                    'Remote directory %s should not replace local file %s' % (
1324 a3ba3bce Stavros Sachtouris
                        rpath,
1325 a3ba3bce Stavros Sachtouris
                        local_path))
1326 a3ba3bce Stavros Sachtouris
            if self['resume']:
1327 a3ba3bce Stavros Sachtouris
                with open(lprefix, 'rwb+') as f:
1328 a3ba3bce Stavros Sachtouris
                    yield (f, rpath)
1329 a3ba3bce Stavros Sachtouris
            else:
1330 a3ba3bce Stavros Sachtouris
                raiseCLIError(
1331 db36a6a7 Stavros Sachtouris
                    'Local file %s already exist' % local_path,
1332 db36a6a7 Stavros Sachtouris
                    details=['Try --resume to overwrite it'])
1333 a3ba3bce Stavros Sachtouris
        else:
1334 a3ba3bce Stavros Sachtouris
            if len(remotes) > 1 or remotes[0][1]:
1335 a3ba3bce Stavros Sachtouris
                raiseCLIError(
1336 a3ba3bce Stavros Sachtouris
                    'Local directory %s does not exist' % local_path)
1337 a3ba3bce Stavros Sachtouris
            with open(lprefix, 'wb+') as f:
1338 a3ba3bce Stavros Sachtouris
                yield (f, remotes[0][0])
1339 7493ccb6 Stavros Sachtouris
1340 68858765 Stavros Sachtouris
    @errors.generic.all
1341 68858765 Stavros Sachtouris
    @errors.pithos.connection
1342 68858765 Stavros Sachtouris
    @errors.pithos.container
1343 68858765 Stavros Sachtouris
    @errors.pithos.object_path
1344 68858765 Stavros Sachtouris
    @errors.pithos.local_path
1345 68858765 Stavros Sachtouris
    def _run(self, local_path):
1346 2fe2672e Stavros Sachtouris
        poolsize = self['poolsize']
1347 68858765 Stavros Sachtouris
        if poolsize:
1348 16b0afe6 Stavros Sachtouris
            self.client.MAX_THREADS = int(poolsize)
1349 f6c09d14 Stavros Sachtouris
        progress_bar = None
1350 ae99b37d Stavros Sachtouris
        try:
1351 a3ba3bce Stavros Sachtouris
            for f, rpath in self._outputs(local_path):
1352 3d568c09 Stavros Sachtouris
                (
1353 3d568c09 Stavros Sachtouris
                    progress_bar,
1354 a3ba3bce Stavros Sachtouris
                    download_cb) = self._safe_progress_bar(
1355 a3ba3bce Stavros Sachtouris
                        'Download %s' % rpath)
1356 ae99b37d Stavros Sachtouris
                self.client.download_object(
1357 5655d560 Stavros Sachtouris
                    rpath, f,
1358 ae99b37d Stavros Sachtouris
                    download_cb=download_cb,
1359 3d568c09 Stavros Sachtouris
                    range_str=self['range'],
1360 ae99b37d Stavros Sachtouris
                    version=self['object_version'],
1361 ae99b37d Stavros Sachtouris
                    if_match=self['if_match'],
1362 ae99b37d Stavros Sachtouris
                    resume=self['resume'],
1363 ae99b37d Stavros Sachtouris
                    if_none_match=self['if_none_match'],
1364 ae99b37d Stavros Sachtouris
                    if_modified_since=self['if_modified_since'],
1365 ae99b37d Stavros Sachtouris
                    if_unmodified_since=self['if_unmodified_since'])
1366 7493ccb6 Stavros Sachtouris
        except KeyboardInterrupt:
1367 b78ee581 Stavros Sachtouris
            from threading import activeCount, enumerate as activethreads
1368 cae76f25 Stavros Sachtouris
            timeout = 0.5
1369 b78ee581 Stavros Sachtouris
            while activeCount() > 1:
1370 cae76f25 Stavros Sachtouris
                stdout.write('\nCancel %s threads: ' % (activeCount() - 1))
1371 cae76f25 Stavros Sachtouris
                stdout.flush()
1372 b78ee581 Stavros Sachtouris
                for thread in activethreads():
1373 b78ee581 Stavros Sachtouris
                    try:
1374 cae76f25 Stavros Sachtouris
                        thread.join(timeout)
1375 cae76f25 Stavros Sachtouris
                        stdout.write('.' if thread.isAlive() else '*')
1376 b78ee581 Stavros Sachtouris
                    except RuntimeError:
1377 b78ee581 Stavros Sachtouris
                        continue
1378 cae76f25 Stavros Sachtouris
                    finally:
1379 cae76f25 Stavros Sachtouris
                        stdout.flush()
1380 cae76f25 Stavros Sachtouris
                        timeout += 0.1
1381 b78ee581 Stavros Sachtouris
            print('\nDownload canceled by user')
1382 7493ccb6 Stavros Sachtouris
            if local_path is not None:
1383 624ee36f Stavros Sachtouris
                print('to resume, re-run with --resume')
1384 68858765 Stavros Sachtouris
        except Exception:
1385 68858765 Stavros Sachtouris
            self._safe_progress_bar_finish(progress_bar)
1386 68858765 Stavros Sachtouris
            raise
1387 68858765 Stavros Sachtouris
        finally:
1388 68858765 Stavros Sachtouris
            self._safe_progress_bar_finish(progress_bar)
1389 68858765 Stavros Sachtouris
1390 68858765 Stavros Sachtouris
    def main(self, container___path, local_path=None):
1391 a3ba3bce Stavros Sachtouris
        super(self.__class__, self)._run(container___path)
1392 68858765 Stavros Sachtouris
        self._run(local_path=local_path)
1393 7493ccb6 Stavros Sachtouris
1394 234954d1 Stavros Sachtouris
1395 d486baec Stavros Sachtouris
@command(pithos_cmds)
1396 545c6c29 Stavros Sachtouris
class file_hashmap(_file_container_command, _optional_json):
1397 72952f4f Stavros Sachtouris
    """Get the hash-map of an object"""
1398 7493ccb6 Stavros Sachtouris
1399 2fe2672e Stavros Sachtouris
    arguments = dict(
1400 2fe2672e Stavros Sachtouris
        if_match=ValueArgument('show output if ETags match', '--if-match'),
1401 2fe2672e Stavros Sachtouris
        if_none_match=ValueArgument(
1402 545c6c29 Stavros Sachtouris
            'show output if ETags match', '--if-none-match'),
1403 2fe2672e Stavros Sachtouris
        if_modified_since=DateArgument(
1404 545c6c29 Stavros Sachtouris
            'show output modified since then', '--if-modified-since'),
1405 2fe2672e Stavros Sachtouris
        if_unmodified_since=DateArgument(
1406 545c6c29 Stavros Sachtouris
            'show output unmodified since then', '--if-unmodified-since'),
1407 2fe2672e Stavros Sachtouris
        object_version=ValueArgument(
1408 545c6c29 Stavros Sachtouris
            'get the specific version', ('-O', '--object-version'))
1409 2fe2672e Stavros Sachtouris
    )
1410 7493ccb6 Stavros Sachtouris
1411 b4cf92b8 Stavros Sachtouris
    @errors.generic.all
1412 b4cf92b8 Stavros Sachtouris
    @errors.pithos.connection
1413 b4cf92b8 Stavros Sachtouris
    @errors.pithos.container
1414 b4cf92b8 Stavros Sachtouris
    @errors.pithos.object_path
1415 b4cf92b8 Stavros Sachtouris
    def _run(self):
1416 545c6c29 Stavros Sachtouris
        self._print(self.client.get_object_hashmap(
1417 b4cf92b8 Stavros Sachtouris
            self.path,
1418 b4cf92b8 Stavros Sachtouris
            version=self['object_version'],
1419 b4cf92b8 Stavros Sachtouris
            if_match=self['if_match'],
1420 b4cf92b8 Stavros Sachtouris
            if_none_match=self['if_none_match'],
1421 b4cf92b8 Stavros Sachtouris
            if_modified_since=self['if_modified_since'],
1422 545c6c29 Stavros Sachtouris
            if_unmodified_since=self['if_unmodified_since']), print_dict)
1423 b4cf92b8 Stavros Sachtouris
1424 7493ccb6 Stavros Sachtouris
    def main(self, container___path):
1425 b4cf92b8 Stavros Sachtouris
        super(self.__class__, self)._run(
1426 72952f4f Stavros Sachtouris
            container___path,
1427 72952f4f Stavros Sachtouris
            path_is_optional=False)
1428 b4cf92b8 Stavros Sachtouris
        self._run()
1429 7493ccb6 Stavros Sachtouris
1430 234954d1 Stavros Sachtouris
1431 d486baec Stavros Sachtouris
@command(pithos_cmds)
1432 915b99b5 Stavros Sachtouris
class file_delete(_file_container_command, _optional_output_cmd):
1433 72952f4f Stavros Sachtouris
    """Delete a container [or an object]
1434 72952f4f Stavros Sachtouris
    How to delete a non-empty container:
1435 3ae60112 Stavros Sachtouris
    - empty the container:  /file delete -R <container>
1436 3ae60112 Stavros Sachtouris
    - delete it:            /file delete <container>
1437 72952f4f Stavros Sachtouris
    .
1438 72952f4f Stavros Sachtouris
    Semantics of directory deletion:
1439 3ae60112 Stavros Sachtouris
    .a preserve the contents: /file delete <container>:<directory>
1440 72952f4f Stavros Sachtouris
    .    objects of the form dir/filename can exist with a dir object
1441 3ae60112 Stavros Sachtouris
    .b delete contents:       /file delete -R <container>:<directory>
1442 72952f4f Stavros Sachtouris
    .    all dir/* objects are affected, even if dir does not exist
1443 72952f4f Stavros Sachtouris
    .
1444 72952f4f Stavros Sachtouris
    To restore a deleted object OBJ in a container CONT:
1445 3ae60112 Stavros Sachtouris
    - get object versions: /file versions CONT:OBJ
1446 72952f4f Stavros Sachtouris
    .   and choose the version to be restored
1447 3ae60112 Stavros Sachtouris
    - restore the object:  /file copy --source-version=<version> CONT:OBJ OBJ
1448 72952f4f Stavros Sachtouris
    """
1449 7493ccb6 Stavros Sachtouris
1450 2fe2672e Stavros Sachtouris
    arguments = dict(
1451 439826ec Stavros Sachtouris
        until=DateArgument('remove history until that date', '--until'),
1452 7147e1ca Stavros Sachtouris
        yes=FlagArgument('Do not prompt for permission', '--yes'),
1453 2fe2672e Stavros Sachtouris
        recursive=FlagArgument(
1454 234954d1 Stavros Sachtouris
            'empty dir or container and delete (if dir)',
1455 915b99b5 Stavros Sachtouris
            ('-R', '--recursive'))
1456 2fe2672e Stavros Sachtouris
    )
1457 2fe2672e Stavros Sachtouris
1458 f724cd35 Stavros Sachtouris
    def __init__(self, arguments={}, auth_base=None):
1459 f724cd35 Stavros Sachtouris
        super(self.__class__, self).__init__(arguments, auth_base)
1460 439826ec Stavros Sachtouris
        self['delimiter'] = DelimiterArgument(
1461 2fe2672e Stavros Sachtouris
            self,
1462 234954d1 Stavros Sachtouris
            parsed_name='--delimiter',
1463 234954d1 Stavros Sachtouris
            help='delete objects prefixed with <object><delimiter>')
1464 7493ccb6 Stavros Sachtouris
1465 b4cf92b8 Stavros Sachtouris
    @errors.generic.all
1466 b4cf92b8 Stavros Sachtouris
    @errors.pithos.connection
1467 b4cf92b8 Stavros Sachtouris
    @errors.pithos.container
1468 b4cf92b8 Stavros Sachtouris
    @errors.pithos.object_path
1469 b4cf92b8 Stavros Sachtouris
    def _run(self):
1470 b4cf92b8 Stavros Sachtouris
        if self.path:
1471 24ff0a35 Stavros Sachtouris
            if self['yes'] or ask_user(
1472 24ff0a35 Stavros Sachtouris
                    'Delete %s:%s ?' % (self.container, self.path)):
1473 915b99b5 Stavros Sachtouris
                self._optional_output(self.client.del_object(
1474 b4cf92b8 Stavros Sachtouris
                    self.path,
1475 915b99b5 Stavros Sachtouris
                    until=self['until'], delimiter=self['delimiter']))
1476 7493ccb6 Stavros Sachtouris
            else:
1477 b4cf92b8 Stavros Sachtouris
                print('Aborted')
1478 b4cf92b8 Stavros Sachtouris
        else:
1479 24ff0a35 Stavros Sachtouris
            if self['recursive']:
1480 de73876b Stavros Sachtouris
                ask_msg = 'Delete container contents'
1481 de73876b Stavros Sachtouris
            else:
1482 de73876b Stavros Sachtouris
                ask_msg = 'Delete container'
1483 b4cf92b8 Stavros Sachtouris
            if self['yes'] or ask_user('%s %s ?' % (ask_msg, self.container)):
1484 915b99b5 Stavros Sachtouris
                self._optional_output(self.client.del_container(
1485 915b99b5 Stavros Sachtouris
                    until=self['until'], delimiter=self['delimiter']))
1486 b4cf92b8 Stavros Sachtouris
            else:
1487 b4cf92b8 Stavros Sachtouris
                print('Aborted')
1488 b4cf92b8 Stavros Sachtouris
1489 edab7ba7 Stavros Sachtouris
    def main(self, container____path__=None):
1490 b4cf92b8 Stavros Sachtouris
        super(self.__class__, self)._run(container____path__)
1491 b4cf92b8 Stavros Sachtouris
        self._run()
1492 7493ccb6 Stavros Sachtouris
1493 234954d1 Stavros Sachtouris
1494 d486baec Stavros Sachtouris
@command(pithos_cmds)
1495 915b99b5 Stavros Sachtouris
class file_purge(_file_container_command, _optional_output_cmd):
1496 72952f4f Stavros Sachtouris
    """Delete a container and release related data blocks
1497 72952f4f Stavros Sachtouris
    Non-empty containers can not purged.
1498 72952f4f Stavros Sachtouris
    To purge a container with content:
1499 3ae60112 Stavros Sachtouris
    .   /file delete -R <container>
1500 72952f4f Stavros Sachtouris
    .      objects are deleted, but data blocks remain on server
1501 3ae60112 Stavros Sachtouris
    .   /file purge <container>
1502 72952f4f Stavros Sachtouris
    .      container and data blocks are released and deleted
1503 2fe2672e Stavros Sachtouris
    """
1504 234954d1 Stavros Sachtouris
1505 7147e1ca Stavros Sachtouris
    arguments = dict(
1506 7147e1ca Stavros Sachtouris
        yes=FlagArgument('Do not prompt for permission', '--yes'),
1507 915b99b5 Stavros Sachtouris
        force=FlagArgument('purge even if not empty', ('-F', '--force'))
1508 7147e1ca Stavros Sachtouris
    )
1509 7147e1ca Stavros Sachtouris
1510 b4cf92b8 Stavros Sachtouris
    @errors.generic.all
1511 b4cf92b8 Stavros Sachtouris
    @errors.pithos.connection
1512 b4cf92b8 Stavros Sachtouris
    @errors.pithos.container
1513 b4cf92b8 Stavros Sachtouris
    def _run(self):
1514 b4cf92b8 Stavros Sachtouris
        if self['yes'] or ask_user('Purge container %s?' % self.container):
1515 be4a8ccd Stavros Sachtouris
            try:
1516 5655d560 Stavros Sachtouris
                r = self.client.purge_container()
1517 be4a8ccd Stavros Sachtouris
            except ClientError as ce:
1518 be4a8ccd Stavros Sachtouris
                if ce.status in (409,):
1519 be4a8ccd Stavros Sachtouris
                    if self['force']:
1520 be4a8ccd Stavros Sachtouris
                        self.client.del_container(delimiter='/')
1521 5655d560 Stavros Sachtouris
                        r = self.client.purge_container()
1522 be4a8ccd Stavros Sachtouris
                    else:
1523 be4a8ccd Stavros Sachtouris
                        raiseCLIError(ce, details=['Try -F to force-purge'])
1524 be4a8ccd Stavros Sachtouris
                else:
1525 be4a8ccd Stavros Sachtouris
                    raise
1526 915b99b5 Stavros Sachtouris
            self._optional_output(r)
1527 b4cf92b8 Stavros Sachtouris
        else:
1528 b4cf92b8 Stavros Sachtouris
            print('Aborted')
1529 b4cf92b8 Stavros Sachtouris
1530 52edad0a Stavros Sachtouris
    def main(self, container=None):
1531 b4cf92b8 Stavros Sachtouris
        super(self.__class__, self)._run(container)
1532 52edad0a Stavros Sachtouris
        if container and self.container != container:
1533 52edad0a Stavros Sachtouris
            raiseCLIError('Invalid container name %s' % container, details=[
1534 52edad0a Stavros Sachtouris
                'Did you mean "%s" ?' % self.container,
1535 52edad0a Stavros Sachtouris
                'Use --container for names containing :'])
1536 b4cf92b8 Stavros Sachtouris
        self._run()
1537 7493ccb6 Stavros Sachtouris
1538 234954d1 Stavros Sachtouris
1539 d486baec Stavros Sachtouris
@command(pithos_cmds)
1540 3ae60112 Stavros Sachtouris
class file_publish(_file_container_command):
1541 2fe2672e Stavros Sachtouris
    """Publish the object and print the public url"""
1542 7493ccb6 Stavros Sachtouris
1543 b4cf92b8 Stavros Sachtouris
    @errors.generic.all
1544 b4cf92b8 Stavros Sachtouris
    @errors.pithos.connection
1545 b4cf92b8 Stavros Sachtouris
    @errors.pithos.container
1546 b4cf92b8 Stavros Sachtouris
    @errors.pithos.object_path
1547 b4cf92b8 Stavros Sachtouris
    def _run(self):
1548 b4cf92b8 Stavros Sachtouris
        url = self.client.publish_object(self.path)
1549 b4cf92b8 Stavros Sachtouris
        print(url)
1550 b4cf92b8 Stavros Sachtouris
1551 7493ccb6 Stavros Sachtouris
    def main(self, container___path):
1552 b4cf92b8 Stavros Sachtouris
        super(self.__class__, self)._run(
1553 201baa17 Stavros Sachtouris
            container___path, path_is_optional=False)
1554 b4cf92b8 Stavros Sachtouris
        self._run()
1555 7493ccb6 Stavros Sachtouris
1556 234954d1 Stavros Sachtouris
1557 d486baec Stavros Sachtouris
@command(pithos_cmds)
1558 915b99b5 Stavros Sachtouris
class file_unpublish(_file_container_command, _optional_output_cmd):
1559 7493ccb6 Stavros Sachtouris
    """Unpublish an object"""
1560 7493ccb6 Stavros Sachtouris
1561 b4cf92b8 Stavros Sachtouris
    @errors.generic.all
1562 b4cf92b8 Stavros Sachtouris
    @errors.pithos.connection
1563 b4cf92b8 Stavros Sachtouris
    @errors.pithos.container
1564 b4cf92b8 Stavros Sachtouris
    @errors.pithos.object_path
1565 b4cf92b8 Stavros Sachtouris
    def _run(self):
1566 915b99b5 Stavros Sachtouris
            self._optional_output(self.client.unpublish_object(self.path))
1567 b4cf92b8 Stavros Sachtouris
1568 7493ccb6 Stavros Sachtouris
    def main(self, container___path):
1569 b4cf92b8 Stavros Sachtouris
        super(self.__class__, self)._run(
1570 201baa17 Stavros Sachtouris
            container___path, path_is_optional=False)
1571 b4cf92b8 Stavros Sachtouris
        self._run()
1572 7493ccb6 Stavros Sachtouris
1573 234954d1 Stavros Sachtouris
1574 d486baec Stavros Sachtouris
@command(pithos_cmds)
1575 5655d560 Stavros Sachtouris
class file_permissions(_pithos_init):
1576 5655d560 Stavros Sachtouris
    """Manage user and group accessibility for objects
1577 5655d560 Stavros Sachtouris
    Permissions are lists of users and user groups. There are read and write
1578 72952f4f Stavros Sachtouris
    permissions. Users and groups with write permission have also read
1579 72952f4f Stavros Sachtouris
    permission.
1580 72952f4f Stavros Sachtouris
    """
1581 7493ccb6 Stavros Sachtouris
1582 5655d560 Stavros Sachtouris
1583 545c6c29 Stavros Sachtouris
def print_permissions(permissions_dict):
1584 545c6c29 Stavros Sachtouris
    expected_keys = ('read', 'write')
1585 545c6c29 Stavros Sachtouris
    if set(permissions_dict).issubset(expected_keys):
1586 545c6c29 Stavros Sachtouris
        print_dict(permissions_dict)
1587 545c6c29 Stavros Sachtouris
    else:
1588 545c6c29 Stavros Sachtouris
        invalid_keys = set(permissions_dict.keys()).difference(expected_keys)
1589 545c6c29 Stavros Sachtouris
        raiseCLIError(
1590 545c6c29 Stavros Sachtouris
            'Illegal permission keys: %s' % ', '.join(invalid_keys),
1591 545c6c29 Stavros Sachtouris
            importance=1, details=[
1592 545c6c29 Stavros Sachtouris
                'Valid permission types: %s' % ' '.join(expected_keys)])
1593 545c6c29 Stavros Sachtouris
1594 545c6c29 Stavros Sachtouris
1595 5655d560 Stavros Sachtouris
@command(pithos_cmds)
1596 545c6c29 Stavros Sachtouris
class file_permissions_get(_file_container_command, _optional_json):
1597 5655d560 Stavros Sachtouris
    """Get read and write permissions of an object"""
1598 5655d560 Stavros Sachtouris
1599 b4cf92b8 Stavros Sachtouris
    @errors.generic.all
1600 b4cf92b8 Stavros Sachtouris
    @errors.pithos.connection
1601 b4cf92b8 Stavros Sachtouris
    @errors.pithos.container
1602 b4cf92b8 Stavros Sachtouris
    @errors.pithos.object_path
1603 b4cf92b8 Stavros Sachtouris
    def _run(self):
1604 545c6c29 Stavros Sachtouris
        self._print(
1605 545c6c29 Stavros Sachtouris
            self.client.get_object_sharing(self.path), print_permissions)
1606 b4cf92b8 Stavros Sachtouris
1607 7493ccb6 Stavros Sachtouris
    def main(self, container___path):
1608 b4cf92b8 Stavros Sachtouris
        super(self.__class__, self)._run(
1609 201baa17 Stavros Sachtouris
            container___path, path_is_optional=False)
1610 b4cf92b8 Stavros Sachtouris
        self._run()
1611 7493ccb6 Stavros Sachtouris
1612 234954d1 Stavros Sachtouris
1613 d486baec Stavros Sachtouris
@command(pithos_cmds)
1614 915b99b5 Stavros Sachtouris
class file_permissions_set(_file_container_command, _optional_output_cmd):
1615 72952f4f Stavros Sachtouris
    """Set permissions for an object
1616 72952f4f Stavros Sachtouris
    New permissions overwrite existing permissions.
1617 72952f4f Stavros Sachtouris
    Permission format:
1618 72952f4f Stavros Sachtouris
    -   read=<username>[,usergroup[,...]]
1619 72952f4f Stavros Sachtouris
    -   write=<username>[,usegroup[,...]]
1620 72952f4f Stavros Sachtouris
    E.g. to give read permissions for file F to users A and B and write for C:
1621 5655d560 Stavros Sachtouris
    .       /file permissions set F read=A,B write=C
1622 72952f4f Stavros Sachtouris
    """
1623 7493ccb6 Stavros Sachtouris
1624 b4cf92b8 Stavros Sachtouris
    @errors.generic.all
1625 00336c85 Stavros Sachtouris
    def format_permission_dict(self, permissions):
1626 7493ccb6 Stavros Sachtouris
        read = False
1627 7493ccb6 Stavros Sachtouris
        write = False
1628 3dabe5d2 Stavros Sachtouris
        for perms in permissions:
1629 7493ccb6 Stavros Sachtouris
            splstr = perms.split('=')
1630 7493ccb6 Stavros Sachtouris
            if 'read' == splstr[0]:
1631 24ff0a35 Stavros Sachtouris
                read = [ug.strip() for ug in splstr[1].split(',')]
1632 7493ccb6 Stavros Sachtouris
            elif 'write' == splstr[0]:
1633 24ff0a35 Stavros Sachtouris
                write = [ug.strip() for ug in splstr[1].split(',')]
1634 7493ccb6 Stavros Sachtouris
            else:
1635 24ff0a35 Stavros Sachtouris
                msg = 'Usage:\tread=<groups,users> write=<groups,users>'
1636 24ff0a35 Stavros Sachtouris
                raiseCLIError(None, msg)
1637 234954d1 Stavros Sachtouris
        return (read, write)
1638 7493ccb6 Stavros Sachtouris
1639 b4cf92b8 Stavros Sachtouris
    @errors.generic.all
1640 b4cf92b8 Stavros Sachtouris
    @errors.pithos.connection
1641 b4cf92b8 Stavros Sachtouris
    @errors.pithos.container
1642 b4cf92b8 Stavros Sachtouris
    @errors.pithos.object_path
1643 b4cf92b8 Stavros Sachtouris
    def _run(self, read, write):
1644 915b99b5 Stavros Sachtouris
        self._optional_output(self.client.set_object_sharing(
1645 201baa17 Stavros Sachtouris
            self.path, read_permission=read, write_permission=write))
1646 b4cf92b8 Stavros Sachtouris
1647 3dabe5d2 Stavros Sachtouris
    def main(self, container___path, *permissions):
1648 b4cf92b8 Stavros Sachtouris
        super(self.__class__, self)._run(
1649 201baa17 Stavros Sachtouris
            container___path, path_is_optional=False)
1650 201baa17 Stavros Sachtouris
        read, write = self.format_permission_dict(permissions)
1651 b4cf92b8 Stavros Sachtouris
        self._run(read, write)
1652 7493ccb6 Stavros Sachtouris
1653 234954d1 Stavros Sachtouris
1654 d486baec Stavros Sachtouris
@command(pithos_cmds)
1655 915b99b5 Stavros Sachtouris
class file_permissions_delete(_file_container_command, _optional_output_cmd):
1656 72952f4f Stavros Sachtouris
    """Delete all permissions set on object
1657 5655d560 Stavros Sachtouris
    To modify permissions, use /file permissions set
1658 72952f4f Stavros Sachtouris
    """
1659 7493ccb6 Stavros Sachtouris
1660 b4cf92b8 Stavros Sachtouris
    @errors.generic.all
1661 b4cf92b8 Stavros Sachtouris
    @errors.pithos.connection
1662 b4cf92b8 Stavros Sachtouris
    @errors.pithos.container
1663 b4cf92b8 Stavros Sachtouris
    @errors.pithos.object_path
1664 b4cf92b8 Stavros Sachtouris
    def _run(self):
1665 915b99b5 Stavros Sachtouris
        self._optional_output(self.client.del_object_sharing(self.path))
1666 b4cf92b8 Stavros Sachtouris
1667 7493ccb6 Stavros Sachtouris
    def main(self, container___path):
1668 b4cf92b8 Stavros Sachtouris
        super(self.__class__, self)._run(
1669 201baa17 Stavros Sachtouris
            container___path, path_is_optional=False)
1670 b4cf92b8 Stavros Sachtouris
        self._run()
1671 7493ccb6 Stavros Sachtouris
1672 234954d1 Stavros Sachtouris
1673 d486baec Stavros Sachtouris
@command(pithos_cmds)
1674 545c6c29 Stavros Sachtouris
class file_info(_file_container_command, _optional_json):
1675 72952f4f Stavros Sachtouris
    """Get detailed information for user account, containers or objects
1676 3ae60112 Stavros Sachtouris
    to get account info:    /file info
1677 3ae60112 Stavros Sachtouris
    to get container info:  /file info <container>
1678 3ae60112 Stavros Sachtouris
    to get object info:     /file info <container>:<path>
1679 72952f4f Stavros Sachtouris
    """
1680 7493ccb6 Stavros Sachtouris
1681 2fe2672e Stavros Sachtouris
    arguments = dict(
1682 2fe2672e Stavros Sachtouris
        object_version=ValueArgument(
1683 2fe2672e Stavros Sachtouris
            'show specific version \ (applies only for objects)',
1684 545c6c29 Stavros Sachtouris
            ('-O', '--object-version'))
1685 2fe2672e Stavros Sachtouris
    )
1686 6ac7f90f Stavros Sachtouris
1687 b4cf92b8 Stavros Sachtouris
    @errors.generic.all
1688 b4cf92b8 Stavros Sachtouris
    @errors.pithos.connection
1689 b4cf92b8 Stavros Sachtouris
    @errors.pithos.container
1690 b4cf92b8 Stavros Sachtouris
    @errors.pithos.object_path
1691 b4cf92b8 Stavros Sachtouris
    def _run(self):
1692 b4cf92b8 Stavros Sachtouris
        if self.container is None:
1693 b4cf92b8 Stavros Sachtouris
            r = self.client.get_account_info()
1694 b4cf92b8 Stavros Sachtouris
        elif self.path is None:
1695 b4cf92b8 Stavros Sachtouris
            r = self.client.get_container_info(self.container)
1696 b4cf92b8 Stavros Sachtouris
        else:
1697 b4cf92b8 Stavros Sachtouris
            r = self.client.get_object_info(
1698 201baa17 Stavros Sachtouris
                self.path, version=self['object_version'])
1699 545c6c29 Stavros Sachtouris
        self._print(r, print_dict)
1700 b4cf92b8 Stavros Sachtouris
1701 7493ccb6 Stavros Sachtouris
    def main(self, container____path__=None):
1702 b4cf92b8 Stavros Sachtouris
        super(self.__class__, self)._run(container____path__)
1703 b4cf92b8 Stavros Sachtouris
        self._run()
1704 7493ccb6 Stavros Sachtouris
1705 234954d1 Stavros Sachtouris
1706 d486baec Stavros Sachtouris
@command(pithos_cmds)
1707 5655d560 Stavros Sachtouris
class file_metadata(_pithos_init):
1708 5655d560 Stavros Sachtouris
    """Metadata are attached on objects. They are formed as key:value pairs.
1709 5655d560 Stavros Sachtouris
    They can have arbitary values.
1710 5655d560 Stavros Sachtouris
    """
1711 5655d560 Stavros Sachtouris
1712 5655d560 Stavros Sachtouris
1713 5655d560 Stavros Sachtouris
@command(pithos_cmds)
1714 545c6c29 Stavros Sachtouris
class file_metadata_get(_file_container_command, _optional_json):
1715 72952f4f Stavros Sachtouris
    """Get metadata for account, containers or objects"""
1716 7493ccb6 Stavros Sachtouris
1717 2fe2672e Stavros Sachtouris
    arguments = dict(
1718 f40f0cb7 Stavros Sachtouris
        detail=FlagArgument('show detailed output', ('-l', '--details')),
1719 2fe2672e Stavros Sachtouris
        until=DateArgument('show metadata until then', '--until'),
1720 2fe2672e Stavros Sachtouris
        object_version=ValueArgument(
1721 2fe2672e Stavros Sachtouris
            'show specific version \ (applies only for objects)',
1722 545c6c29 Stavros Sachtouris
            ('-O', '--object-version'))
1723 2fe2672e Stavros Sachtouris
    )
1724 7493ccb6 Stavros Sachtouris
1725 b4cf92b8 Stavros Sachtouris
    @errors.generic.all
1726 b4cf92b8 Stavros Sachtouris
    @errors.pithos.connection
1727 b4cf92b8 Stavros Sachtouris
    @errors.pithos.container
1728 b4cf92b8 Stavros Sachtouris
    @errors.pithos.object_path
1729 b4cf92b8 Stavros Sachtouris
    def _run(self):
1730 b4cf92b8 Stavros Sachtouris
        until = self['until']
1731 545c6c29 Stavros Sachtouris
        r = None
1732 b4cf92b8 Stavros Sachtouris
        if self.container is None:
1733 b4cf92b8 Stavros Sachtouris
            if self['detail']:
1734 b4cf92b8 Stavros Sachtouris
                r = self.client.get_account_info(until=until)
1735 7493ccb6 Stavros Sachtouris
            else:
1736 b4cf92b8 Stavros Sachtouris
                r = self.client.get_account_meta(until=until)
1737 b4cf92b8 Stavros Sachtouris
                r = pretty_keys(r, '-')
1738 b4cf92b8 Stavros Sachtouris
        elif self.path is None:
1739 b4cf92b8 Stavros Sachtouris
            if self['detail']:
1740 b4cf92b8 Stavros Sachtouris
                r = self.client.get_container_info(until=until)
1741 b4cf92b8 Stavros Sachtouris
            else:
1742 b4cf92b8 Stavros Sachtouris
                cmeta = self.client.get_container_meta(until=until)
1743 b4cf92b8 Stavros Sachtouris
                ometa = self.client.get_container_object_meta(until=until)
1744 b4cf92b8 Stavros Sachtouris
                r = {}
1745 b4cf92b8 Stavros Sachtouris
                if cmeta:
1746 b4cf92b8 Stavros Sachtouris
                    r['container-meta'] = pretty_keys(cmeta, '-')
1747 b4cf92b8 Stavros Sachtouris
                if ometa:
1748 b4cf92b8 Stavros Sachtouris
                    r['object-meta'] = pretty_keys(ometa, '-')
1749 b4cf92b8 Stavros Sachtouris
        else:
1750 b4cf92b8 Stavros Sachtouris
            if self['detail']:
1751 de73876b Stavros Sachtouris
                r = self.client.get_object_info(
1752 de73876b Stavros Sachtouris
                    self.path,
1753 b4cf92b8 Stavros Sachtouris
                    version=self['object_version'])
1754 b4cf92b8 Stavros Sachtouris
            else:
1755 de73876b Stavros Sachtouris
                r = self.client.get_object_meta(
1756 de73876b Stavros Sachtouris
                    self.path,
1757 b4cf92b8 Stavros Sachtouris
                    version=self['object_version'])
1758 b4cf92b8 Stavros Sachtouris
                r = pretty_keys(pretty_keys(r, '-'))
1759 b4cf92b8 Stavros Sachtouris
        if r:
1760 545c6c29 Stavros Sachtouris
            self._print(r, print_dict)
1761 b4cf92b8 Stavros Sachtouris
1762 b4cf92b8 Stavros Sachtouris
    def main(self, container____path__=None):
1763 b4cf92b8 Stavros Sachtouris
        super(self.__class__, self)._run(container____path__)
1764 b4cf92b8 Stavros Sachtouris
        self._run()
1765 7493ccb6 Stavros Sachtouris
1766 234954d1 Stavros Sachtouris
1767 d486baec Stavros Sachtouris
@command(pithos_cmds)
1768 915b99b5 Stavros Sachtouris
class file_metadata_set(_file_container_command, _optional_output_cmd):
1769 5655d560 Stavros Sachtouris
    """Set a piece of metadata for account, container or object"""
1770 7493ccb6 Stavros Sachtouris
1771 b4cf92b8 Stavros Sachtouris
    @errors.generic.all
1772 b4cf92b8 Stavros Sachtouris
    @errors.pithos.connection
1773 b4cf92b8 Stavros Sachtouris
    @errors.pithos.container
1774 b4cf92b8 Stavros Sachtouris
    @errors.pithos.object_path
1775 b4cf92b8 Stavros Sachtouris
    def _run(self, metakey, metaval):
1776 b4cf92b8 Stavros Sachtouris
        if not self.container:
1777 915b99b5 Stavros Sachtouris
            r = self.client.set_account_meta({metakey: metaval})
1778 b4cf92b8 Stavros Sachtouris
        elif not self.path:
1779 915b99b5 Stavros Sachtouris
            r = self.client.set_container_meta({metakey: metaval})
1780 b4cf92b8 Stavros Sachtouris
        else:
1781 915b99b5 Stavros Sachtouris
            r = self.client.set_object_meta(self.path, {metakey: metaval})
1782 915b99b5 Stavros Sachtouris
        self._optional_output(r)
1783 b4cf92b8 Stavros Sachtouris
1784 b4cf92b8 Stavros Sachtouris
    def main(self, metakey, metaval, container____path__=None):
1785 b4cf92b8 Stavros Sachtouris
        super(self.__class__, self)._run(container____path__)
1786 b4cf92b8 Stavros Sachtouris
        self._run(metakey=metakey, metaval=metaval)
1787 7493ccb6 Stavros Sachtouris
1788 234954d1 Stavros Sachtouris
1789 d486baec Stavros Sachtouris
@command(pithos_cmds)
1790 915b99b5 Stavros Sachtouris
class file_metadata_delete(_file_container_command, _optional_output_cmd):
1791 72952f4f Stavros Sachtouris
    """Delete metadata with given key from account, container or object
1792 5655d560 Stavros Sachtouris
    - to get metadata of current account: /file metadata get
1793 5655d560 Stavros Sachtouris
    - to get metadata of a container:     /file metadata get <container>
1794 5655d560 Stavros Sachtouris
    - to get metadata of an object:       /file metadata get <container>:<path>
1795 72952f4f Stavros Sachtouris
    """
1796 7493ccb6 Stavros Sachtouris
1797 b4cf92b8 Stavros Sachtouris
    @errors.generic.all
1798 b4cf92b8 Stavros Sachtouris
    @errors.pithos.connection
1799 b4cf92b8 Stavros Sachtouris
    @errors.pithos.container
1800 b4cf92b8 Stavros Sachtouris
    @errors.pithos.object_path
1801 b4cf92b8 Stavros Sachtouris
    def _run(self, metakey):
1802 b4cf92b8 Stavros Sachtouris
        if self.container is None:
1803 915b99b5 Stavros Sachtouris
            r = self.client.del_account_meta(metakey)
1804 b4cf92b8 Stavros Sachtouris
        elif self.path is None:
1805 915b99b5 Stavros Sachtouris
            r = self.client.del_container_meta(metakey)
1806 b4cf92b8 Stavros Sachtouris
        else:
1807 915b99b5 Stavros Sachtouris
            r = self.client.del_object_meta(self.path, metakey)
1808 915b99b5 Stavros Sachtouris
        self._optional_output(r)
1809 b4cf92b8 Stavros Sachtouris
1810 7493ccb6 Stavros Sachtouris
    def main(self, metakey, container____path__=None):
1811 b4cf92b8 Stavros Sachtouris
        super(self.__class__, self)._run(container____path__)
1812 b4cf92b8 Stavros Sachtouris
        self._run(metakey)
1813 7493ccb6 Stavros Sachtouris
1814 234954d1 Stavros Sachtouris
1815 d486baec Stavros Sachtouris
@command(pithos_cmds)
1816 545c6c29 Stavros Sachtouris
class file_quota(_file_account_command, _optional_json):
1817 3ed6dbde Stavros Sachtouris
    """Get account quota"""
1818 001200c3 Stavros Sachtouris
1819 001200c3 Stavros Sachtouris
    arguments = dict(
1820 001200c3 Stavros Sachtouris
        in_bytes=FlagArgument('Show result in bytes', ('-b', '--bytes'))
1821 de73876b Stavros Sachtouris
    )
1822 7493ccb6 Stavros Sachtouris
1823 b4cf92b8 Stavros Sachtouris
    @errors.generic.all
1824 b4cf92b8 Stavros Sachtouris
    @errors.pithos.connection
1825 b4cf92b8 Stavros Sachtouris
    def _run(self):
1826 545c6c29 Stavros Sachtouris
1827 545c6c29 Stavros Sachtouris
        def pretty_print(output):
1828 545c6c29 Stavros Sachtouris
            if not self['in_bytes']:
1829 545c6c29 Stavros Sachtouris
                for k in output:
1830 545c6c29 Stavros Sachtouris
                    output[k] = format_size(output[k])
1831 545c6c29 Stavros Sachtouris
            pretty_dict(output, '-')
1832 545c6c29 Stavros Sachtouris
1833 545c6c29 Stavros Sachtouris
        self._print(self.client.get_account_quota(), pretty_print)
1834 7493ccb6 Stavros Sachtouris
1835 3ed6dbde Stavros Sachtouris
    def main(self, custom_uuid=None):
1836 3ed6dbde Stavros Sachtouris
        super(self.__class__, self)._run(custom_account=custom_uuid)
1837 b4cf92b8 Stavros Sachtouris
        self._run()
1838 b4cf92b8 Stavros Sachtouris
1839 234954d1 Stavros Sachtouris
1840 d486baec Stavros Sachtouris
@command(pithos_cmds)
1841 326a79b9 Stavros Sachtouris
class file_containerlimit(_pithos_init):
1842 326a79b9 Stavros Sachtouris
    """Container size limit commands"""
1843 326a79b9 Stavros Sachtouris
1844 326a79b9 Stavros Sachtouris
1845 326a79b9 Stavros Sachtouris
@command(pithos_cmds)
1846 545c6c29 Stavros Sachtouris
class file_containerlimit_get(_file_container_command, _optional_json):
1847 3ed6dbde Stavros Sachtouris
    """Get container size limit"""
1848 3ed6dbde Stavros Sachtouris
1849 3ed6dbde Stavros Sachtouris
    arguments = dict(
1850 3ed6dbde Stavros Sachtouris
        in_bytes=FlagArgument('Show result in bytes', ('-b', '--bytes'))
1851 3ed6dbde Stavros Sachtouris
    )
1852 3ed6dbde Stavros Sachtouris
1853 3ed6dbde Stavros Sachtouris
    @errors.generic.all
1854 3ed6dbde Stavros Sachtouris
    @errors.pithos.container
1855 3ed6dbde Stavros Sachtouris
    def _run(self):
1856 545c6c29 Stavros Sachtouris
1857 545c6c29 Stavros Sachtouris
        def pretty_print(output):
1858 545c6c29 Stavros Sachtouris
            if not self['in_bytes']:
1859 545c6c29 Stavros Sachtouris
                for k, v in output.items():
1860 545c6c29 Stavros Sachtouris
                    output[k] = 'unlimited' if '0' == v else format_size(v)
1861 545c6c29 Stavros Sachtouris
            pretty_dict(output, '-')
1862 545c6c29 Stavros Sachtouris
1863 545c6c29 Stavros Sachtouris
        self._print(
1864 545c6c29 Stavros Sachtouris
            self.client.get_container_limit(self.container), pretty_print)
1865 3ed6dbde Stavros Sachtouris
1866 3ed6dbde Stavros Sachtouris
    def main(self, container=None):
1867 3ed6dbde Stavros Sachtouris
        super(self.__class__, self)._run()
1868 3ed6dbde Stavros Sachtouris
        self.container = container
1869 3ed6dbde Stavros Sachtouris
        self._run()
1870 3ed6dbde Stavros Sachtouris
1871 3ed6dbde Stavros Sachtouris
1872 3ed6dbde Stavros Sachtouris
@command(pithos_cmds)
1873 545c6c29 Stavros Sachtouris
class file_containerlimit_set(_file_account_command, _optional_output_cmd):
1874 326a79b9 Stavros Sachtouris
    """Set new storage limit for a container
1875 326a79b9 Stavros Sachtouris
    By default, the limit is set in bytes
1876 001200c3 Stavros Sachtouris
    Users may specify a different unit, e.g:
1877 326a79b9 Stavros Sachtouris
    /file containerlimit set 2.3GB mycontainer
1878 3ed6dbde Stavros Sachtouris
    Valid units: B, KiB (1024 B), KB (1000 B), MiB, MB, GiB, GB, TiB, TB
1879 9f783a51 Stavros Sachtouris
    To set container limit to "unlimited", use 0
1880 001200c3 Stavros Sachtouris
    """
1881 001200c3 Stavros Sachtouris
1882 b4cf92b8 Stavros Sachtouris
    @errors.generic.all
1883 326a79b9 Stavros Sachtouris
    def _calculate_limit(self, user_input):
1884 326a79b9 Stavros Sachtouris
        limit = 0
1885 001200c3 Stavros Sachtouris
        try:
1886 326a79b9 Stavros Sachtouris
            limit = int(user_input)
1887 001200c3 Stavros Sachtouris
        except ValueError:
1888 001200c3 Stavros Sachtouris
            index = 0
1889 001200c3 Stavros Sachtouris
            digits = [str(num) for num in range(0, 10)] + ['.']
1890 001200c3 Stavros Sachtouris
            while user_input[index] in digits:
1891 001200c3 Stavros Sachtouris
                index += 1
1892 326a79b9 Stavros Sachtouris
            limit = user_input[:index]
1893 001200c3 Stavros Sachtouris
            format = user_input[index:]
1894 001200c3 Stavros Sachtouris
            try:
1895 326a79b9 Stavros Sachtouris
                return to_bytes(limit, format)
1896 001200c3 Stavros Sachtouris
            except Exception as qe:
1897 de73876b Stavros Sachtouris
                msg = 'Failed to convert %s to bytes' % user_input,
1898 de73876b Stavros Sachtouris
                raiseCLIError(qe, msg, details=[
1899 326a79b9 Stavros Sachtouris
                    'Syntax: containerlimit set <limit>[format] [container]',
1900 326a79b9 Stavros Sachtouris
                    'e.g.: containerlimit set 2.3GB mycontainer',
1901 326a79b9 Stavros Sachtouris
                    'Valid formats:',
1902 de73876b Stavros Sachtouris
                    '(*1024): B, KiB, MiB, GiB, TiB',
1903 de73876b Stavros Sachtouris
                    '(*1000): B, KB, MB, GB, TB'])
1904 326a79b9 Stavros Sachtouris
        return limit
1905 7493ccb6 Stavros Sachtouris
1906 b4cf92b8 Stavros Sachtouris
    @errors.generic.all
1907 b4cf92b8 Stavros Sachtouris
    @errors.pithos.connection
1908 b4cf92b8 Stavros Sachtouris
    @errors.pithos.container
1909 326a79b9 Stavros Sachtouris
    def _run(self, limit):
1910 b4cf92b8 Stavros Sachtouris
        if self.container:
1911 b4cf92b8 Stavros Sachtouris
            self.client.container = self.container
1912 915b99b5 Stavros Sachtouris
        self._optional_output(self.client.set_container_limit(limit))
1913 b4cf92b8 Stavros Sachtouris
1914 326a79b9 Stavros Sachtouris
    def main(self, limit, container=None):
1915 b4cf92b8 Stavros Sachtouris
        super(self.__class__, self)._run()
1916 326a79b9 Stavros Sachtouris
        limit = self._calculate_limit(limit)
1917 b4cf92b8 Stavros Sachtouris
        self.container = container
1918 326a79b9 Stavros Sachtouris
        self._run(limit)
1919 7493ccb6 Stavros Sachtouris
1920 234954d1 Stavros Sachtouris
1921 d486baec Stavros Sachtouris
@command(pithos_cmds)
1922 915b99b5 Stavros Sachtouris
class file_versioning(_pithos_init):
1923 915b99b5 Stavros Sachtouris
    """Manage the versioning scheme of current pithos user account"""
1924 915b99b5 Stavros Sachtouris
1925 915b99b5 Stavros Sachtouris
1926 915b99b5 Stavros Sachtouris
@command(pithos_cmds)
1927 545c6c29 Stavros Sachtouris
class file_versioning_get(_file_account_command, _optional_json):
1928 776eee69 Stavros Sachtouris
    """Get  versioning for account or container"""
1929 7493ccb6 Stavros Sachtouris
1930 b4cf92b8 Stavros Sachtouris
    @errors.generic.all
1931 b4cf92b8 Stavros Sachtouris
    @errors.pithos.connection
1932 b4cf92b8 Stavros Sachtouris
    @errors.pithos.container
1933 b4cf92b8 Stavros Sachtouris
    def _run(self):
1934 545c6c29 Stavros Sachtouris
        self._print(
1935 545c6c29 Stavros Sachtouris
            self.client.get_container_versioning(self.container) if (
1936 545c6c29 Stavros Sachtouris
                self.container) else self.client.get_account_versioning(),
1937 545c6c29 Stavros Sachtouris
            print_dict)
1938 b4cf92b8 Stavros Sachtouris
1939 234954d1 Stavros Sachtouris
    def main(self, container=None):
1940 b4cf92b8 Stavros Sachtouris
        super(self.__class__, self)._run()
1941 b4cf92b8 Stavros Sachtouris
        self.container = container
1942 b4cf92b8 Stavros Sachtouris
        self._run()
1943 7493ccb6 Stavros Sachtouris
1944 234954d1 Stavros Sachtouris
1945 d486baec Stavros Sachtouris
@command(pithos_cmds)
1946 915b99b5 Stavros Sachtouris
class file_versioning_set(_file_account_command, _optional_output_cmd):
1947 776eee69 Stavros Sachtouris
    """Set versioning mode (auto, none) for account or container"""
1948 7493ccb6 Stavros Sachtouris
1949 b4cf92b8 Stavros Sachtouris
    def _check_versioning(self, versioning):
1950 b4cf92b8 Stavros Sachtouris
        if versioning and versioning.lower() in ('auto', 'none'):
1951 b4cf92b8 Stavros Sachtouris
            return versioning.lower()
1952 b4cf92b8 Stavros Sachtouris
        raiseCLIError('Invalid versioning %s' % versioning, details=[
1953 b4cf92b8 Stavros Sachtouris
            'Versioning can be auto or none'])
1954 b4cf92b8 Stavros Sachtouris
1955 b4cf92b8 Stavros Sachtouris
    @errors.generic.all
1956 b4cf92b8 Stavros Sachtouris
    @errors.pithos.connection
1957 b4cf92b8 Stavros Sachtouris
    @errors.pithos.container
1958 b4cf92b8 Stavros Sachtouris
    def _run(self, versioning):
1959 52edad0a Stavros Sachtouris
        if self.container:
1960 b4cf92b8 Stavros Sachtouris
            self.client.container = self.container
1961 915b99b5 Stavros Sachtouris
            r = self.client.set_container_versioning(versioning)
1962 52edad0a Stavros Sachtouris
        else:
1963 915b99b5 Stavros Sachtouris
            r = self.client.set_account_versioning(versioning)
1964 915b99b5 Stavros Sachtouris
        self._optional_output(r)
1965 b4cf92b8 Stavros Sachtouris
1966 234954d1 Stavros Sachtouris
    def main(self, versioning, container=None):
1967 b4cf92b8 Stavros Sachtouris
        super(self.__class__, self)._run()
1968 b4cf92b8 Stavros Sachtouris
        self._run(self._check_versioning(versioning))
1969 7493ccb6 Stavros Sachtouris
1970 234954d1 Stavros Sachtouris
1971 d486baec Stavros Sachtouris
@command(pithos_cmds)
1972 915b99b5 Stavros Sachtouris
class file_group(_pithos_init):
1973 915b99b5 Stavros Sachtouris
    """Manage access groups and group members"""
1974 915b99b5 Stavros Sachtouris
1975 915b99b5 Stavros Sachtouris
1976 915b99b5 Stavros Sachtouris
@command(pithos_cmds)
1977 545c6c29 Stavros Sachtouris
class file_group_list(_file_account_command, _optional_json):
1978 545c6c29 Stavros Sachtouris
    """list all groups and group members"""
1979 7493ccb6 Stavros Sachtouris
1980 b4cf92b8 Stavros Sachtouris
    @errors.generic.all
1981 b4cf92b8 Stavros Sachtouris
    @errors.pithos.connection
1982 b4cf92b8 Stavros Sachtouris
    def _run(self):
1983 545c6c29 Stavros Sachtouris
        self._print(self.client.get_account_group(), pretty_dict, delim='-')
1984 b4cf92b8 Stavros Sachtouris
1985 7493ccb6 Stavros Sachtouris
    def main(self):
1986 b4cf92b8 Stavros Sachtouris
        super(self.__class__, self)._run()
1987 b4cf92b8 Stavros Sachtouris
        self._run()
1988 7493ccb6 Stavros Sachtouris
1989 234954d1 Stavros Sachtouris
1990 d486baec Stavros Sachtouris
@command(pithos_cmds)
1991 915b99b5 Stavros Sachtouris
class file_group_set(_file_account_command, _optional_output_cmd):
1992 4fcc38a2 Stavros Sachtouris
    """Set a user group"""
1993 7493ccb6 Stavros Sachtouris
1994 b4cf92b8 Stavros Sachtouris
    @errors.generic.all
1995 b4cf92b8 Stavros Sachtouris
    @errors.pithos.connection
1996 b4cf92b8 Stavros Sachtouris
    def _run(self, groupname, *users):
1997 915b99b5 Stavros Sachtouris
        self._optional_output(self.client.set_account_group(groupname, users))
1998 b4cf92b8 Stavros Sachtouris
1999 7493ccb6 Stavros Sachtouris
    def main(self, groupname, *users):
2000 b4cf92b8 Stavros Sachtouris
        super(self.__class__, self)._run()
2001 b4cf92b8 Stavros Sachtouris
        if users:
2002 b4cf92b8 Stavros Sachtouris
            self._run(groupname, *users)
2003 b4cf92b8 Stavros Sachtouris
        else:
2004 b4cf92b8 Stavros Sachtouris
            raiseCLIError('No users to add in group %s' % groupname)
2005 7493ccb6 Stavros Sachtouris
2006 234954d1 Stavros Sachtouris
2007 d486baec Stavros Sachtouris
@command(pithos_cmds)
2008 915b99b5 Stavros Sachtouris
class file_group_delete(_file_account_command, _optional_output_cmd):
2009 4fcc38a2 Stavros Sachtouris
    """Delete a user group"""
2010 7493ccb6 Stavros Sachtouris
2011 b4cf92b8 Stavros Sachtouris
    @errors.generic.all
2012 b4cf92b8 Stavros Sachtouris
    @errors.pithos.connection
2013 b4cf92b8 Stavros Sachtouris
    def _run(self, groupname):
2014 915b99b5 Stavros Sachtouris
        self._optional_output(self.client.del_account_group(groupname))
2015 b4cf92b8 Stavros Sachtouris
2016 7493ccb6 Stavros Sachtouris
    def main(self, groupname):
2017 b4cf92b8 Stavros Sachtouris
        super(self.__class__, self)._run()
2018 b4cf92b8 Stavros Sachtouris
        self._run(groupname)
2019 a23f6ffe Stavros Sachtouris
2020 234954d1 Stavros Sachtouris
2021 d486baec Stavros Sachtouris
@command(pithos_cmds)
2022 545c6c29 Stavros Sachtouris
class file_sharers(_file_account_command, _optional_json):
2023 4fcc38a2 Stavros Sachtouris
    """List the accounts that share objects with current user"""
2024 a23f6ffe Stavros Sachtouris
2025 2fe2672e Stavros Sachtouris
    arguments = dict(
2026 f40f0cb7 Stavros Sachtouris
        detail=FlagArgument('show detailed output', ('-l', '--details')),
2027 2fe2672e Stavros Sachtouris
        marker=ValueArgument('show output greater then marker', '--marker')
2028 2fe2672e Stavros Sachtouris
    )
2029 a23f6ffe Stavros Sachtouris
2030 b4cf92b8 Stavros Sachtouris
    @errors.generic.all
2031 b4cf92b8 Stavros Sachtouris
    @errors.pithos.connection
2032 b4cf92b8 Stavros Sachtouris
    def _run(self):
2033 b4cf92b8 Stavros Sachtouris
        accounts = self.client.get_sharing_accounts(marker=self['marker'])
2034 545c6c29 Stavros Sachtouris
        if self['json_output'] or self['detail']:
2035 545c6c29 Stavros Sachtouris
            self._print(accounts)
2036 24ff0a35 Stavros Sachtouris
        else:
2037 545c6c29 Stavros Sachtouris
            self._print([acc['name'] for acc in accounts])
2038 a23f6ffe Stavros Sachtouris
2039 b4cf92b8 Stavros Sachtouris
    def main(self):
2040 b4cf92b8 Stavros Sachtouris
        super(self.__class__, self)._run()
2041 b4cf92b8 Stavros Sachtouris
        self._run()
2042 38dc5d2f Stavros Sachtouris
2043 234954d1 Stavros Sachtouris
2044 545c6c29 Stavros Sachtouris
def version_print(versions):
2045 545c6c29 Stavros Sachtouris
    print_items([dict(id=vitem[0], created=strftime(
2046 545c6c29 Stavros Sachtouris
        '%d-%m-%Y %H:%M:%S',
2047 545c6c29 Stavros Sachtouris
        localtime(float(vitem[1])))) for vitem in versions])
2048 545c6c29 Stavros Sachtouris
2049 545c6c29 Stavros Sachtouris
2050 d486baec Stavros Sachtouris
@command(pithos_cmds)
2051 545c6c29 Stavros Sachtouris
class file_versions(_file_container_command, _optional_json):
2052 4fcc38a2 Stavros Sachtouris
    """Get the list of object versions
2053 4fcc38a2 Stavros Sachtouris
    Deleted objects may still have versions that can be used to restore it and
2054 4fcc38a2 Stavros Sachtouris
    get information about its previous state.
2055 4fcc38a2 Stavros Sachtouris
    The version number can be used in a number of other commands, like info,
2056 4fcc38a2 Stavros Sachtouris
    copy, move, meta. See these commands for more information, e.g.
2057 3ae60112 Stavros Sachtouris
    /file info -h
2058 4fcc38a2 Stavros Sachtouris
    """
2059 38dc5d2f Stavros Sachtouris
2060 b4cf92b8 Stavros Sachtouris
    @errors.generic.all
2061 b4cf92b8 Stavros Sachtouris
    @errors.pithos.connection
2062 b4cf92b8 Stavros Sachtouris
    @errors.pithos.container
2063 b4cf92b8 Stavros Sachtouris
    @errors.pithos.object_path
2064 b4cf92b8 Stavros Sachtouris
    def _run(self):
2065 545c6c29 Stavros Sachtouris
        self._print(
2066 545c6c29 Stavros Sachtouris
            self.client.get_object_versionlist(self.path), version_print)
2067 b4cf92b8 Stavros Sachtouris
2068 38dc5d2f Stavros Sachtouris
    def main(self, container___path):
2069 3ae60112 Stavros Sachtouris
        super(file_versions, self)._run(
2070 b4cf92b8 Stavros Sachtouris
            container___path,
2071 b4cf92b8 Stavros Sachtouris
            path_is_optional=False)
2072 b4cf92b8 Stavros Sachtouris
        self._run()