Statistics
| Branch: | Tag: | Revision:

root / kamaki / cli / commands / pithos.py @ 776b275c

History | View | Annotate | Download (78.6 kB)

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

600 6736f171 Stavros Sachtouris
        :param src_path: (str) source path
601 6736f171 Stavros Sachtouris

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