Skip to content

Commit c4408c4

Browse files
committed
Adds support for systemd amphora images
This patch enables auto-detection of the init system used in the amphora image and adds support for systemd amphora. This patch allows Ubuntu xenial amphora images to work. It also merges two functional test files into one file to reduce code duplication. This is a scenario gate fix. Change-Id: I5fec1680bd47719ae9f2fcb6abaaba8a78e2ae8b Closes-Bug: #1640866
1 parent 5e76bab commit c4408c4

File tree

25 files changed

+630
-1256
lines changed

25 files changed

+630
-1256
lines changed
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
[Unit]
2+
Description=OpenStack Octavia Amphora Agent
3+
After=network.target syslog.service
4+
Wants=syslog.service
5+
6+
[Service]
7+
ExecStart=/usr/local/bin/amphora-agent --config-file /etc/octavia/amphora-agent.conf
8+
KillMode=mixed
9+
Restart=always
10+
11+
[Install]
12+
WantedBy=multi-user.target
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#!/bin/bash
2+
3+
if [ ${DIB_DEBUG_TRACE:-0} -gt 0 ]; then
4+
set -x
5+
fi
6+
set -eu
7+
set -o pipefail
8+
9+
if [[ -f /bin/systemctl ]]; then
10+
/bin/systemctl enable amphora-agent
11+
fi
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
os-svc-install
22
package-installs
3+
pkg-map
34
sysctl
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
{
2-
"haproxy/trusty-backports": null
2+
"haproxy": null
33
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
"release": {
3+
"ubuntu": {
4+
"trusty": {
5+
"haproxy": "haproxy/trusty-backports"
6+
}
7+
}
8+
},
9+
"distro": {
10+
"ubuntu": {
11+
"haproxy": "haproxy"
12+
}
13+
},
14+
"family": {
15+
"debian": {
16+
"haproxy": "haproxy"
17+
}
18+
},
19+
"default": {
20+
"haproxy": "haproxy"
21+
}
22+
}

elements/haproxy-octavia-ubuntu/post-install.d/20-disable-default-haproxy

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,6 @@
33
set -eu
44
set -o pipefail
55

6-
update-rc.d -f haproxy remove
6+
# Doing both here as just remove doesn't seem to work on xenial
7+
update-rc.d haproxy disable || true
8+
update-rc.d -f haproxy remove || true

elements/haproxy-octavia-ubuntu/pre-install.d/01-backports

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,6 @@
44
set -eu
55
set -o xtrace
66

7-
echo deb http://archive.ubuntu.com/ubuntu trusty-backports main restricted universe multiverse > /etc/apt/sources.list.d/backports.list
7+
if [ "$DISTRO_NAME" == "ubuntu" ] && [ "$DIB_RELEASE" == "trusty" ]; then
8+
echo deb http://archive.ubuntu.com/ubuntu trusty-backports main restricted universe multiverse > /etc/apt/sources.list.d/backports.list
9+
fi
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
os-svc-install
22
package-installs
3+
pkg-map
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
{
2-
"keepalived/trusty-backports": null
2+
"keepalived": null
33
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{
2+
"release": {
3+
"ubuntu": {
4+
"trusty": {
5+
"keepalived": "keepalived/trusty-backports"
6+
}
7+
}
8+
},
9+
"distro": {
10+
"ubuntu": {
11+
"keepalived": "keepalived"
12+
}
13+
},
14+
"family": {
15+
"debian": {
16+
"keepalived": "keepalived"
17+
}
18+
},
19+
"default": {
20+
"keepalived": "keepalived"
21+
}
22+
23+
}

elements/keepalived-octavia-ubuntu/pre-install.d/00-backports

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,6 @@
44
set -eu
55
set -o xtrace
66

7-
echo deb http://archive.ubuntu.com/ubuntu trusty-backports main restricted universe multiverse > /etc/apt/sources.list.d/backports.list
7+
if [ "$DISTRO_NAME" == "ubuntu" ] && [ "$DIB_RELEASE" == "trusty" ]; then
8+
echo deb http://archive.ubuntu.com/ubuntu trusty-backports main restricted universe multiverse > /etc/apt/sources.list.d/backports.list
9+
fi

etc/octavia.conf

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,10 @@
124124
# respawn_interval = 2
125125
# client_cert = /etc/octavia/certs/client.pem
126126
# server_ca = /etc/octavia/certs/server_ca.pem
127+
#
128+
# This setting is deprecated. It is now automatically discovered.
127129
# use_upstart = True
130+
#
128131
# rest_request_conn_timeout = 10
129132
# rest_request_read_timeout = 60
130133

octavia/amphorae/backends/agent/api_server/keepalived.py

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,9 @@
3131

3232
j2_env = jinja2.Environment(autoescape=True, loader=jinja2.FileSystemLoader(
3333
os.path.dirname(os.path.realpath(__file__)) + consts.AGENT_API_TEMPLATES))
34-
template = j2_env.get_template(consts.KEEPALIVED_CONF)
34+
UPSTART_TEMPLATE = j2_env.get_template(consts.KEEPALIVED_JINJA2_UPSTART)
35+
SYSVINIT_TEMPLATE = j2_env.get_template(consts.KEEPALIVED_JINJA2_SYSVINIT)
36+
SYSTEMD_TEMPLATE = j2_env.get_template(consts.KEEPALIVED_JINJA2_SYSTEMD)
3537
check_script_template = j2_env.get_template(consts.CHECK_SCRIPT_CONF)
3638

3739

@@ -54,10 +56,28 @@ def upload_keepalived_config(self):
5456
f.write(b)
5557
b = stream.read(BUFFER)
5658

57-
file_path = util.keepalived_init_path()
58-
# mode 00755
59-
mode = (stat.S_IRWXU | stat.S_IRGRP | stat.S_IXGRP |
60-
stat.S_IROTH | stat.S_IXOTH)
59+
init_system = util.get_os_init_system()
60+
61+
file_path = util.keepalived_init_path(init_system)
62+
63+
if init_system == consts.INIT_SYSTEMD:
64+
template = SYSTEMD_TEMPLATE
65+
init_enable_cmd = "systemctl enable octavia-keepalived"
66+
elif init_system == consts.INIT_UPSTART:
67+
template = UPSTART_TEMPLATE
68+
elif init_system == consts.INIT_SYSVINIT:
69+
template = SYSVINIT_TEMPLATE
70+
init_enable_cmd = "insserv {file}".format(file=file_path)
71+
else:
72+
raise util.UnknownInitError()
73+
74+
if init_system == consts.INIT_SYSTEMD:
75+
# mode 00644
76+
mode = stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IROTH
77+
else:
78+
# mode 00755
79+
mode = (stat.S_IRWXU | stat.S_IRGRP | stat.S_IXGRP |
80+
stat.S_IROTH | stat.S_IXOTH)
6181
if not os.path.exists(file_path):
6282
with os.fdopen(os.open(file_path, flags, mode), 'w') as text_file:
6383
text = template.render(
@@ -71,13 +91,28 @@ def upload_keepalived_config(self):
7191

7292
# Renders the Keepalived check script
7393
keepalived_path = util.keepalived_check_script_path()
94+
# mode 00755
95+
mode = (stat.S_IRWXU | stat.S_IRGRP | stat.S_IXGRP |
96+
stat.S_IROTH | stat.S_IXOTH)
7497
open_obj = os.open(keepalived_path, flags, mode)
7598
with os.fdopen(open_obj, 'w') as text_file:
7699
text = check_script_template.render(
77100
check_scripts_dir=util.keepalived_check_scripts_dir()
78101
)
79102
text_file.write(text)
80103

104+
# Make sure the new service is enabled on boot
105+
if init_system != consts.INIT_UPSTART:
106+
try:
107+
subprocess.check_output(init_enable_cmd.split(),
108+
stderr=subprocess.STDOUT)
109+
except subprocess.CalledProcessError as e:
110+
LOG.debug("Failed to enable octavia-keepalived service: "
111+
"%(err)s", {'err': e})
112+
return flask.make_response(flask.jsonify(dict(
113+
message="Error enabling octavia-keepalived service",
114+
details=e.output)), 500)
115+
81116
res = flask.make_response(flask.jsonify({
82117
'message': 'OK'}), 200)
83118
res.headers['ETag'] = stream.get_md5()

octavia/amphorae/backends/agent/api_server/listener.py

Lines changed: 77 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,14 @@
3030
from octavia.amphorae.backends.utils import haproxy_query as query
3131
from octavia.common import constants as consts
3232
from octavia.common import utils as octavia_utils
33+
from octavia.i18n import _LE
3334

3435
LOG = logging.getLogger(__name__)
3536
BUFFER = 100
3637

3738
UPSTART_CONF = 'upstart.conf.j2'
3839
SYSVINIT_CONF = 'sysvinit.conf.j2'
40+
SYSTEMD_CONF = 'systemd.conf.j2'
3941

4042
JINJA_ENV = jinja2.Environment(
4143
autoescape=True,
@@ -44,6 +46,7 @@
4446
) + consts.AGENT_API_TEMPLATES))
4547
UPSTART_TEMPLATE = JINJA_ENV.get_template(UPSTART_CONF)
4648
SYSVINIT_TEMPLATE = JINJA_ENV.get_template(SYSVINIT_CONF)
49+
SYSTEMD_TEMPLATE = JINJA_ENV.get_template(SYSTEMD_CONF)
4750

4851

4952
class ParsingError(Exception):
@@ -113,7 +116,7 @@ def upload_haproxy_config(self, amphora_id, listener_id):
113116
try:
114117
subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT)
115118
except subprocess.CalledProcessError as e:
116-
LOG.debug("Failed to verify haproxy file: %s", e)
119+
LOG.error(_LE("Failed to verify haproxy file: %s"), e)
117120
os.remove(name) # delete file
118121
return flask.make_response(flask.jsonify(dict(
119122
message="Invalid request",
@@ -122,15 +125,44 @@ def upload_haproxy_config(self, amphora_id, listener_id):
122125
# file ok - move it
123126
os.rename(name, util.config_path(listener_id))
124127

125-
use_upstart = util.CONF.haproxy_amphora.use_upstart
126-
file = util.init_path(listener_id)
127-
# mode 00755
128-
mode = (stat.S_IRWXU | stat.S_IRGRP | stat.S_IXGRP |
129-
stat.S_IROTH | stat.S_IXOTH)
130-
if not os.path.exists(file):
131-
with os.fdopen(os.open(file, flags, mode), 'w') as text_file:
132-
template = (UPSTART_TEMPLATE if use_upstart
133-
else SYSVINIT_TEMPLATE)
128+
try:
129+
130+
init_system = util.get_os_init_system()
131+
132+
LOG.debug('Found init system: {0}'.format(init_system))
133+
134+
init_path = util.init_path(listener_id, init_system)
135+
136+
if init_system == consts.INIT_SYSTEMD:
137+
template = SYSTEMD_TEMPLATE
138+
init_enable_cmd = "systemctl enable haproxy-{list}".format(
139+
list=listener_id)
140+
elif init_system == consts.INIT_UPSTART:
141+
template = UPSTART_TEMPLATE
142+
elif init_system == consts.INIT_SYSVINIT:
143+
template = SYSVINIT_TEMPLATE
144+
init_enable_cmd = "insserv {file}".format(file=init_path)
145+
else:
146+
raise util.UnknownInitError()
147+
148+
except util.UnknownInitError:
149+
LOG.error(_LE("Unknown init system found."))
150+
return flask.make_response(flask.jsonify(dict(
151+
message="Unknown init system in amphora",
152+
details="The amphora image is running an unknown init "
153+
"system. We can't create the init configuration "
154+
"file for the load balancing process.")), 500)
155+
156+
if init_system == consts.INIT_SYSTEMD:
157+
# mode 00644
158+
mode = (stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IROTH)
159+
else:
160+
# mode 00755
161+
mode = (stat.S_IRWXU | stat.S_IRGRP | stat.S_IXGRP |
162+
stat.S_IROTH | stat.S_IXOTH)
163+
if not os.path.exists(init_path):
164+
with os.fdopen(os.open(init_path, flags, mode), 'w') as text_file:
165+
134166
text = template.render(
135167
peer_name=peer_name,
136168
haproxy_pid=util.pid_path(listener_id),
@@ -143,18 +175,18 @@ def upload_haproxy_config(self, amphora_id, listener_id):
143175
)
144176
text_file.write(text)
145177

146-
if not use_upstart:
147-
insrvcmd = ("insserv {file}".format(file=file))
148-
178+
# Make sure the new service is enabled on boot
179+
if init_system != consts.INIT_UPSTART:
149180
try:
150-
subprocess.check_output(insrvcmd.split(),
181+
subprocess.check_output(init_enable_cmd.split(),
151182
stderr=subprocess.STDOUT)
152183
except subprocess.CalledProcessError as e:
153-
LOG.debug("Failed to make %(file)s executable: %(err)s",
154-
{'file': file, 'err': e})
184+
LOG.error(_LE("Failed to enable haproxy-%(list)s "
185+
"service: %(err)s"),
186+
{'list': listener_id, 'err': e})
155187
return flask.make_response(flask.jsonify(dict(
156-
message="Error making file {0} executable".format(file),
157-
details=e.output)), 500)
188+
message="Error enabling haproxy-{0} service".format(
189+
listener_id), details=e.output)), 500)
158190

159191
res = flask.make_response(flask.jsonify({
160192
'message': 'OK'}), 202)
@@ -221,7 +253,7 @@ def delete_listener(self, listener_id):
221253
try:
222254
subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT)
223255
except subprocess.CalledProcessError as e:
224-
LOG.debug("Failed to stop HAProxy service: %s", e)
256+
LOG.error(_LE("Failed to stop HAProxy service: %s"), e)
225257
return flask.make_response(flask.jsonify(dict(
226258
message="Error stopping haproxy",
227259
details=e.output)), 500)
@@ -239,10 +271,34 @@ def delete_listener(self, listener_id):
239271
except Exception:
240272
pass
241273

274+
# disable the service
275+
init_system = util.get_os_init_system()
276+
init_path = util.init_path(listener_id, init_system)
277+
278+
if init_system == consts.INIT_SYSTEMD:
279+
init_disable_cmd = "systemctl disable haproxy-{list}".format(
280+
list=listener_id)
281+
elif init_system == consts.INIT_SYSVINIT:
282+
init_disable_cmd = "insserv -r {file}".format(file=init_path)
283+
elif init_system != consts.INIT_UPSTART:
284+
raise util.UnknownInitError()
285+
286+
if init_system != consts.INIT_UPSTART:
287+
try:
288+
subprocess.check_output(init_disable_cmd.split(),
289+
stderr=subprocess.STDOUT)
290+
except subprocess.CalledProcessError as e:
291+
LOG.error(_LE("Failed to disable haproxy-%(list)s "
292+
"service: %(err)s"),
293+
{'list': listener_id, 'err': e})
294+
return flask.make_response(flask.jsonify(dict(
295+
message="Error disabling haproxy-{0} service".format(
296+
listener_id), details=e.output)), 500)
297+
242298
# delete the directory + init script for that listener
243299
shutil.rmtree(util.haproxy_dir(listener_id))
244-
if os.path.exists(util.init_path(listener_id)):
245-
os.remove(util.init_path(listener_id))
300+
if os.path.exists(init_path):
301+
os.remove(init_path)
246302

247303
return flask.jsonify({'message': 'OK'})
248304

0 commit comments

Comments
 (0)