45
44
'VersionRequiresName',
46
45
'default_name_by_purpose',
47
'validate_external_dependencies',
51
from urlparse import urlparse
53
48
from lazr.enum import DBEnumeratedType
54
49
from lazr.restful.declarations import (
57
51
export_as_webservice_entry,
58
52
export_factory_operation,
59
53
export_operation_as,
60
54
export_read_operation,
61
55
export_write_operation,
63
operation_for_version,
64
57
operation_parameters,
65
58
operation_returns_collection_of,
66
59
operation_returns_entry,
67
60
rename_parameters_as,
70
64
from lazr.restful.fields import (
118
112
# Exceptions used in the webservice that need to be in this file to get
119
113
# picked up therein.
120
@error_status(httplib.BAD_REQUEST)
121
115
class CannotCopy(Exception):
122
116
"""Exception raised when a copy cannot be performed."""
125
@error_status(httplib.BAD_REQUEST)
117
webservice_error(400) #Bad request.
126
120
class CannotSwitchPrivacy(Exception):
127
121
"""Raised when switching the privacy of an archive that has
128
122
publishing records."""
131
class PocketNotFound(NameLookupFailed):
123
webservice_error(400) # Bad request.
126
class PocketNotFound(Exception):
132
127
"""Invalid pocket."""
133
_message_prefix = "No such pocket"
136
@error_status(httplib.BAD_REQUEST)
128
webservice_error(400) #Bad request.
131
class DistroSeriesNotFound(Exception):
132
"""Invalid distroseries."""
133
webservice_error(400) #Bad request.
137
136
class AlreadySubscribed(Exception):
138
137
"""Raised when creating a subscription for a subscribed person."""
141
@error_status(httplib.BAD_REQUEST)
138
webservice_error(400) # Bad request.
142
141
class ArchiveNotPrivate(Exception):
143
142
"""Raised when creating an archive subscription for a public archive."""
146
@error_status(httplib.BAD_REQUEST)
143
webservice_error(400) # Bad request.
147
146
class NoTokensForTeams(Exception):
148
147
"""Raised when creating a token for a team, rather than a person."""
151
class ComponentNotFound(NameLookupFailed):
148
webservice_error(400) # Bad request.
151
class ComponentNotFound(Exception):
152
152
"""Invalid source name."""
153
_message_prefix = 'No such component'
156
@error_status(httplib.BAD_REQUEST)
153
webservice_error(400) #Bad request.
157
156
class InvalidComponent(Exception):
158
157
"""Invalid component name."""
158
webservice_error(400) #Bad request.
161
161
class NoSuchPPA(NameLookupFailed):
162
162
"""Raised when we try to look up an PPA that doesn't exist."""
163
webservice_error(400) #Bad request.
163
164
_message_prefix = "No such ppa"
166
@error_status(httplib.BAD_REQUEST)
167
167
class VersionRequiresName(Exception):
168
168
"""Raised on some queries when version is specified but name is not."""
169
webservice_error(400) # Bad request.
171
172
class CannotRestrictArchitectures(Exception):
172
173
"""The architectures for this archive can not be restricted."""
175
@error_status(httplib.FORBIDDEN)
176
176
class CannotUploadToArchive(Exception):
177
177
"""A reason for not being able to upload to an archive."""
178
webservice_error(403) # Forbidden.
179
180
_fmt = '%(person)s has no upload rights to %(archive)s.'
247
248
CannotUploadToArchive.__init__(self, archive_name=archive_name)
250
@error_status(httplib.BAD_REQUEST)
251
class InvalidExternalDependencies(Exception):
252
"""Tried to set external dependencies to an invalid value."""
254
def __init__(self, errors):
255
error_msg = 'Invalid external dependencies:\n%s\n' % '\n'.join(errors)
256
super(Exception, self).__init__(self, error_msg)
260
251
class IArchivePublic(IHasOwner, IPrivacy):
261
252
"""An Archive interface for publicly available operations."""
262
253
id = Attribute("The archive ID.")
421
412
"A delta to apply to all build scores for the archive. Builds "
422
413
"with a higher score will build sooner."))
424
external_dependencies = exported(
425
Text(title=_("External dependencies"), required=False,
426
readonly=False, description=_(
415
external_dependencies = Text(
416
title=_("External dependencies"), required=False, readonly=False,
427
418
"Newline-separated list of repositories to be used to retrieve "
428
419
"any external build dependencies when building packages in the "
429
420
"archive, in the format:\n"
432
423
"The series variable is replaced with the series name of the "
433
424
"context build.\n"
434
"NOTE: This is for migration of OEM PPAs only!")))
425
"NOTE: This is for migration of OEM PPAs only!"))
436
enabled_restricted_families = exported(
427
enabled_restricted_families = CollectionField(
438
428
title=_("Enabled restricted families"),
440
430
"The restricted architecture families on which the archive "
442
value_type=Reference(schema=Interface),
443
# Really IProcessorFamily.
432
value_type=Reference(schema=IProcessorFamily),
447
435
commercial = exported(
523
def removeArchiveDependency(dependency):
524
"""Remove the `IArchiveDependency` record for the given dependency.
526
:param dependency: is an `IArchive` object.
529
def addArchiveDependency(dependency, pocket, component=None):
530
"""Record an archive dependency record for the context archive.
532
:param dependency: is an `IArchive` object.
533
:param pocket: is an `PackagePublishingPocket` enum.
534
:param component: is an optional `IComponent` object, if not given
535
the archive dependency will be tied to the component used
536
for a corresponding source in primary archive.
538
:raise: `ArchiveDependencyError` if given 'dependency' does not fit
540
:return: a `IArchiveDependency` object targeted to the context
541
`IArchive` requiring 'dependency' `IArchive`.
535
544
def getPermissions(person, item, perm_type):
536
545
"""Get the `IArchivePermission` record with the supplied details.
1173
1173
@operation_returns_collection_of(Interface)
1174
1174
@export_read_operation()
1175
1175
def getComponentsForQueueAdmin(person):
1176
"""Return `IArchivePermission` for the person's queue admin
1176
"""Return `IArchivePermission` for the person's queue admin components
1179
:param person: An `IPerson`.
1178
:param person: An `IPerson`
1180
1179
:return: A list of `IArchivePermission` records.
1183
def hasAnyPermission(person):
1184
"""Whether or not this person has any permission at all on this
1187
:param person: The `IPerson` for whom the check is performed.
1188
:return: A boolean indicating if the person has any permission on this
1192
1182
def getPackageDownloadCount(bpr, day, country):
1193
1183
"""Get the `IBinaryPackageDownloadCount` with the given key."""
1222
1212
class IArchiveAppend(Interface):
1223
1213
"""Archive interface for operations restricted by append privilege."""
1225
@call_with(person=REQUEST_USER)
1226
1215
@operation_parameters(
1227
1216
source_names=List(
1228
1217
title=_("Source package names"),
1229
1218
value_type=TextLine()),
1230
from_archive=Reference(schema=Interface),
1231
#Really IArchive, see below
1219
from_archive=Reference(schema=Interface), #Really IArchive, see below
1232
1220
to_pocket=TextLine(title=_("Pocket name")),
1233
1221
to_series=TextLine(title=_("Distroseries name"), required=False),
1234
1222
include_binaries=Bool(
1239
1227
@export_write_operation()
1240
1228
# Source_names is a string because exporting a SourcePackageName is
1241
1229
# rather nonsensical as it only has id and name columns.
1242
def syncSources(source_names, from_archive, to_pocket, to_series=None,
1243
include_binaries=False, person=None):
1230
def syncSources(source_names, from_archive, to_pocket,
1231
to_series=None, include_binaries=False):
1244
1232
"""Synchronise (copy) named sources into this archive from another.
1246
1234
It will copy the most recent PUBLISHED versions of the named
1258
1246
:param include_binaries: optional boolean, controls whether or not
1259
1247
the published binaries for each given source should also be
1260
1248
copied along with the source.
1261
:param person: the `IPerson` who requests the sync.
1263
1250
:raises NoSuchSourcePackageName: if the source name is invalid
1264
1251
:raises PocketNotFound: if the pocket name is invalid
1265
:raises NoSuchDistroSeries: if the distro series name is invalid
1252
:raises DistroSeriesNotFound: if the distro series name is invalid
1266
1253
:raises CannotCopy: if there is a problem copying.
1269
@call_with(person=REQUEST_USER)
1270
1256
@operation_parameters(
1271
1257
source_name=TextLine(title=_("Source package name")),
1272
1258
version=TextLine(title=_("Version")),
1273
from_archive=Reference(schema=Interface),
1274
# Really IArchive, see below
1259
from_archive=Reference(schema=Interface), #Really IArchive, see below
1275
1260
to_pocket=TextLine(title=_("Pocket name")),
1276
1261
to_series=TextLine(title=_("Distroseries name"), required=False),
1277
1262
include_binaries=Bool(
1286
1271
# we should consider either changing this method or adding a new one
1287
1272
# that takes that object instead.
1288
1273
def syncSource(source_name, version, from_archive, to_pocket,
1289
to_series=None, include_binaries=False, person=None):
1274
to_series=None, include_binaries=False):
1290
1275
"""Synchronise (copy) a single named source into this archive.
1292
1277
Copy a specific version of a named source to the destination
1300
1285
:param include_binaries: optional boolean, controls whether or not
1301
1286
the published binaries for each given source should also be
1302
1287
copied along with the source.
1303
:param person: the `IPerson` who requests the sync.
1305
1289
:raises NoSuchSourcePackageName: if the source name is invalid
1306
1290
:raises PocketNotFound: if the pocket name is invalid
1307
:raises NoSuchDistroSeries: if the distro series name is invalid
1291
:raises DistroSeriesNotFound: if the distro series name is invalid
1308
1292
:raises CannotCopy: if there is a problem copying.
1311
1295
@call_with(registrant=REQUEST_USER)
1312
1296
@operation_parameters(
1313
subscriber=PublicPersonChoice(
1297
subscriber = PublicPersonChoice(
1314
1298
title=_("Subscriber"),
1316
1300
vocabulary='ValidPersonOrTeam',
1457
def addArchiveDependency(dependency, pocket, component=None):
1458
"""Record an archive dependency record for the context archive.
1460
:param dependency: is an `IArchive` object.
1461
:param pocket: is an `PackagePublishingPocket` enum.
1462
:param component: is an optional `IComponent` object, if not given
1463
the archive dependency will be tied to the component used
1464
for a corresponding source in primary archive.
1466
:raise: `ArchiveDependencyError` if given 'dependency' does not fit
1467
the context archive.
1468
:return: a `IArchiveDependency` object targeted to the context
1469
`IArchive` requiring 'dependency' `IArchive`.
1472
@operation_parameters(
1473
dependency=Reference(schema=Interface, required=True),
1477
description=_("The pocket into which this entry is published"),
1478
# Really PackagePublishingPocket.
1479
vocabulary=DBEnumeratedType,
1481
component=TextLine(title=_("Component"), required=False),
1483
@export_operation_as('addArchiveDependency')
1484
@export_factory_operation(Interface, []) # Really IArchiveDependency
1485
@operation_for_version('devel')
1486
def _addArchiveDependency(dependency, pocket, component=None):
1487
"""Record an archive dependency record for the context archive.
1489
:param dependency: is an `IArchive` object.
1490
:param pocket: is an `PackagePublishingPocket` enum.
1491
:param component: is the name of a component. If not given,
1492
the archive dependency will be tied to the component used
1493
for a corresponding source in primary archive.
1495
:raise: `ArchiveDependencyError` if given 'dependency' does not fit
1496
the context archive.
1497
:return: a `IArchiveDependency` object targeted to the context
1498
`IArchive` requiring 'dependency' `IArchive`.
1500
@operation_parameters(
1501
dependency=Reference(schema=Interface, required=True),
1504
@export_write_operation()
1505
@operation_for_version('devel')
1506
def removeArchiveDependency(dependency):
1507
"""Remove the `IArchiveDependency` record for the given dependency.
1509
:param dependency: is an `IArchive` object.
1513
class IArchiveCommercial(Interface):
1514
"""Archive interface for operations restricted by commercial."""
1516
@operation_parameters(
1517
family=Reference(schema=Interface, required=True),
1518
# Really IProcessorFamily.
1520
@export_write_operation()
1521
@operation_for_version('devel')
1522
def enableRestrictedFamily(family):
1523
"""Add the processor family to the set of enabled restricted families.
1525
:param family: is an `IProcessorFamily` object.
1529
class IArchive(IArchivePublic, IArchiveAppend, IArchiveEdit, IArchiveView,
1530
IArchiveCommercial):
1442
class IArchive(IArchivePublic, IArchiveAppend, IArchiveEdit, IArchiveView):
1531
1443
"""Main Archive interface."""
1532
1444
export_as_webservice_entry()
1760
1672
# Circular dependency issues fixed in _schema_circular_imports.py
1763
def validate_external_dependencies(ext_deps):
1764
"""Validate the external_dependencies field.
1766
:param ext_deps: The dependencies form field to check.
1769
# The field can consist of multiple entries separated by
1770
# newlines, so process each in turn.
1771
for dep in ext_deps.splitlines():
1773
deb, url, suite, components = dep.split(" ", 3)
1776
"'%s' is not a complete and valid sources.list entry"
1781
errors.append("%s: Must start with 'deb'" % dep)
1782
url_components = urlparse(url)
1783
if not url_components[0] or not url_components[1]:
1784
errors.append("%s: Invalid URL" % dep)