#
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
# Common Development and Distribution License (the "License").
# You may not use this file except in compliance with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# See the License for the specific language governing permissions
# and limitations under the License.
#
# When distributing Covered Code, include this CDDL HEADER in each
# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
# If applicable, add the following below this CDDL HEADER, with the
# fields enclosed by brackets "[]" replaced with your own identifying
# information: Portions Copyright [yyyy] [name of copyright owner]
#
# CDDL HEADER END
#
#
#
"""feed - routines for generating RFC 4287 Atom feeds for packaging server
At present, the pkg.server.feed module provides a set of routines that, from
a catalog, allow the construction of a feed representing the activity within
a given time period."""
import cherrypy
import copy
import datetime
import os
import shutil
import six
import time
"""Returns a string representing a datetime object formatted according
to RFC 3339.
"""
"""Returns a datetime object representing 'ts_str', which should be in
the format specified by RFC 3339.
"""
def fmri_to_taguri(f):
"""Generates a 'tag' uri compliant with RFC 4151. Visit
http://www.taguri.org/ for more information.
"""
unquote(f.get_url_path()))
"""This function performs general initialization work that is needed
for feeds to work correctly.
"""
# Ensure any configuration changes are reflected in the feed.
__clear_cache(depot, None)
to provide title, author and contact information to the provided
xmini document object using the provided feed object and update
time.
"""
t.appendChild(ti)
feed.appendChild(t)
feed.appendChild(l)
# Atom requires each feed to have a permanent, universally unique
# identifier.
path)
i.appendChild(it)
feed.appendChild(i)
# Indicate when the feed was last updated.
u.appendChild(ut)
feed.appendChild(u)
# Add our icon.
i.appendChild(it)
feed.appendChild(i)
# Add our logo.
l.appendChild(lt)
feed.appendChild(l)
"to the repository.")
"""Each transaction is an entry. We have non-trivial content, so we
can omit summary elements.
"""
# Generate a 'tag' uri, to uniquely identify the entry, using the fmri.
eid.appendChild(i)
e.appendChild(eid)
# Attempt to determine the operation that was performed and generate
# the entry title and content.
# XXX renaming, obsoletion?
# If this fmri is not the same as the oldest one
# for the FMRI's package stem, assume this is a
# newer version of that package.
else:
else:
# XXX Better way to reflect an error? (Aborting will make a
# non-well-formed document.)
op_title = "Unknown Operation"
op_content = "{0} was changed in the repository."
# Now add a title for our entry.
# Indicate when the entry was last updated (in this case, when the
# package was added).
e.appendChild(eu)
# Link to the info output for the given package FMRI.
e.appendChild(l)
# Using the description for the operation performed, add the FMRI and
# tag information.
e.appendChild(ec)
feed.appendChild(e)
"""Returns a list of the CatalogUpdate files that contain the changes
that have been made to the catalog since the specified UTC datetime
object 'ts'."""
if c.last_modified <= ts:
# No updates needed.
return []
# The last component of the update name is the locale.
# For now, only look at CatalogUpdates that for the 'C'
# locale. Any other CatalogUpdates just contain localized
# catalog data, so aren't currently interesting.
if locale != "C":
continue
# CatalogUpdate hasn't changed since 'ts'.
continue
if not updates:
# No updates needed.
return []
# Ensure updates are in chronological ascending order.
"""Generate new Atom document for current updates. The cached feed
file is written to depot.tmp_root/CACHE_FILENAME.
"""
# Our configuration is stored in hours, convert it to days and seconds.
d.appendChild(feed)
# Cache the first entry in the catalog for any given package stem found
# in the list of updates so that it can be used to quickly determine if
# the fmri in the update is a 'new' package or an update to an existing
# package.
first = {}
def get_first(f):
stem = f.get_pkg_stem()
# The first version returned is the oldest version.
# Add all of the unique package stems for that version
# to the list.
break
# A value of None is used to denote that no previous
# version exists for this particular stem. This could
# happen when a prior version exists for a different
# publisher, or no prior version exists at all.
# Updates should be presented in reverse chronological order.
# Exclude this particular update.
continue
if not pub:
if not pub:
return
try:
except IOError:
"Unable to clear feed cache.")
"""Checks to see if the feed cache file exists and if it is still
valid. Returns False, None if the cache is valid or True, last
where last is a timestamp representing when the cache was
generated.
"""
last = None
# Attempt to parse the cached copy. If we can't, for any
# reason, assume we need to remove it and start over.
try:
except Exception:
d = None
# Get the feed element and attempt to get the time we last
# generated the feed to determine whether we need to regenerate
# it. If for some reason we can't get that information, assume
# the cache is invalid, clear it, and force regeneration.
fe = None
if d:
if fe:
utn = None
break
if utn:
# Since our feed cache and updatelog might have
# been created within the same second, we need
# to ignore small variances when determining
# whether to update the feed cache.
else:
else:
return need_update, last
"""If there have been package updates since we last generated the feed,
update the feed and send it to the client. Otherwise, send them the
cached copy if it is available.
"""
# First check to see if we already have a valid cache of the feed.
if need_update:
# Update always looks at feed.window seconds before the last
# update until "now." If last is none, we want it to use "now"
# as its starting point.
if last is None:
# Generate and cache the feed.