blob: f69e93af3754773a5c1b7866bc27d66679db75aa [file] [log] [blame]
Max Rasskazovb07aacb2015-05-29 19:54:52 +03001#-*- coding: utf-8 -*-
2
Max Rasskazov2f93dd22015-06-04 14:20:44 +03003import os
Max Rasskazovb07aacb2015-05-29 19:54:52 +03004import re
Max Rasskazov2f93dd22015-06-04 14:20:44 +03005
Max Rasskazovb1b09a62015-06-03 15:21:35 +03006import utils
Max Rasskazovb07aacb2015-05-29 19:54:52 +03007
Max Rasskazovb1b09a62015-06-03 15:21:35 +03008
9logger = utils.logger.getChild('RsyncUrl')
Max Rasskazovb07aacb2015-05-29 19:54:52 +030010
11
12class RsyncUrl(object):
13
14 def __init__(self, remote_url):
15
Max Rasskazov64f77bf2015-06-02 15:22:48 +030016 self._url = remote_url
Max Rasskazovb07aacb2015-05-29 19:54:52 +030017 self._url_type = False
18
19 self.regexps = {
20 # ssh: [USER@]HOST:SRC
21 'ssh': re.compile(
22 r'^'
23 r'(?P<user>[-\w]+@)?'
24 r'(?P<host>[-\.\w]+){1}'
25 r':'
Max Rasskazov95e001b2015-05-30 23:53:22 +030026 r'(?P<path>(~{0,1}[\w/-]*)){1}'
Max Rasskazovb07aacb2015-05-29 19:54:52 +030027 r'$'
28 ),
29 # rsync: [USER@]HOST::SRC
30 'rsync1': re.compile(
31 r'^'
32 r'(?P<user>[-\w]+@)?'
33 r'(?P<host>[-\.\w]+){1}'
34 r'::'
Max Rasskazov64f77bf2015-06-02 15:22:48 +030035 r'(?P<module>[\w-]+){1}'
36 r'(?P<path>[\w/-]*)?'
Max Rasskazovb07aacb2015-05-29 19:54:52 +030037 r'$'
38 ),
39 # rsync://[USER@]HOST[:PORT]/SRC
40 'rsync2': re.compile(
41 r'^rsync://'
42 r'(?P<user>[-\w]+@)?'
43 r'(?P<host>[-\.\w]+){1}'
44 r'(?P<port>:[\d]+)?'
Max Rasskazov64f77bf2015-06-02 15:22:48 +030045 r'(?P<module>/[\w-]*)?'
46 r'(?P<path>[\w/-]*)?'
Max Rasskazovb07aacb2015-05-29 19:54:52 +030047 r'$'
48 ),
49 # local/path/to/directory
50 'path': re.compile(
51 r'^'
Max Rasskazov95e001b2015-05-30 23:53:22 +030052 r'(?P<path>(~{0,1}[\w/-]+)){1}'
Max Rasskazovb07aacb2015-05-29 19:54:52 +030053 r'$'
54 ),
55 }
56
Max Rasskazov64f77bf2015-06-02 15:22:48 +030057 self._match = self._get_matching_regexp()
Max Rasskazovb07aacb2015-05-29 19:54:52 +030058 if self.match is None:
Max Rasskazov64f77bf2015-06-02 15:22:48 +030059 self.user, self.host, self.module, self.port, self.path = \
60 None, None, None, None, None
Max Rasskazovb07aacb2015-05-29 19:54:52 +030061 else:
62 self._parse_rsync_url(self.match)
63
64 def _get_matching_regexp(self):
65 regexps = self._get_all_matching_regexps()
66 regexps_len = len(regexps)
67 #if regexps_len > 1:
68 # raise Exception('Rsync location {} matches with {} regexps {}'
69 # ''.format(self.url, len(regexps), str(regexps)))
70 # TODO: Possible may be better remove this raise and keep
71 # only warning with request to fail bug. rsync will parse this
72 # remote later
73 if regexps_len != 1:
74 logger.warn('Rsync location "{}" matches with {} regexps: {}.'
75 'Please fail a bug on {} if it is wrong.'
76 ''.format(self.url, len(regexps), str(regexps), '...'))
77 if regexps_len == 0:
78 self._url_type = None
79 return None
80 else:
81 return regexps[0]
82
83 def _get_all_matching_regexps(self):
84 regexps = list()
85 for url_type, regexp in self.regexps.items():
86 match = regexp.match(self.url)
87 if match is not None:
88 if self.url_type is False:
89 self._url_type = url_type
90 regexps.append(regexp)
Max Rasskazovb07aacb2015-05-29 19:54:52 +030091 return regexps
92
93 def _parse_rsync_url(self, regexp):
94 # parse remote url
95
Max Rasskazov64f77bf2015-06-02 15:22:48 +030096 for match in re.finditer(regexp, self._url):
Max Rasskazovb07aacb2015-05-29 19:54:52 +030097
98 self.path = match.group('path')
Max Rasskazov64f77bf2015-06-02 15:22:48 +030099 if self.path is None:
100 self.path = ''
Max Rasskazovb07aacb2015-05-29 19:54:52 +0300101
102 try:
103 self.host = match.group('host')
104 except IndexError:
105 self.host = None
106
107 try:
108 self.user = match.group('user')
109 except IndexError:
110 self.user = None
111 else:
112 if self.user is not None:
113 self.user = self.user.strip('@')
114
115 try:
116 self.port = match.group('port')
117 except IndexError:
118 self.port = None
119 else:
120 if self.port is not None:
121 self.port = int(self.port.strip(':'))
122
Max Rasskazov64f77bf2015-06-02 15:22:48 +0300123 try:
124 self.module = match.group('module')
125 except IndexError:
126 self.module = None
127 else:
128 if self.module is not None:
129 self.module = self.module.strip('/')
130 if not self.module:
131 self.module = None
132
133 @property
134 def match(self):
135 return self._match
136
Max Rasskazovb07aacb2015-05-29 19:54:52 +0300137 @property
138 def url_type(self):
139 return self._url_type
Max Rasskazov64f77bf2015-06-02 15:22:48 +0300140
141 @property
142 def is_valid(self):
143 if self.match is None:
144 return False
145 if self.path in (None, False):
146 return False
147 if self.url_type != 'path':
148 if self.host in ('', None, False):
149 return False
150 if self.url_type.startswith('rsync'):
151 if self.module is None:
152 return False
153 return True
154
Max Rasskazovba34b142015-06-04 17:12:27 +0300155 def _fn_join(self, *parts):
156 ''' Joins filenames with ignoring empty parts (None, '', etc)'''
157 parts = [_ for _ in parts if _]
Max Rasskazov2f93dd22015-06-04 14:20:44 +0300158
Max Rasskazovba34b142015-06-04 17:12:27 +0300159 if len(parts) > 0:
160 if parts[-1].endswith(os.path.sep):
161 isdir = True
162 else:
163 isdir = False
164 first, parts = parts[0], parts[1:]
165 else:
166 return ''
167
168 if first is None:
169 first = ''
170 if len(first) > 1:
171 while first.endswith(os.path.sep):
172 first = first[:-1]
173
174 subs = os.path.sep.join([_ for _ in parts if _]).split(os.path.sep)
Max Rasskazov2f93dd22015-06-04 14:20:44 +0300175 subs = [_ for _ in subs if _]
176
Max Rasskazovba34b142015-06-04 17:12:27 +0300177 result = re.sub(r'^//', r'/', os.path.sep.join([first, ] + subs))
Max Rasskazov2f93dd22015-06-04 14:20:44 +0300178 result = re.sub(r'([^:])//', r'\1/', result)
Max Rasskazovba34b142015-06-04 17:12:27 +0300179 if not result.endswith(os.path.sep) and isdir:
180 result += os.path.sep
Max Rasskazov2f93dd22015-06-04 14:20:44 +0300181 return result
182
Max Rasskazov64f77bf2015-06-02 15:22:48 +0300183 @property
184 def url(self):
185 return self._url
Max Rasskazov2f93dd22015-06-04 14:20:44 +0300186
Max Rasskazovba34b142015-06-04 17:12:27 +0300187 def urljoin(self, *parts):
188 return self._fn_join(self.url, *parts)
189
Max Rasskazov46cc0732015-06-05 19:23:24 +0300190 def a_dir(self, *path):
Max Rasskazovba34b142015-06-04 17:12:27 +0300191 result = self._fn_join(*path)
Max Rasskazov2f93dd22015-06-04 14:20:44 +0300192 if not result.endswith('/'):
193 result += '/'
194 return result
195
Max Rasskazovba34b142015-06-04 17:12:27 +0300196 def url_in(self, *path):
Max Rasskazov46cc0732015-06-05 19:23:24 +0300197 return self.a_dir(self.url, *path)
Max Rasskazovba34b142015-06-04 17:12:27 +0300198
Max Rasskazov46cc0732015-06-05 19:23:24 +0300199 def a_file(self, *path):
Max Rasskazovba34b142015-06-04 17:12:27 +0300200 result = self._fn_join(*path)
201 if len(result) > 1:
202 while result.endswith(os.path.sep):
203 result = result[:-1]
204 return result
205
Max Rasskazov2f93dd22015-06-04 14:20:44 +0300206 def url_is(self, *path):
Max Rasskazov46cc0732015-06-05 19:23:24 +0300207 return self.a_file(self.url, *path)