This repository has been archived by the owner on Dec 7, 2022. It is now read-only.
/
distributor.py
265 lines (234 loc) · 10 KB
/
distributor.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
# -*- coding: utf-8 -*-
from gettext import gettext as _
from logging import getLogger
import os
import warnings
from pulp.plugins.distributor import Distributor
from pulp.server.db import model
from pulp.server.config import config as pulp_conf
from pulp.server.compat import json
from pulp_node import constants
from pulp_node import pathlib
from pulp_node.conduit import NodesConduit
from pulp_node.distributors.http.publisher import HttpPublisher
from pulp_node.error import TASK_DEPRECATION_WARNING, NodeDeprecationWarning
_logger = getLogger(__name__)
PROPERTY_MISSING = _('Missing required configuration property: %(p)s')
PROPERTY_INVALID = _('Property %(p)s must be: %(v)s')
CONFIGURATION_PATH = '/etc/pulp/server/plugins.conf.d/nodes/distributor/http.conf'
def entry_point():
"""
Entry point that pulp platform uses to load the distributor.
:return: distributor class and its configuration.
:rtype: Distributor, dict
"""
with open(CONFIGURATION_PATH) as fp:
return NodesHttpDistributor, json.load(fp)
class NodesHttpDistributor(Distributor):
"""
The (nodes) distributor
"""
@classmethod
def metadata(cls):
return {
'id': constants.HTTP_DISTRIBUTOR,
'display_name': 'Pulp Nodes HTTP Distributor',
'types': ['node']
}
def validate_config(self, repo, config, config_conduit):
"""
Layout:
{
protocol : (http|https|file),
http : {
alias : [url, directory]
},
https : {
alias : [url, directory],
ssl (optional) : {
ca_cert : <path>,
client_cert : <path>
verify : <bool>
}
}
}
"""
key = constants.PROTOCOL_KEYWORD
protocol = config.get(key)
valid_protocols = ('http', 'https', 'file')
if not protocol:
return (False, PROPERTY_MISSING % {'p': key})
if protocol not in valid_protocols:
return (False, PROPERTY_INVALID % {'p': key, 'v': valid_protocols})
for key in ('http', 'https'):
section = config.get(key)
if not section:
return (False, PROPERTY_MISSING % {'p': key})
key = (key, 'alias')
alias = section.get(key[1])
if not alias:
return (False, PROPERTY_MISSING % {'p': '.'.join(key)})
return (True, None)
def publish_repo(self, repo, conduit, config):
"""
Publishes the given repository.
While this call may be implemented using multiple threads, its execution
from the Pulp server's standpoint should be synchronous. This call should
not return until the publish is complete.
It is not expected that this call be atomic. Should an error occur, it
is not the responsibility of the distributor to rollback any changes
that have been made.
:param repo: metadata describing the repository
:type repo: pulp.plugins.model.Repository
:param conduit: provides access to relevant Pulp functionality
:type conduit: pulp.plugins.conduits.repo_publish.RepoPublishConduit
:param config: plugin configuration
:type config: pulp.plugins.config.PluginConfiguration
:return: report describing the publish run
:rtype: pulp.plugins.model.PublishReport
"""
warnings.warn(TASK_DEPRECATION_WARNING, NodeDeprecationWarning)
nodes_conduit = NodesConduit()
units = nodes_conduit.get_units(repo.id)
with self.publisher(repo, config) as publisher:
publisher.publish(units)
publisher.commit()
details = dict(unit_count=len(units))
return conduit.build_success_report('succeeded', details)
def publisher(self, repo, config):
"""
Get a configured publisher.
:param repo: A repository.
:type repo: pulp.plugins.model.Repository
:param config: plugin configuration
:type config: pulp.plugins.config.PluginConfiguration
:return: The configured publisher.
"""
protocol = config.get(constants.PROTOCOL_KEYWORD)
host = pulp_conf.get('server', 'server_name')
section = config.get(protocol)
alias = section.get('alias')
base_url = '://'.join((protocol, host))
repo_publish_dir = self._get_publish_dir(repo.id, config)
return HttpPublisher(base_url, alias, repo.id, repo_publish_dir)
def cancel_publish_repo(self):
pass
def create_consumer_payload(self, repo, config, binding_config):
"""
Called when a consumer binds to a repository using this distributor.
This call should return a dictionary describing all data the consumer
will need to access the repository. The contents will vary wildly
depending on the method the repository is published, but examples
of returned data includes authentication information, location of the
repository (e.g. URL), and data required to verify the contents
of the published repository.
:param repo: metadata describing the repository
:type repo: pulp.plugins.model.Repository
:param config: plugin configuration
:type config: pulp.plugins.config.PluginCallConfiguration
:param binding_config: The configuration stored on the binding.
:type binding_config: dict
:return: dictionary of relevant data
:rtype: dict
"""
payload = {}
self._add_repository(repo.id, payload)
self._add_importers(repo, config, binding_config or {}, payload)
self._add_distributors(repo.id, payload)
return payload
def distributor_removed(self, repo, config):
"""
Called when a distributor of this type is removed from a repository.
This will delete any published node data from the filesystem.
:param repo: metadata describing the repository
:type repo: pulp.plugins.model.Repository
:param config: plugin config
:type config: pulp.plugins.config.PluginCallConfiguration
"""
warnings.warn(TASK_DEPRECATION_WARNING, NodeDeprecationWarning)
_logger.debug(_('removing published node data for repo %s' % repo.id))
repo_publish_path = self._get_publish_dir(repo.id, config)
os.system('rm -rf %s' % repo_publish_path)
def _get_publish_dir(self, repo_id, config):
"""
generate the full path where the given repo should be published
:param repo_id: unique ID for the repository
:type repo_id: str
:param config: plugin config
:type config: pulp.plugins.config.PluginCallConfiguration
:return: full path to the directory where this repo's data
should be published
:rtype: str
"""
protocol = config.get(constants.PROTOCOL_KEYWORD)
section = config.get(protocol)
url, publish_path = section.get('alias')
return os.path.join(publish_path, repo_id)
def _add_repository(self, repo_id, payload):
"""
Add repository information to the payload.
:param repo_id: The repository ID.
:type repo_id: str
:param payload: The repository payload
:type payload: dict
"""
repo_obj = model.Repository.objects.get_repo_or_missing_resource(repo_id)
# Pseudo serialize the repository object so that it can be used by a node.
payload['repository'] = {'id': repo_obj.repo_id, 'display_name': repo_obj.display_name,
'description': repo_obj.description, 'notes': repo_obj.notes,
'scratchpad': repo_obj.scratchpad}
def _add_importers(self, repo, config, binding_config, payload):
"""
Add the nodes importer.
:param repo: A repo object.
:type repo: pulp.plugins.model.Repository
:param config: plugin configuration
:type config: pulp.plugins.config.PluginCallConfiguration
:param binding_config: The configuration stored on the binding.
:type binding_config: dict
:param payload: The bind payload.
:type payload: dict
"""
conf = self._importer_conf(repo, config, binding_config)
importer = {
'id': constants.HTTP_IMPORTER,
'importer_type_id': constants.HTTP_IMPORTER,
'config': conf,
}
payload['importers'] = [importer]
def _importer_conf(self, repo, config, binding_config):
"""
Build the nodes importer configuration.
:param repo: A repo object.
:type repo: pulp.plugins.model.Repository
:param config: plugin configuration
:type config: pulp.plugins.config.PluginCallConfiguration
:param binding_config: The configuration stored on the binding.
:type binding_config: dict
:return: The importer configuration.
:rtype: dict
"""
publisher = self.publisher(repo, config)
manifest_url = pathlib.url_join(publisher.base_url, publisher.manifest_path())
strategy = binding_config.get(constants.STRATEGY_KEYWORD, constants.DEFAULT_STRATEGY)
configuration = {
constants.STRATEGY_KEYWORD: strategy,
constants.MANIFEST_URL_KEYWORD: manifest_url,
}
return configuration
def _add_distributors(self, repo_id, payload):
"""
Add repository distributors information to the payload.
:param repo_id: The repository ID.
:type repo_id: str
:param payload: The distributor(s) payload
:type payload: dict
"""
distributors = []
for dist in model.Distributor.objects(repo_id=repo_id):
if dist.distributor_type_id in constants.ALL_DISTRIBUTORS:
continue
serialized = model.Distributor.SERIALIZER(dist).data
serialized.pop('_href')
distributors.append(serialized)
payload['distributors'] = distributors