blob: 83061ffa30a32d0a4af9d32644a7fcf8fb258479 [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 Rasskazov2f93dd22015-06-04 14:20:44 +0300155 def _url_join(self, *suburls):
156 url = self.url
157 if len(url) > 1:
158 while url.endswith(os.path.sep):
159 url = url[:-1]
160
Max Rasskazova021f572015-06-04 15:11:13 +0300161 subs = os.path.sep.join([_ for _ in suburls if _]).split(os.path.sep)
Max Rasskazov2f93dd22015-06-04 14:20:44 +0300162 subs = [_ for _ in subs if _]
163
164 result = re.sub(r'^//', r'/', os.path.sep.join([url, ] + subs))
165 result = re.sub(r'([^:])//', r'\1/', result)
166 return result
167
Max Rasskazov64f77bf2015-06-02 15:22:48 +0300168 @property
169 def url(self):
170 return self._url
Max Rasskazov2f93dd22015-06-04 14:20:44 +0300171
172 def url_in(self, *path):
173 result = self._url_join(*path)
174 if not result.endswith('/'):
175 result += '/'
176 return result
177
178 def url_is(self, *path):
179 return self._url_join(*path)