]>
git.ipfire.org Git - people/jschlag/pbs.git/blob - src/buildservice/mirrors.py
8 import tornado
.httpclient
14 log
= logging
.getLogger("mirrors")
17 from .decorators
import lazy_property
19 class Mirrors(base
.Object
):
21 res
= self
.db
.query("SELECT * FROM mirrors \
22 WHERE deleted IS FALSE ORDER BY hostname")
26 mirror
= Mirror(self
.backend
, row
.id, data
=row
)
27 mirrors
.append(mirror
)
31 def _get_mirror(self
, query
, *args
):
32 res
= self
.db
.get(query
, *args
)
35 return Mirror(self
.backend
, res
.id, data
=res
)
37 def create(self
, hostname
, path
="", owner
=None, contact
=None, user
=None):
38 mirror
= self
._get
_mirror
("INSERT INTO mirrors(hostname, path, owner, contact) \
39 VALUES(%s, %s, %s, %s) RETURNING *", hostname
, path
, owner
, contact
)
42 mirror
.log("created", user
=user
)
46 def get_by_id(self
, id):
47 return self
._get
_mirror
("SELECT * FROM mirrors WHERE id = %s", id)
49 def get_by_hostname(self
, hostname
):
50 return self
._get
_mirror
("SELECT * FROM mirrors \
51 WHERE hostname = %s AND deleted IS FALSE", hostname
)
53 def get_for_location(self
, address
):
54 country_code
= self
.backend
.geoip
.guess_from_address(address
)
56 # Cannot return any good mirrors if location is unknown
62 # Walk through all mirrors
64 if mirror
.country_code
== country_code
:
65 mirrors
.append(mirror
)
67 # XXX needs to search for nearby countries
71 def get_history(self
, limit
=None, offset
=None, mirror
=None, user
=None):
72 query
= "SELECT * FROM mirrors_history"
78 conditions
.append("mirror_id = %s")
79 args
.append(mirror
.id)
82 conditions
.append("user_id = %s")
86 query
+= " WHERE %s" % " AND ".join(conditions
)
88 query
+= " ORDER BY time DESC"
92 query
+= " LIMIT %s,%s"
93 args
+= [offset
, limit
,]
99 for entry
in self
.db
.query(query
, *args
):
100 entry
= logs
.MirrorLogEntry(self
.pakfire
, entry
)
101 entries
.append(entry
)
105 def check(self
, **kwargs
):
107 Runs the mirror check for all mirrors
110 with self
.db
.transaction():
111 mirror
.check(**kwargs
)
114 class Mirror(base
.DataObject
):
117 def __eq__(self
, other
):
118 if isinstance(other
, self
.__class
__):
119 return self
.id == other
.id
121 def log(self
, action
, user
=None):
126 self
.db
.execute("INSERT INTO mirrors_history(mirror_id, action, user_id, time) \
127 VALUES(%s, %s, %s, NOW())", self
.id, action
, user_id
)
129 def set_hostname(self
, hostname
):
130 self
._set
_attribute
("hostname", hostname
)
132 hostname
= property(lambda self
: self
.data
.hostname
, set_hostname
)
134 def set_deleted(self
, deleted
):
135 self
._set
_attribute
("deleted", deleted
)
137 deleted
= property(lambda s
: s
.data
.deleted
, set_deleted
)
141 return self
.data
.path
143 def set_path(self
, path
):
144 self
._set
_attribute
("path", path
)
146 path
= property(lambda self
: self
.data
.path
, set_path
)
150 return self
.make_url()
152 def make_url(self
, path
=""):
153 url
= "%s://%s%s" % (
154 "https" if self
.supports_https
else "http",
159 if path
.startswith("/"):
162 return urlparse
.urljoin(url
, path
)
164 def set_supports_https(self
, supports_https
):
165 self
._set
_attribute
("supports_https", supports_https
)
167 supports_https
= property(lambda s
: s
.data
.supports_https
, set_supports_https
)
169 def set_owner(self
, owner
):
170 self
._set
_attribute
("owner", owner
)
172 owner
= property(lambda self
: self
.data
.owner
or "", set_owner
)
174 def set_contact(self
, contact
):
175 self
._set
_attribute
("contact", contact
)
177 contact
= property(lambda self
: self
.data
.contact
or "", set_contact
)
179 def check(self
, connect_timeout
=10, request_timeout
=10):
180 log
.info("Running mirror check for %s" % self
.hostname
)
182 client
= tornado
.httpclient
.HTTPClient()
184 # Get URL for .timestamp
185 url
= self
.make_url(".timestamp")
186 log
.debug(" Fetching %s..." % url
)
189 time_start
= time
.time()
195 # XXX needs to catch connection resets, DNS errors, etc.
198 response
= client
.fetch(url
,
199 connect_timeout
=connect_timeout
,
200 request_timeout
=request_timeout
)
202 # We expect the response to be an integer
203 # which holds the timestamp of the last sync
204 # in seconds since epoch UTC
206 timestamp
= int(response
.body
)
210 # Convert to datetime
211 last_sync_at
= datetime
.datetime
.utcfromtimestamp(timestamp
)
213 # Must have synced within 24 hours
214 now
= datetime
.datetime
.utcnow()
215 if now
- last_sync_at
>= datetime
.timedelta(hours
=24):
218 except tornado
.httpclient
.HTTPError
as e
:
223 response_time
= time
.time() - time_start
226 self
.db
.execute("INSERT INTO mirrors_checks(mirror_id, response_time, \
227 http_status, last_sync_at, status) VALUES(%s, %s, %s, %s, %s)",
228 self
.id, response_time
, http_status
, last_sync_at
, status
)
231 def last_check(self
):
232 res
= self
.db
.get("SELECT * FROM mirrors_checks \
233 WHERE mirror_id = %s ORDER BY timestamp DESC LIMIT 1", self
.id)
240 return self
.last_check
.status
243 def average_response_time(self
):
244 res
= self
.db
.get("SELECT AVG(response_time) AS response_time \
245 FROM mirrors_checks WHERE mirror_id = %s \
246 AND timestamp >= NOW() - '24 hours'::interval", self
.id)
248 return res
.response_time
252 return socket
.gethostbyname(self
.hostname
)
255 def country_code(self
):
256 return self
.backend
.geoip
.guess_from_address(self
.address
) or "UNKNOWN"
258 def get_history(self
, *args
, **kwargs
):
259 kwargs
["mirror"] = self
261 return self
.pakfire
.mirrors
.get_history(*args
, **kwargs
)