39
44
from twisted.web.resource import Resource
40
45
from twisted.web.server import Site
41
47
from zope.interface import implements
50
class ConfigOops(Schema):
51
"""Configuration validator for OOPS options."""
55
directory = String(if_missing=b"")
56
reporter = String(if_missing=b"")
58
chained_validators = (
59
RequireIfPresent("reporter", present="directory"),
63
class ConfigBroker(Schema):
64
"""Configuration validator for message broker options."""
68
host = String(if_missing=b"localhost")
69
port = Int(min=1, max=65535, if_missing=5673)
70
username = String(if_missing=None)
71
password = String(if_missing=None)
72
vhost = String(if_missing="/")
76
"""Configuration validator."""
80
port = Int(min=1, max=65535, if_missing=8001)
81
logfile = String(not_empty=True)
86
def parse(cls, stream):
87
"""Load a YAML configuration from `stream` and validate."""
88
return cls().to_python(yaml.load(stream))
91
def load(cls, filename):
92
"""Load a YAML configuration from `filename` and validate."""
93
with open(filename, "rb") as stream:
94
return cls.parse(stream)
44
97
class Options(usage.Options):
45
98
"""Command line options for the provisioning server."""
48
["port", None, 8001, "Port to serve on."],
49
["logfile", "l", "pserv.log", "Logfile name."],
50
["oops-dir", "r", None, "Where to write OOPS reports"],
51
["oops-reporter", "o", "MAAS-PS", "String identifying this service."],
54
# Move these back into optParameters when RabbitMQ is a required component
55
# of a running MaaS installation.
56
optParameters_FOR_RABBIT = [
57
["brokerport", "p", 5672, "Broker port"],
58
["brokerhost", "h", '127.0.0.1', "Broker host"],
59
["brokeruser", "u", None, "Broker user"],
60
["brokerpassword", "a", None, "Broker password"],
61
["brokervhost", "v", '/', "Broker vhost"],
64
def postOptions(self):
65
for int_arg in ('port',):
67
self[int_arg] = int(self[int_arg])
68
except (TypeError, ValueError):
69
raise usage.UsageError("--%s must be an integer." % int_arg)
70
if not self["oops-reporter"] and self["oops-dir"]:
71
raise usage.UsageError(
72
"A reporter must be supplied to identify reports "
73
"from this service from other OOPS reports.")
101
["config-file", "c", "pserv.yaml", "Configuration file to load."],
76
105
class ProvisioningServiceMaker(object):
84
113
self.tapname = name
85
114
self.description = description
87
def makeService(self, options, _set_proc_title=True):
88
"""Construct a service.
90
:param _set_proc_title: For testing; if `False` this will stop the
91
obfuscation of command-line parameters in the process title.
93
# Required to hide the command line options that include a password.
94
# There is a small window where it can be seen though, between
95
# invocation and when this code runs. TODO: Make this optional (so
96
# that we don't override process title in tests).
98
setproctitle.setproctitle("maas provisioning service")
116
def makeService(self, options):
117
"""Construct a service."""
100
118
services = MultiService()
102
log_service = LogService(options["logfile"])
120
config_file = options["config-file"]
121
if config_file is None:
122
config = Config.parse(b"")
124
config = Config.load(config_file)
126
log_service = LogService(config["logfile"])
103
127
log_service.setServiceParent(services)
105
oops_dir = options["oops-dir"]
106
oops_reporter = options["oops-reporter"]
129
oops_config = config["oops"]
130
oops_dir = oops_config["directory"]
131
oops_reporter = oops_config["reporter"]
107
132
oops_service = OOPSService(log_service, oops_dir, oops_reporter)
108
133
oops_service.setServiceParent(services)
110
broker_port = options.get("brokerport")
111
broker_host = options.get("brokerhost")
112
broker_user = options.get("brokeruser")
113
broker_password = options.get("brokerpassword")
114
broker_vhost = options.get("brokervhost")
135
broker_config = config["broker"]
136
broker_port = broker_config["port"]
137
broker_host = broker_config["host"]
138
broker_username = broker_config["username"]
139
broker_password = broker_config["password"]
140
broker_vhost = broker_config["vhost"]
116
142
# Connecting to RabbitMQ is optional; it is not yet a required
117
143
# component of a running MaaS installation.
118
if broker_user is not None and broker_password is not None:
144
if broker_username is not None and broker_password is not None:
119
145
cb_connected = lambda ignored: None # TODO
120
146
cb_disconnected = lambda ignored: None # TODO
121
147
cb_failed = lambda (connector, reason): (
122
148
log.err(reason, "Connection failed"))
123
149
client_factory = AMQFactory(
124
broker_user, broker_password, broker_vhost,
150
broker_username, broker_password, broker_vhost,
125
151
cb_connected, cb_disconnected, cb_failed)
126
152
client_service = TCPClient(
127
153
broker_host, broker_port, client_factory)