]>
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
):
20 def _get_mirror(self
, query
, *args
):
21 res
= self
.db
.get(query
, *args
)
24 return Mirror(self
.backend
, res
.id, data
=res
)
26 def _get_mirrors(self
, query
, *args
):
27 res
= self
.db
.query(query
, *args
)
30 yield Mirror(self
.backend
, row
.id, data
=row
)
33 mirrors
= self
._get
_mirrors
("SELECT * FROM mirrors \
34 WHERE deleted IS FALSE ORDER BY hostname")
38 def create(self
, hostname
, path
="", owner
=None, contact
=None, user
=None):
39 mirror
= self
._get
_mirror
("INSERT INTO mirrors(hostname, path, owner, contact) \
40 VALUES(%s, %s, %s, %s) RETURNING *", hostname
, path
, owner
, contact
)
43 mirror
.log("created", user
=user
)
47 def get_by_id(self
, id):
48 return self
._get
_mirror
("SELECT * FROM mirrors WHERE id = %s", id)
50 def get_by_hostname(self
, hostname
):
51 return self
._get
_mirror
("SELECT * FROM mirrors \
52 WHERE hostname = %s AND deleted IS FALSE", hostname
)
54 def get_for_location(self
, address
):
55 country_code
= self
.backend
.geoip
.guess_from_address(address
)
57 # Cannot return any good mirrors if location is unknown
63 # Walk through all mirrors
65 if mirror
.country_code
== country_code
:
66 mirrors
.append(mirror
)
68 # XXX needs to search for nearby countries
72 def get_history(self
, limit
=None, offset
=None, mirror
=None, user
=None):
73 query
= "SELECT * FROM mirrors_history"
79 conditions
.append("mirror_id = %s")
80 args
.append(mirror
.id)
83 conditions
.append("user_id = %s")
87 query
+= " WHERE %s" % " AND ".join(conditions
)
89 query
+= " ORDER BY time DESC"
93 query
+= " LIMIT %s,%s"
94 args
+= [offset
, limit
,]
100 for entry
in self
.db
.query(query
, *args
):
101 entry
= logs
.MirrorLogEntry(self
.pakfire
, entry
)
102 entries
.append(entry
)
106 def check(self
, **kwargs
):
108 Runs the mirror check for all mirrors
111 with self
.db
.transaction():
112 mirror
.check(**kwargs
)
115 class Mirror(base
.DataObject
):
118 def __eq__(self
, other
):
119 if isinstance(other
, self
.__class
__):
120 return self
.id == other
.id
122 def log(self
, action
, user
=None):
127 self
.db
.execute("INSERT INTO mirrors_history(mirror_id, action, user_id, time) \
128 VALUES(%s, %s, %s, NOW())", self
.id, action
, user_id
)
130 def set_hostname(self
, hostname
):
131 self
._set
_attribute
("hostname", hostname
)
133 hostname
= property(lambda self
: self
.data
.hostname
, set_hostname
)
135 def set_deleted(self
, deleted
):
136 self
._set
_attribute
("deleted", deleted
)
138 deleted
= property(lambda s
: s
.data
.deleted
, set_deleted
)
142 return self
.data
.path
144 def set_path(self
, path
):
145 self
._set
_attribute
("path", path
)
147 path
= property(lambda self
: self
.data
.path
, set_path
)
151 return self
.make_url()
153 def make_url(self
, path
=""):
154 url
= "%s://%s%s" % (
155 "https" if self
.supports_https
else "http",
160 if path
.startswith("/"):
163 return urlparse
.urljoin(url
, path
)
165 def set_supports_https(self
, supports_https
):
166 self
._set
_attribute
("supports_https", supports_https
)
168 supports_https
= property(lambda s
: s
.data
.supports_https
, set_supports_https
)
170 def set_owner(self
, owner
):
171 self
._set
_attribute
("owner", owner
)
173 owner
= property(lambda self
: self
.data
.owner
or "", set_owner
)
175 def set_contact(self
, contact
):
176 self
._set
_attribute
("contact", contact
)
178 contact
= property(lambda self
: self
.data
.contact
or "", set_contact
)
180 def check(self
, connect_timeout
=10, request_timeout
=10):
181 log
.info("Running mirror check for %s" % self
.hostname
)
183 client
= tornado
.httpclient
.HTTPClient()
185 # Get URL for .timestamp
186 url
= self
.make_url(".timestamp")
187 log
.debug(" Fetching %s..." % url
)
190 time_start
= time
.time()
196 # XXX needs to catch connection resets, DNS errors, etc.
199 response
= client
.fetch(url
,
200 connect_timeout
=connect_timeout
,
201 request_timeout
=request_timeout
)
203 # We expect the response to be an integer
204 # which holds the timestamp of the last sync
205 # in seconds since epoch UTC
207 timestamp
= int(response
.body
)
209 # If we could not parse the timestamp, we probably got
210 # an error page or something similar.
211 # So that's an error then...
215 # Timestamp seems to be okay
217 # Convert to datetime
218 last_sync_at
= datetime
.datetime
.utcfromtimestamp(timestamp
)
220 # Must have synced within 24 hours
221 now
= datetime
.datetime
.utcnow()
222 if now
- last_sync_at
>= datetime
.timedelta(hours
=24):
225 except tornado
.httpclient
.HTTPError
as e
:
230 response_time
= time
.time() - time_start
233 self
.db
.execute("INSERT INTO mirrors_checks(mirror_id, response_time, \
234 http_status, last_sync_at, status) VALUES(%s, %s, %s, %s, %s)",
235 self
.id, response_time
, http_status
, last_sync_at
, status
)
238 def last_check(self
):
239 res
= self
.db
.get("SELECT * FROM mirrors_checks \
240 WHERE mirror_id = %s ORDER BY timestamp DESC LIMIT 1", self
.id)
247 return self
.last_check
.status
250 def average_response_time(self
):
251 res
= self
.db
.get("SELECT AVG(response_time) AS response_time \
252 FROM mirrors_checks WHERE mirror_id = %s \
253 AND timestamp >= NOW() - '24 hours'::interval", self
.id)
255 return res
.response_time
259 return socket
.gethostbyname(self
.hostname
)
262 def country_code(self
):
263 return self
.backend
.geoip
.guess_from_address(self
.address
) or "UNKNOWN"
265 def get_history(self
, *args
, **kwargs
):
266 kwargs
["mirror"] = self
268 return self
.pakfire
.mirrors
.get_history(*args
, **kwargs
)