Statistics
| Branch: | Tag: | Revision:

root / kamaki / cli / commands / pithos.py @ 8dbf5a1a

History | View | Annotate | Download (78 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 54b6be76 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 234954d1 Stavros Sachtouris
# Command specs
142 234954d1 Stavros Sachtouris
143 e3d4d442 Stavros Sachtouris
144 5eae854d Stavros Sachtouris
class _pithos_init(_command_init):
145 befed235 Stavros Sachtouris
    """Initialize a pithos+ kamaki client"""
146 befed235 Stavros Sachtouris
147 ece4ae4b Stavros Sachtouris
    @staticmethod
148 ece4ae4b Stavros Sachtouris
    def _is_dir(remote_dict):
149 ece4ae4b Stavros Sachtouris
        return 'application/directory' == remote_dict.get(
150 54b6be76 Stavros Sachtouris
            'content_type', remote_dict.get('content-type', ''))
151 ece4ae4b Stavros Sachtouris
152 1395c40e Stavros Sachtouris
    @errors.generic.all
153 1395c40e Stavros Sachtouris
    def _run(self):
154 3ae60112 Stavros Sachtouris
        self.token = self.config.get('file', 'token')\
155 234954d1 Stavros Sachtouris
            or self.config.get('global', 'token')
156 54b6be76 Stavros Sachtouris
157 54b6be76 Stavros Sachtouris
        if getattr(self, 'auth_base', False):
158 54b6be76 Stavros Sachtouris
            pithos_endpoints = self.auth_base.get_service_endpoints(
159 54b6be76 Stavros Sachtouris
                self.config.get('pithos', 'type'),
160 54b6be76 Stavros Sachtouris
                self.config.get('pithos', 'version'))
161 54b6be76 Stavros Sachtouris
            self.base_url = pithos_endpoints['publicURL']
162 54b6be76 Stavros Sachtouris
        else:
163 92101413 Stavros Sachtouris
            self.base_url = self.config.get('file', 'url')\
164 92101413 Stavros Sachtouris
                or self.config.get('store', 'url')\
165 92101413 Stavros Sachtouris
                or self.config.get('pithos', 'url')
166 54b6be76 Stavros Sachtouris
        if not self.base_url:
167 54b6be76 Stavros Sachtouris
            raise CLIBaseUrlError(service='pithos')
168 54b6be76 Stavros Sachtouris
169 1f5debf7 Stavros Sachtouris
        self._set_account()
170 3ae60112 Stavros Sachtouris
        self.container = self.config.get('file', 'container')\
171 92101413 Stavros Sachtouris
            or self.config.get('store', 'container')\
172 92101413 Stavros Sachtouris
            or self.config.get('pithos', 'container')\
173 234954d1 Stavros Sachtouris
            or self.config.get('global', 'container')
174 de73876b Stavros Sachtouris
        self.client = PithosClient(
175 de73876b Stavros Sachtouris
            base_url=self.base_url,
176 234954d1 Stavros Sachtouris
            token=self.token,
177 234954d1 Stavros Sachtouris
            account=self.account,
178 7493ccb6 Stavros Sachtouris
            container=self.container)
179 f47417e7 Stavros Sachtouris
        self._set_log_params()
180 c5b9380c Stavros Sachtouris
        self._update_max_threads()
181 7493ccb6 Stavros Sachtouris
182 1395c40e Stavros Sachtouris
    def main(self):
183 1395c40e Stavros Sachtouris
        self._run()
184 1395c40e Stavros Sachtouris
185 1f5debf7 Stavros Sachtouris
    def _set_account(self):
186 92101413 Stavros Sachtouris
        if getattr(self, 'auth_base', False):
187 8c54338a Stavros Sachtouris
            self.account = self.auth_base.user_term('id', self.token)
188 3c346c91 Stavros Sachtouris
        else:
189 92101413 Stavros Sachtouris
            astakos_url = self.config.get('user', 'url')\
190 92101413 Stavros Sachtouris
                or self.config.get('astakos', 'url')
191 3c346c91 Stavros Sachtouris
            if not astakos_url:
192 3c346c91 Stavros Sachtouris
                raise CLIBaseUrlError(service='astakos')
193 3c346c91 Stavros Sachtouris
            astakos = AstakosClient(astakos_url, self.token)
194 8c54338a Stavros Sachtouris
            self.account = astakos.user_term('id')
195 1f5debf7 Stavros Sachtouris
196 234954d1 Stavros Sachtouris
197 3ae60112 Stavros Sachtouris
class _file_account_command(_pithos_init):
198 7493ccb6 Stavros Sachtouris
    """Base class for account level storage commands"""
199 7493ccb6 Stavros Sachtouris
200 f724cd35 Stavros Sachtouris
    def __init__(self, arguments={}, auth_base=None):
201 f724cd35 Stavros Sachtouris
        super(_file_account_command, self).__init__(arguments, auth_base)
202 439826ec Stavros Sachtouris
        self['account'] = ValueArgument(
203 47ae7577 Stavros Sachtouris
            'Set user account (not permanent)',
204 f40f0cb7 Stavros Sachtouris
            ('-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 47ae7577 Stavros Sachtouris
            'Set container to work with (temporary)',
228 f40f0cb7 Stavros Sachtouris
            ('-C', '--container'))
229 7493ccb6 Stavros Sachtouris
230 de73876b Stavros Sachtouris
    def extract_container_and_path(
231 24ff0a35 Stavros Sachtouris
            self,
232 24ff0a35 Stavros Sachtouris
            container_with_path,
233 24ff0a35 Stavros Sachtouris
            path_is_optional=True):
234 1395c40e Stavros Sachtouris
        """Contains all heuristics for deciding what should be used as
235 1395c40e Stavros Sachtouris
        container or path. Options are:
236 1395c40e Stavros Sachtouris
        * user string of the form container:path
237 1395c40e Stavros Sachtouris
        * self.container, self.path variables set by super constructor, or
238 1395c40e Stavros Sachtouris
        explicitly by the caller application
239 1395c40e Stavros Sachtouris
        Error handling is explicit as these error cases happen only here
240 1395c40e Stavros Sachtouris
        """
241 83ba5545 Stavros Sachtouris
        try:
242 83ba5545 Stavros Sachtouris
            assert isinstance(container_with_path, str)
243 83ba5545 Stavros Sachtouris
        except AssertionError as err:
244 edab7ba7 Stavros Sachtouris
            if self['container'] and path_is_optional:
245 edab7ba7 Stavros Sachtouris
                self.container = self['container']
246 edab7ba7 Stavros Sachtouris
                self.client.container = self['container']
247 edab7ba7 Stavros Sachtouris
                return
248 83ba5545 Stavros Sachtouris
            raiseCLIError(err)
249 447c9568 Stavros Sachtouris
250 1395c40e Stavros Sachtouris
        user_cont, sep, userpath = container_with_path.partition(':')
251 447c9568 Stavros Sachtouris
252 447c9568 Stavros Sachtouris
        if sep:
253 1395c40e Stavros Sachtouris
            if not user_cont:
254 2005b18e Stavros Sachtouris
                raiseCLIError(CLISyntaxError(
255 2005b18e Stavros Sachtouris
                    'Container is missing\n',
256 1395c40e Stavros Sachtouris
                    details=errors.pithos.container_howto))
257 47ae7577 Stavros Sachtouris
            alt_cont = self['container']
258 1395c40e Stavros Sachtouris
            if alt_cont and user_cont != alt_cont:
259 c1558584 Stavros Sachtouris
                raiseCLIError(CLISyntaxError(
260 1395c40e Stavros Sachtouris
                    'Conflict: 2 containers (%s, %s)' % (user_cont, alt_cont),
261 1395c40e Stavros Sachtouris
                    details=errors.pithos.container_howto)
262 47ae7577 Stavros Sachtouris
                )
263 1395c40e Stavros Sachtouris
            self.container = user_cont
264 1395c40e Stavros Sachtouris
            if not userpath:
265 c1558584 Stavros Sachtouris
                raiseCLIError(CLISyntaxError(
266 1395c40e Stavros Sachtouris
                    'Path is missing for object in container %s' % user_cont,
267 1395c40e Stavros Sachtouris
                    details=errors.pithos.container_howto)
268 47ae7577 Stavros Sachtouris
                )
269 1395c40e Stavros Sachtouris
            self.path = userpath
270 447c9568 Stavros Sachtouris
        else:
271 47ae7577 Stavros Sachtouris
            alt_cont = self['container'] or self.client.container
272 447c9568 Stavros Sachtouris
            if alt_cont:
273 447c9568 Stavros Sachtouris
                self.container = alt_cont
274 1395c40e Stavros Sachtouris
                self.path = user_cont
275 447c9568 Stavros Sachtouris
            elif path_is_optional:
276 1395c40e Stavros Sachtouris
                self.container = user_cont
277 7493ccb6 Stavros Sachtouris
                self.path = None
278 7493ccb6 Stavros Sachtouris
            else:
279 1395c40e Stavros Sachtouris
                self.container = user_cont
280 447c9568 Stavros Sachtouris
                raiseCLIError(CLISyntaxError(
281 c1558584 Stavros Sachtouris
                    'Both container and path are required',
282 1395c40e Stavros Sachtouris
                    details=errors.pithos.container_howto)
283 47ae7577 Stavros Sachtouris
                )
284 7493ccb6 Stavros Sachtouris
285 1395c40e Stavros Sachtouris
    @errors.generic.all
286 1395c40e Stavros Sachtouris
    def _run(self, container_with_path=None, path_is_optional=True):
287 3ae60112 Stavros Sachtouris
        super(_file_container_command, self)._run()
288 edab7ba7 Stavros Sachtouris
        if self['container']:
289 edab7ba7 Stavros Sachtouris
            self.client.container = self['container']
290 edab7ba7 Stavros Sachtouris
            if container_with_path:
291 edab7ba7 Stavros Sachtouris
                self.path = container_with_path
292 edab7ba7 Stavros Sachtouris
            elif not path_is_optional:
293 edab7ba7 Stavros Sachtouris
                raise CLISyntaxError(
294 edab7ba7 Stavros Sachtouris
                    'Both container and path are required',
295 edab7ba7 Stavros Sachtouris
                    details=errors.pithos.container_howto)
296 edab7ba7 Stavros Sachtouris
        elif container_with_path:
297 47ae7577 Stavros Sachtouris
            self.extract_container_and_path(
298 47ae7577 Stavros Sachtouris
                container_with_path,
299 234954d1 Stavros Sachtouris
                path_is_optional)
300 7493ccb6 Stavros Sachtouris
            self.client.container = self.container
301 7493ccb6 Stavros Sachtouris
        self.container = self.client.container
302 7493ccb6 Stavros Sachtouris
303 1395c40e Stavros Sachtouris
    def main(self, container_with_path=None, path_is_optional=True):
304 1395c40e Stavros Sachtouris
        self._run(container_with_path, path_is_optional)
305 1395c40e Stavros Sachtouris
306 7493ccb6 Stavros Sachtouris
307 d486baec Stavros Sachtouris
@command(pithos_cmds)
308 545c6c29 Stavros Sachtouris
class file_list(_file_container_command, _optional_json):
309 7493ccb6 Stavros Sachtouris
    """List containers, object trees or objects in a directory
310 2703cceb Stavros Sachtouris
    Use with:
311 7ae842c2 Stavros Sachtouris
    1 no parameters : containers in current account
312 2703cceb Stavros Sachtouris
    2. one parameter (container) or --container : contents of container
313 2703cceb Stavros Sachtouris
    3. <container>:<prefix> or --container=<container> <prefix>: objects in
314 439826ec Stavros Sachtouris
    .   container starting with prefix
315 7493ccb6 Stavros Sachtouris
    """
316 7493ccb6 Stavros Sachtouris
317 47ae7577 Stavros Sachtouris
    arguments = dict(
318 f40f0cb7 Stavros Sachtouris
        detail=FlagArgument('detailed output', ('-l', '--list')),
319 f40f0cb7 Stavros Sachtouris
        limit=IntArgument('limit number of listed items', ('-n', '--number')),
320 f40f0cb7 Stavros Sachtouris
        marker=ValueArgument('output greater that marker', '--marker'),
321 f40f0cb7 Stavros Sachtouris
        prefix=ValueArgument('output starting with prefix', '--prefix'),
322 47ae7577 Stavros Sachtouris
        delimiter=ValueArgument('show output up to delimiter', '--delimiter'),
323 47ae7577 Stavros Sachtouris
        path=ValueArgument(
324 47ae7577 Stavros Sachtouris
            'show output starting with prefix up to /',
325 47ae7577 Stavros Sachtouris
            '--path'),
326 47ae7577 Stavros Sachtouris
        meta=ValueArgument(
327 47ae7577 Stavros Sachtouris
            'show output with specified meta keys',
328 47ae7577 Stavros Sachtouris
            '--meta',
329 47ae7577 Stavros Sachtouris
            default=[]),
330 47ae7577 Stavros Sachtouris
        if_modified_since=ValueArgument(
331 47ae7577 Stavros Sachtouris
            'show output modified since then',
332 47ae7577 Stavros Sachtouris
            '--if-modified-since'),
333 47ae7577 Stavros Sachtouris
        if_unmodified_since=ValueArgument(
334 47ae7577 Stavros Sachtouris
            'show output not modified since then',
335 47ae7577 Stavros Sachtouris
            '--if-unmodified-since'),
336 47ae7577 Stavros Sachtouris
        until=DateArgument('show metadata until then', '--until'),
337 47ae7577 Stavros Sachtouris
        format=ValueArgument(
338 47ae7577 Stavros Sachtouris
            'format to parse until data (default: d/m/Y H:M:S )',
339 47ae7577 Stavros Sachtouris
            '--format'),
340 47ae7577 Stavros Sachtouris
        shared=FlagArgument('show only shared', '--shared'),
341 439826ec Stavros Sachtouris
        more=FlagArgument(
342 439826ec Stavros Sachtouris
            'output results in pages (-n to set items per page, default 10)',
343 eb18b8a7 Stavros Sachtouris
            '--more'),
344 eb18b8a7 Stavros Sachtouris
        exact_match=FlagArgument(
345 eb18b8a7 Stavros Sachtouris
            'Show only objects that match exactly with path',
346 ed9af02c Stavros Sachtouris
            '--exact-match'),
347 545c6c29 Stavros Sachtouris
        enum=FlagArgument('Enumerate results', '--enumerate')
348 47ae7577 Stavros Sachtouris
    )
349 c41a86b2 Stavros Sachtouris
350 7493ccb6 Stavros Sachtouris
    def print_objects(self, object_list):
351 0399ac7e Stavros Sachtouris
        if self['json_output']:
352 0399ac7e Stavros Sachtouris
            print_json(object_list)
353 0399ac7e Stavros Sachtouris
            return
354 439826ec Stavros Sachtouris
        limit = int(self['limit']) if self['limit'] > 0 else len(object_list)
355 234954d1 Stavros Sachtouris
        for index, obj in enumerate(object_list):
356 24ff0a35 Stavros Sachtouris
            if self['exact_match'] and self.path and not (
357 2005b18e Stavros Sachtouris
                    obj['name'] == self.path or 'content_type' in obj):
358 2005b18e Stavros Sachtouris
                continue
359 7493ccb6 Stavros Sachtouris
            pretty_obj = obj.copy()
360 7493ccb6 Stavros Sachtouris
            index += 1
361 234954d1 Stavros Sachtouris
            empty_space = ' ' * (len(str(len(object_list))) - len(str(index)))
362 7493ccb6 Stavros Sachtouris
            if obj['content_type'] == 'application/directory':
363 7493ccb6 Stavros Sachtouris
                isDir = True
364 7493ccb6 Stavros Sachtouris
                size = 'D'
365 7493ccb6 Stavros Sachtouris
            else:
366 7493ccb6 Stavros Sachtouris
                isDir = False
367 7493ccb6 Stavros Sachtouris
                size = format_size(obj['bytes'])
368 234954d1 Stavros Sachtouris
                pretty_obj['bytes'] = '%s (%s)' % (obj['bytes'], size)
369 7493ccb6 Stavros Sachtouris
            oname = bold(obj['name'])
370 ed9af02c Stavros Sachtouris
            prfx = ('%s%s. ' % (empty_space, index)) if self['enum'] else ''
371 47ae7577 Stavros Sachtouris
            if self['detail']:
372 ed9af02c Stavros Sachtouris
                print('%s%s' % (prfx, oname))
373 7493ccb6 Stavros Sachtouris
                print_dict(pretty_keys(pretty_obj), exclude=('name'))
374 7493ccb6 Stavros Sachtouris
                print
375 7493ccb6 Stavros Sachtouris
            else:
376 ed9af02c Stavros Sachtouris
                oname = '%s%9s %s' % (prfx, size, oname)
377 7493ccb6 Stavros Sachtouris
                oname += '/' if isDir else ''
378 7493ccb6 Stavros Sachtouris
                print(oname)
379 439826ec Stavros Sachtouris
            if self['more']:
380 439826ec Stavros Sachtouris
                page_hold(index, limit, len(object_list))
381 7493ccb6 Stavros Sachtouris
382 7493ccb6 Stavros Sachtouris
    def print_containers(self, container_list):
383 0399ac7e Stavros Sachtouris
        if self['json_output']:
384 0399ac7e Stavros Sachtouris
            print_json(container_list)
385 0399ac7e Stavros Sachtouris
            return
386 439826ec Stavros Sachtouris
        limit = int(self['limit']) if self['limit'] > 0\
387 439826ec Stavros Sachtouris
            else len(container_list)
388 234954d1 Stavros Sachtouris
        for index, container in enumerate(container_list):
389 234954d1 Stavros Sachtouris
            if 'bytes' in container:
390 234954d1 Stavros Sachtouris
                size = format_size(container['bytes'])
391 ed9af02c Stavros Sachtouris
            prfx = ('%s. ' % (index + 1)) if self['enum'] else ''
392 ed9af02c Stavros Sachtouris
            cname = '%s%s' % (prfx, bold(container['name']))
393 47ae7577 Stavros Sachtouris
            if self['detail']:
394 7493ccb6 Stavros Sachtouris
                print(cname)
395 7493ccb6 Stavros Sachtouris
                pretty_c = container.copy()
396 234954d1 Stavros Sachtouris
                if 'bytes' in container:
397 234954d1 Stavros Sachtouris
                    pretty_c['bytes'] = '%s (%s)' % (container['bytes'], size)
398 7493ccb6 Stavros Sachtouris
                print_dict(pretty_keys(pretty_c), exclude=('name'))
399 7493ccb6 Stavros Sachtouris
                print
400 7493ccb6 Stavros Sachtouris
            else:
401 234954d1 Stavros Sachtouris
                if 'count' in container and 'bytes' in container:
402 de73876b Stavros Sachtouris
                    print('%s (%s, %s objects)' % (
403 de73876b Stavros Sachtouris
                        cname,
404 de73876b Stavros Sachtouris
                        size,
405 de73876b Stavros Sachtouris
                        container['count']))
406 7493ccb6 Stavros Sachtouris
                else:
407 7493ccb6 Stavros Sachtouris
                    print(cname)
408 439826ec Stavros Sachtouris
            if self['more']:
409 439826ec Stavros Sachtouris
                page_hold(index + 1, limit, len(container_list))
410 7493ccb6 Stavros Sachtouris
411 1395c40e Stavros Sachtouris
    @errors.generic.all
412 1395c40e Stavros Sachtouris
    @errors.pithos.connection
413 1395c40e Stavros Sachtouris
    @errors.pithos.object_path
414 1395c40e Stavros Sachtouris
    @errors.pithos.container
415 1395c40e Stavros Sachtouris
    def _run(self):
416 1395c40e Stavros Sachtouris
        if self.container is None:
417 1395c40e Stavros Sachtouris
            r = self.client.account_get(
418 1395c40e Stavros Sachtouris
                limit=False if self['more'] else self['limit'],
419 1395c40e Stavros Sachtouris
                marker=self['marker'],
420 1395c40e Stavros Sachtouris
                if_modified_since=self['if_modified_since'],
421 1395c40e Stavros Sachtouris
                if_unmodified_since=self['if_unmodified_since'],
422 1395c40e Stavros Sachtouris
                until=self['until'],
423 1395c40e Stavros Sachtouris
                show_only_shared=self['shared'])
424 545c6c29 Stavros Sachtouris
            self._print(r.json, self.print_containers)
425 1395c40e Stavros Sachtouris
        else:
426 52edad0a Stavros Sachtouris
            prefix = self.path or self['prefix']
427 1395c40e Stavros Sachtouris
            r = self.client.container_get(
428 1395c40e Stavros Sachtouris
                limit=False if self['more'] else self['limit'],
429 1395c40e Stavros Sachtouris
                marker=self['marker'],
430 1395c40e Stavros Sachtouris
                prefix=prefix,
431 1395c40e Stavros Sachtouris
                delimiter=self['delimiter'],
432 1395c40e Stavros Sachtouris
                path=self['path'],
433 1395c40e Stavros Sachtouris
                if_modified_since=self['if_modified_since'],
434 1395c40e Stavros Sachtouris
                if_unmodified_since=self['if_unmodified_since'],
435 1395c40e Stavros Sachtouris
                until=self['until'],
436 1395c40e Stavros Sachtouris
                meta=self['meta'],
437 1395c40e Stavros Sachtouris
                show_only_shared=self['shared'])
438 545c6c29 Stavros Sachtouris
            self._print(r.json, self.print_objects)
439 1395c40e Stavros Sachtouris
440 7493ccb6 Stavros Sachtouris
    def main(self, container____path__=None):
441 1395c40e Stavros Sachtouris
        super(self.__class__, self)._run(container____path__)
442 1395c40e Stavros Sachtouris
        self._run()
443 7493ccb6 Stavros Sachtouris
444 234954d1 Stavros Sachtouris
445 d486baec Stavros Sachtouris
@command(pithos_cmds)
446 915b99b5 Stavros Sachtouris
class file_mkdir(_file_container_command, _optional_output_cmd):
447 f5d9bc54 Stavros Sachtouris
    """Create a directory"""
448 1e29b9f6 Stavros Sachtouris
449 3d568c09 Stavros Sachtouris
    __doc__ += '\n. '.join([
450 3d568c09 Stavros Sachtouris
        'Kamaki hanldes directories the same way as OOS Storage and Pithos+:',
451 3d568c09 Stavros Sachtouris
        'A   directory  is   an  object  with  type  "application/directory"',
452 3d568c09 Stavros Sachtouris
        'An object with path  dir/name can exist even if  dir does not exist',
453 3d568c09 Stavros Sachtouris
        'or even if dir  is  a non  directory  object.  Users can modify dir',
454 3d568c09 Stavros Sachtouris
        'without affecting the dir/name object in any way.'])
455 7493ccb6 Stavros Sachtouris
456 1395c40e Stavros Sachtouris
    @errors.generic.all
457 1395c40e Stavros Sachtouris
    @errors.pithos.connection
458 1395c40e Stavros Sachtouris
    @errors.pithos.container
459 1395c40e Stavros Sachtouris
    def _run(self):
460 915b99b5 Stavros Sachtouris
        self._optional_output(self.client.create_directory(self.path))
461 1395c40e Stavros Sachtouris
462 7493ccb6 Stavros Sachtouris
    def main(self, container___directory):
463 1395c40e Stavros Sachtouris
        super(self.__class__, self)._run(
464 1395c40e Stavros Sachtouris
            container___directory,
465 1395c40e Stavros Sachtouris
            path_is_optional=False)
466 1395c40e Stavros Sachtouris
        self._run()
467 7493ccb6 Stavros Sachtouris
468 234954d1 Stavros Sachtouris
469 d486baec Stavros Sachtouris
@command(pithos_cmds)
470 915b99b5 Stavros Sachtouris
class file_touch(_file_container_command, _optional_output_cmd):
471 1e29b9f6 Stavros Sachtouris
    """Create an empty object (file)
472 1e29b9f6 Stavros Sachtouris
    If object exists, this command will reset it to 0 length
473 1e29b9f6 Stavros Sachtouris
    """
474 1e29b9f6 Stavros Sachtouris
475 1e29b9f6 Stavros Sachtouris
    arguments = dict(
476 1e29b9f6 Stavros Sachtouris
        content_type=ValueArgument(
477 1e29b9f6 Stavros Sachtouris
            'Set content type (default: application/octet-stream)',
478 1e29b9f6 Stavros Sachtouris
            '--content-type',
479 915b99b5 Stavros Sachtouris
            default='application/octet-stream')
480 1e29b9f6 Stavros Sachtouris
    )
481 1e29b9f6 Stavros Sachtouris
482 1395c40e Stavros Sachtouris
    @errors.generic.all
483 1395c40e Stavros Sachtouris
    @errors.pithos.connection
484 1395c40e Stavros Sachtouris
    @errors.pithos.container
485 1395c40e Stavros Sachtouris
    def _run(self):
486 915b99b5 Stavros Sachtouris
        self._optional_output(
487 915b99b5 Stavros Sachtouris
            self.client.create_object(self.path, self['content_type']))
488 1395c40e Stavros Sachtouris
489 1e29b9f6 Stavros Sachtouris
    def main(self, container___path):
490 3ae60112 Stavros Sachtouris
        super(file_touch, self)._run(
491 52edad0a Stavros Sachtouris
            container___path,
492 52edad0a Stavros Sachtouris
            path_is_optional=False)
493 1395c40e Stavros Sachtouris
        self._run()
494 1e29b9f6 Stavros Sachtouris
495 1e29b9f6 Stavros Sachtouris
496 1e29b9f6 Stavros Sachtouris
@command(pithos_cmds)
497 915b99b5 Stavros Sachtouris
class file_create(_file_container_command, _optional_output_cmd):
498 1e29b9f6 Stavros Sachtouris
    """Create a container"""
499 7493ccb6 Stavros Sachtouris
500 2fe2672e Stavros Sachtouris
    arguments = dict(
501 2fe2672e Stavros Sachtouris
        versioning=ValueArgument(
502 2fe2672e Stavros Sachtouris
            'set container versioning (auto/none)',
503 2fe2672e Stavros Sachtouris
            '--versioning'),
504 3ed6dbde Stavros Sachtouris
        limit=IntArgument('set default container limit', '--limit'),
505 2fe2672e Stavros Sachtouris
        meta=KeyValueArgument(
506 2fe2672e Stavros Sachtouris
            'set container metadata (can be repeated)',
507 915b99b5 Stavros Sachtouris
            '--meta')
508 2fe2672e Stavros Sachtouris
    )
509 7493ccb6 Stavros Sachtouris
510 1395c40e Stavros Sachtouris
    @errors.generic.all
511 1395c40e Stavros Sachtouris
    @errors.pithos.connection
512 1395c40e Stavros Sachtouris
    @errors.pithos.container
513 94bedc5b Stavros Sachtouris
    def _run(self, container):
514 915b99b5 Stavros Sachtouris
        self._optional_output(self.client.create_container(
515 915b99b5 Stavros Sachtouris
            container=container,
516 915b99b5 Stavros Sachtouris
            sizelimit=self['limit'],
517 915b99b5 Stavros Sachtouris
            versioning=self['versioning'],
518 915b99b5 Stavros Sachtouris
            metadata=self['meta']))
519 1395c40e Stavros Sachtouris
520 edab7ba7 Stavros Sachtouris
    def main(self, container=None):
521 1395c40e Stavros Sachtouris
        super(self.__class__, self)._run(container)
522 edab7ba7 Stavros Sachtouris
        if container and self.container != container:
523 1395c40e Stavros Sachtouris
            raiseCLIError('Invalid container name %s' % container, details=[
524 edab7ba7 Stavros Sachtouris
                'Did you mean "%s" ?' % self.container,
525 52edad0a Stavros Sachtouris
                'Use --container for names containing :'])
526 94bedc5b Stavros Sachtouris
        self._run(container)
527 7493ccb6 Stavros Sachtouris
528 234954d1 Stavros Sachtouris
529 3ae60112 Stavros Sachtouris
class _source_destination_command(_file_container_command):
530 2fe2672e Stavros Sachtouris
531 2fe2672e Stavros Sachtouris
    arguments = dict(
532 f40f0cb7 Stavros Sachtouris
        destination_account=ValueArgument('', ('a', '--dst-account')),
533 f40f0cb7 Stavros Sachtouris
        recursive=FlagArgument('', ('-R', '--recursive')),
534 761e0cbf Stavros Sachtouris
        prefix=FlagArgument('', '--with-prefix', default=''),
535 761e0cbf Stavros Sachtouris
        suffix=ValueArgument('', '--with-suffix', default=''),
536 761e0cbf Stavros Sachtouris
        add_prefix=ValueArgument('', '--add-prefix', default=''),
537 761e0cbf Stavros Sachtouris
        add_suffix=ValueArgument('', '--add-suffix', default=''),
538 761e0cbf Stavros Sachtouris
        prefix_replace=ValueArgument('', '--prefix-to-replace', default=''),
539 1d3f006b Stavros Sachtouris
        suffix_replace=ValueArgument('', '--suffix-to-replace', default=''),
540 2fe2672e Stavros Sachtouris
    )
541 7493ccb6 Stavros Sachtouris
542 f724cd35 Stavros Sachtouris
    def __init__(self, arguments={}, auth_base=None):
543 761e0cbf Stavros Sachtouris
        self.arguments.update(arguments)
544 f724cd35 Stavros Sachtouris
        super(_source_destination_command, self).__init__(
545 f724cd35 Stavros Sachtouris
            self.arguments, auth_base)
546 761e0cbf Stavros Sachtouris
547 300da0fb Stavros Sachtouris
    def _run(self, source_container___path, path_is_optional=False):
548 300da0fb Stavros Sachtouris
        super(_source_destination_command, self)._run(
549 300da0fb Stavros Sachtouris
            source_container___path,
550 300da0fb Stavros Sachtouris
            path_is_optional)
551 300da0fb Stavros Sachtouris
        self.dst_client = PithosClient(
552 300da0fb Stavros Sachtouris
            base_url=self.client.base_url,
553 300da0fb Stavros Sachtouris
            token=self.client.token,
554 300da0fb Stavros Sachtouris
            account=self['destination_account'] or self.client.account)
555 300da0fb Stavros Sachtouris
556 300da0fb Stavros Sachtouris
    @errors.generic.all
557 300da0fb Stavros Sachtouris
    @errors.pithos.account
558 300da0fb Stavros Sachtouris
    def _dest_container_path(self, dest_container_path):
559 300da0fb Stavros Sachtouris
        if self['destination_container']:
560 300da0fb Stavros Sachtouris
            self.dst_client.container = self['destination_container']
561 300da0fb Stavros Sachtouris
            return (self['destination_container'], dest_container_path)
562 300da0fb Stavros Sachtouris
        if dest_container_path:
563 300da0fb Stavros Sachtouris
            dst = dest_container_path.split(':')
564 300da0fb Stavros Sachtouris
            if len(dst) > 1:
565 300da0fb Stavros Sachtouris
                try:
566 300da0fb Stavros Sachtouris
                    self.dst_client.container = dst[0]
567 300da0fb Stavros Sachtouris
                    self.dst_client.get_container_info(dst[0])
568 300da0fb Stavros Sachtouris
                except ClientError as err:
569 300da0fb Stavros Sachtouris
                    if err.status in (404, 204):
570 300da0fb Stavros Sachtouris
                        raiseCLIError(
571 300da0fb Stavros Sachtouris
                            'Destination container %s not found' % dst[0])
572 300da0fb Stavros Sachtouris
                    raise
573 300da0fb Stavros Sachtouris
                else:
574 300da0fb Stavros Sachtouris
                    self.dst_client.container = dst[0]
575 300da0fb Stavros Sachtouris
                return (dst[0], dst[1])
576 300da0fb Stavros Sachtouris
            return(None, dst[0])
577 300da0fb Stavros Sachtouris
        raiseCLIError('No destination container:path provided')
578 300da0fb Stavros Sachtouris
579 ece4ae4b Stavros Sachtouris
    def _get_all(self, prefix):
580 ece4ae4b Stavros Sachtouris
        return self.client.container_get(prefix=prefix).json
581 ece4ae4b Stavros Sachtouris
582 1d3f006b Stavros Sachtouris
    def _get_src_objects(self, src_path, source_version=None):
583 6736f171 Stavros Sachtouris
        """Get a list of the source objects to be called
584 6736f171 Stavros Sachtouris

585 6736f171 Stavros Sachtouris
        :param src_path: (str) source path
586 6736f171 Stavros Sachtouris

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