Browse code

remove non RabbitMQ messaging

Part of what was decided at summit is devstack needs to return to a
more opinionated stance, the following removes support for non
RabbitMQ messaging. RabbitMQ is used by over 95% of our community
(statistically all of it), so it's a pretty clear line to draw that
this shouldn't be in tree.

iniset_rpc_backend will be our stable hook for other projects that
want to implement this out of tree. The burden on creating those out
of tree plugins will be on those that wish to support those
alternative stacks.

Change-Id: I8073a895c03ec927a2598eff6c2f01e5c82606fc

Sean Dague authored on 2015/06/16 20:19:22
Showing 15 changed files
... ...
@@ -115,22 +115,6 @@ following in the `localrc` section:
115 115
 
116 116
 `mysql` is the default database.
117 117
 
118
-# RPC Backend
119
-
120
-Multiple RPC backends are available. Currently, this
121
-includes RabbitMQ (default), Qpid, and ZeroMQ. Your backend of
122
-choice may be selected via the `localrc` section.
123
-
124
-Note that selecting more than one RPC backend will result in a failure.
125
-
126
-Example (ZeroMQ):
127
-
128
-    ENABLED_SERVICES="$ENABLED_SERVICES,-rabbit,-qpid,zeromq"
129
-
130
-Example (Qpid):
131
-
132
-    ENABLED_SERVICES="$ENABLED_SERVICES,-rabbit,-zeromq,qpid"
133
-
134 118
 # Apache Frontend
135 119
 
136 120
 Apache web server can be enabled for wsgi services that support being deployed
... ...
@@ -88,7 +88,7 @@ Q: Is enabling a service that defaults to off done with the reverse of the above
88 88
 
89 89
     ::
90 90
 
91
-        enable_service qpid
91
+        enable_service q-svc
92 92
 
93 93
 Q: How do I run a specific OpenStack milestone?
94 94
    A: OpenStack milestones have tags set in the git repo. Set the
... ...
@@ -9,11 +9,9 @@ sudo
9 9
 postgresql-server-dev-all
10 10
 python-mysqldb
11 11
 python-mysql.connector
12
-python-qpid # NOPRIME
13 12
 dnsmasq-base
14 13
 dnsmasq-utils # for dhcp_release only available in dist:precise
15 14
 rabbitmq-server # NOPRIME
16
-qpidd # NOPRIME
17 15
 sqlite3
18 16
 vlan
19 17
 radvd # NOPRIME
... ...
@@ -24,10 +24,8 @@ vlan
24 24
 curl
25 25
 genisoimage # required for config_drive
26 26
 rabbitmq-server # NOPRIME
27
-qpidd # NOPRIME
28 27
 socat # used by ajaxterm
29 28
 python-libvirt # NOPRIME
30 29
 python-libxml2
31 30
 python-numpy # used by websockify for spice console
32 31
 python-m2crypto
33
-python-qpid # NOPRIME
34 32
deleted file mode 100644
... ...
@@ -1 +0,0 @@
1
-sasl2-bin # NOPRIME
... ...
@@ -11,6 +11,3 @@ sqlite3
11 11
 sudo
12 12
 vlan
13 13
 radvd # NOPRIME
14
-
15
-# FIXME: qpid is not part of openSUSE, those names are tentative
16
-qpidd # NOPRIME
... ...
@@ -22,7 +22,3 @@ socat
22 22
 sqlite3
23 23
 sudo
24 24
 vlan
25
-
26
-# FIXME: qpid is not part of openSUSE, those names are tentative
27
-python-qpid # NOPRIME
28
-qpidd # NOPRIME
... ...
@@ -11,7 +11,6 @@ mysql-server # NOPRIME
11 11
 openvswitch # NOPRIME
12 12
 postgresql-devel
13 13
 rabbitmq-server # NOPRIME
14
-qpid-cpp-server        # NOPRIME
15 14
 sqlite
16 15
 sudo
17 16
 radvd # NOPRIME
... ...
@@ -22,6 +22,5 @@ mysql-server # NOPRIME
22 22
 parted
23 23
 polkit
24 24
 rabbitmq-server # NOPRIME
25
-qpid-cpp-server # NOPRIME
26 25
 sqlite
27 26
 sudo
28 27
deleted file mode 100644
... ...
@@ -1,3 +0,0 @@
1
-qpid-proton-c-devel # NOPRIME
2
-cyrus-sasl-lib # NOPRIME
3
-cyrus-sasl-plain # NOPRIME
... ...
@@ -1671,7 +1671,7 @@ function disable_service {
1671 1671
 # ``ENABLED_SERVICES`` list, if they are not already present.
1672 1672
 #
1673 1673
 # For example:
1674
-#   enable_service qpid
1674
+#   enable_service q-svc
1675 1675
 #
1676 1676
 # This function does not know about the special cases
1677 1677
 # for nova, glance, and neutron built into is_service_enabled().
... ...
@@ -112,9 +112,7 @@ function configure_glance {
112 112
     iniset $GLANCE_REGISTRY_CONF DEFAULT workers "$API_WORKERS"
113 113
     iniset $GLANCE_REGISTRY_CONF paste_deploy flavor keystone
114 114
     configure_auth_token_middleware $GLANCE_REGISTRY_CONF glance $GLANCE_AUTH_CACHE_DIR/registry
115
-    if is_service_enabled qpid || [ -n "$RABBIT_HOST" ] && [ -n "$RABBIT_PASSWORD" ]; then
116
-        iniset $GLANCE_REGISTRY_CONF DEFAULT notification_driver messaging
117
-    fi
115
+    iniset $GLANCE_REGISTRY_CONF DEFAULT notification_driver messaging
118 116
     iniset_rpc_backend glance $GLANCE_REGISTRY_CONF
119 117
 
120 118
     cp $GLANCE_DIR/etc/glance-api.conf $GLANCE_API_CONF
... ...
@@ -125,9 +123,7 @@ function configure_glance {
125 125
     iniset $GLANCE_API_CONF DEFAULT image_cache_dir $GLANCE_CACHE_DIR/
126 126
     iniset $GLANCE_API_CONF paste_deploy flavor keystone+cachemanagement
127 127
     configure_auth_token_middleware $GLANCE_API_CONF glance $GLANCE_AUTH_CACHE_DIR/api
128
-    if is_service_enabled qpid || [ -n "$RABBIT_HOST" ] && [ -n "$RABBIT_PASSWORD" ]; then
129
-        iniset $GLANCE_API_CONF DEFAULT notification_driver messaging
130
-    fi
128
+    iniset $GLANCE_API_CONF DEFAULT notification_driver messaging
131 129
     iniset_rpc_backend glance $GLANCE_API_CONF
132 130
     if [ "$VIRT_DRIVER" = 'xenserver' ]; then
133 131
         iniset $GLANCE_API_CONF DEFAULT container_formats "ami,ari,aki,bare,ovf,tgz"
... ...
@@ -1,72 +1,32 @@
1 1
 #!/bin/bash
2 2
 #
3 3
 # lib/rpc_backend
4
-# Interface for interactig with different RPC backends
4
+# Interface for installing RabbitMQ on the system
5 5
 
6 6
 # Dependencies:
7 7
 #
8 8
 # - ``functions`` file
9 9
 # - ``RABBIT_{HOST|PASSWORD|USERID}`` must be defined when RabbitMQ is used
10
-# - ``RPC_MESSAGING_PROTOCOL`` option for configuring the messaging protocol
11 10
 
12 11
 # ``stack.sh`` calls the entry points in this order:
13 12
 #
14 13
 # - check_rpc_backend
15 14
 # - install_rpc_backend
16 15
 # - restart_rpc_backend
17
-# - iniset_rpc_backend
16
+# - iniset_rpc_backend (stable interface)
17
+#
18
+# Note: if implementing an out of tree plugin for an RPC backend, you
19
+# should install all services through normal plugin methods, then
20
+# redefine ``iniset_rpc_backend`` in your code. That's the one portion
21
+# of this file which is a standard interface.
18 22
 
19 23
 # Save trace setting
20 24
 XTRACE=$(set +o | grep xtrace)
21 25
 set +o xtrace
22 26
 
23
-RPC_MESSAGING_PROTOCOL=${RPC_MESSAGING_PROTOCOL:-0.9}
24
-
25
-# TODO(sdague): RPC backend selection is super wonky because we treat
26
-# messaging server as a service, which it really isn't for multi host
27
-QPID_HOST=${QPID_HOST:-}
28
-
29
-
30 27
 # Functions
31 28
 # ---------
32 29
 
33
-# Make sure we only have one rpc backend enabled.
34
-# Also check the specified rpc backend is available on your platform.
35
-function check_rpc_backend {
36
-    local c svc
37
-
38
-    local rpc_needed=1
39
-    # We rely on the fact that filenames in lib/* match the service names
40
-    # that can be passed as arguments to is_service_enabled.
41
-    # We check for a call to iniset_rpc_backend in these files, meaning
42
-    # the service needs a backend.
43
-    rpc_candidates=$(grep -rl iniset_rpc_backend $TOP_DIR/lib/ | awk -F/ '{print $NF}')
44
-    for c in ${rpc_candidates}; do
45
-        if is_service_enabled $c; then
46
-            rpc_needed=0
47
-            break
48
-        fi
49
-    done
50
-    local rpc_backend_cnt=0
51
-    for svc in qpid zeromq rabbit; do
52
-        is_service_enabled $svc &&
53
-        (( rpc_backend_cnt++ )) || true
54
-    done
55
-    if [ "$rpc_backend_cnt" -gt 1 ]; then
56
-        echo "ERROR: only one rpc backend may be enabled,"
57
-        echo "       set only one of 'rabbit', 'qpid', 'zeromq'"
58
-        echo "       via ENABLED_SERVICES."
59
-    elif [ "$rpc_backend_cnt" == 0 ] && [ "$rpc_needed" == 0 ]; then
60
-        echo "ERROR: at least one rpc backend must be enabled,"
61
-        echo "       set one of 'rabbit', 'qpid', 'zeromq'"
62
-        echo "       via ENABLED_SERVICES."
63
-    fi
64
-
65
-    if is_service_enabled qpid && ! qpid_is_supported; then
66
-        die $LINENO "Qpid support is not available for this version of your distribution."
67
-    fi
68
-}
69
-
70 30
 # clean up after rpc backend - eradicate all traces so changing backends
71 31
 # produces a clean switch
72 32
 function cleanup_rpc_backend {
... ...
@@ -79,110 +39,14 @@ function cleanup_rpc_backend {
79 79
             # And the Erlang runtime too
80 80
             apt_get purge -y erlang*
81 81
         fi
82
-    elif is_service_enabled qpid; then
83
-        if is_fedora; then
84
-            uninstall_package qpid-cpp-server
85
-        elif is_ubuntu; then
86
-            uninstall_package qpidd
87
-        else
88
-            exit_distro_not_supported "qpid installation"
89
-        fi
90
-    elif is_service_enabled zeromq; then
91
-        if is_fedora; then
92
-            uninstall_package zeromq python-zmq
93
-            if [ "$ZEROMQ_MATCHMAKER" == "redis" ]; then
94
-                uninstall_package redis python-redis
95
-            fi
96
-        elif is_ubuntu; then
97
-            uninstall_package libzmq1 python-zmq
98
-            if [ "$ZEROMQ_MATCHMAKER" == "redis" ]; then
99
-                uninstall_package redis-server python-redis
100
-            fi
101
-        elif is_suse; then
102
-            uninstall_package libzmq1 python-pyzmq
103
-            if [ "$ZEROMQ_MATCHMAKER" == "redis" ]; then
104
-                uninstall_package redis python-redis
105
-            fi
106
-        else
107
-            exit_distro_not_supported "zeromq installation"
108
-        fi
109
-    fi
110
-
111
-    # Remove the AMQP 1.0 messaging libraries
112
-    if [ "$RPC_MESSAGING_PROTOCOL" == "AMQP1" ]; then
113
-        if is_fedora; then
114
-            uninstall_package qpid-proton-c-devel
115
-            uninstall_package python-qpid-proton
116
-        fi
117
-        # TODO(kgiusti) ubuntu cleanup
118 82
     fi
119 83
 }
120 84
 
121 85
 # install rpc backend
122 86
 function install_rpc_backend {
123
-    # Regardless of the broker used, if AMQP 1.0 is configured load
124
-    # the necessary messaging client libraries for oslo.messaging
125
-    if [ "$RPC_MESSAGING_PROTOCOL" == "AMQP1" ]; then
126
-        if is_fedora; then
127
-            install_package qpid-proton-c-devel
128
-            install_package python-qpid-proton
129
-        elif is_ubuntu; then
130
-            # TODO(kgiusti) The QPID AMQP 1.0 protocol libraries
131
-            # are not yet in the ubuntu repos. Enable these installs
132
-            # once they are present:
133
-            #install_package libqpid-proton2-dev
134
-            #install_package python-qpid-proton
135
-            # Also add 'uninstall' directives in cleanup_rpc_backend()!
136
-            exit_distro_not_supported "QPID AMQP 1.0 Proton libraries"
137
-        else
138
-            exit_distro_not_supported "QPID AMQP 1.0 Proton libraries"
139
-        fi
140
-        # Install pyngus client API
141
-        # TODO(kgiusti) can remove once python qpid bindings are
142
-        # available on all supported platforms _and_ pyngus is added
143
-        # to the requirements.txt file in oslo.messaging
144
-        pip_install_gr pyngus
145
-    fi
146
-
147 87
     if is_service_enabled rabbit; then
148 88
         # Install rabbitmq-server
149 89
         install_package rabbitmq-server
150
-    elif is_service_enabled qpid; then
151
-        if is_fedora; then
152
-            install_package qpid-cpp-server
153
-        elif is_ubuntu; then
154
-            install_package qpidd
155
-        else
156
-            exit_distro_not_supported "qpid installation"
157
-        fi
158
-        _configure_qpid
159
-    elif is_service_enabled zeromq; then
160
-        if is_fedora; then
161
-            install_package zeromq python-zmq
162
-            if [ "$ZEROMQ_MATCHMAKER" == "redis" ]; then
163
-                install_package redis python-redis
164
-            fi
165
-        elif is_ubuntu; then
166
-            install_package libzmq1 python-zmq
167
-            if [ "$ZEROMQ_MATCHMAKER" == "redis" ]; then
168
-                install_package redis-server python-redis
169
-            fi
170
-        elif is_suse; then
171
-            install_package libzmq1 python-pyzmq
172
-            if [ "$ZEROMQ_MATCHMAKER" == "redis" ]; then
173
-                install_package redis python-redis
174
-            fi
175
-        else
176
-            exit_distro_not_supported "zeromq installation"
177
-        fi
178
-        # Necessary directory for socket location.
179
-        sudo mkdir -p /var/run/openstack
180
-        sudo chown $STACK_USER /var/run/openstack
181
-    fi
182
-
183
-    # If using the QPID broker, install the QPID python client API
184
-    if is_service_enabled qpid || [ -n "$QPID_HOST" ]; then
185
-        install_package python-qpid
186 90
     fi
187 91
 }
188 92
 
... ...
@@ -232,17 +96,12 @@ function restart_rpc_backend {
232 232
                 sudo rabbitmqctl set_permissions -p child_cell $RABBIT_USERID ".*" ".*" ".*"
233 233
             fi
234 234
         fi
235
-    elif is_service_enabled qpid; then
236
-        echo_summary "Starting qpid"
237
-        restart_service qpidd
238 235
     fi
239 236
 }
240 237
 
241 238
 # builds transport url string
242 239
 function get_transport_url {
243
-    if is_service_enabled qpid || [ -n "$QPID_HOST" ]; then
244
-        echo "qpid://$QPID_USERNAME:$QPID_PASSWORD@$QPID_HOST:5672/"
245
-    elif is_service_enabled rabbit || { [ -n "$RABBIT_HOST" ] && [ -n "$RABBIT_PASSWORD" ]; }; then
240
+    if is_service_enabled rabbit || { [ -n "$RABBIT_HOST" ] && [ -n "$RABBIT_PASSWORD" ]; }; then
246 241
         echo "rabbit://$RABBIT_USERID:$RABBIT_PASSWORD@$RABBIT_HOST:5672/"
247 242
     fi
248 243
 }
... ...
@@ -252,29 +111,7 @@ function iniset_rpc_backend {
252 252
     local package=$1
253 253
     local file=$2
254 254
     local section=${3:-DEFAULT}
255
-    if is_service_enabled zeromq; then
256
-        iniset $file $section rpc_backend "zmq"
257
-        iniset $file $section rpc_zmq_host `hostname`
258
-        if [ "$ZEROMQ_MATCHMAKER" == "redis" ]; then
259
-            iniset $file $section rpc_zmq_matchmaker "redis"
260
-            MATCHMAKER_REDIS_HOST=${MATCHMAKER_REDIS_HOST:-127.0.0.1}
261
-            iniset $file matchmaker_redis host $MATCHMAKER_REDIS_HOST
262
-        else
263
-            die $LINENO "Other matchmaker drivers not supported"
264
-        fi
265
-    elif is_service_enabled qpid || [ -n "$QPID_HOST" ]; then
266
-        # For Qpid use the 'amqp' oslo.messaging transport when AMQP 1.0 is used
267
-        if [ "$RPC_MESSAGING_PROTOCOL" == "AMQP1" ]; then
268
-            iniset $file $section rpc_backend "amqp"
269
-        else
270
-            iniset $file $section rpc_backend "qpid"
271
-        fi
272
-        iniset $file $section qpid_hostname ${QPID_HOST:-$SERVICE_HOST}
273
-        if [ -n "$QPID_USERNAME" ]; then
274
-            iniset $file $section qpid_username $QPID_USERNAME
275
-            iniset $file $section qpid_password $QPID_PASSWORD
276
-        fi
277
-    elif is_service_enabled rabbit || { [ -n "$RABBIT_HOST" ] && [ -n "$RABBIT_PASSWORD" ]; }; then
255
+    if is_service_enabled rabbit || { [ -n "$RABBIT_HOST" ] && [ -n "$RABBIT_PASSWORD" ]; }; then
278 256
         iniset $file $section rpc_backend "rabbit"
279 257
         iniset $file oslo_messaging_rabbit rabbit_hosts $RABBIT_HOST
280 258
         iniset $file oslo_messaging_rabbit rabbit_password $RABBIT_PASSWORD
... ...
@@ -288,17 +125,6 @@ function iniset_rpc_backend {
288 288
     fi
289 289
 }
290 290
 
291
-# Check if qpid can be used on the current distro.
292
-# qpid_is_supported
293
-function qpid_is_supported {
294
-    if [[ -z "$DISTRO" ]]; then
295
-        GetDistro
296
-    fi
297
-
298
-    # Qpid is not in openSUSE
299
-    ( ! is_suse )
300
-}
301
-
302 291
 function rabbit_setuser {
303 292
     local user="$1" pass="$2" found="" out=""
304 293
     out=$(sudo rabbitmqctl list_users) ||
... ...
@@ -314,85 +140,6 @@ function rabbit_setuser {
314 314
     sudo rabbitmqctl set_permissions "$user" ".*" ".*" ".*"
315 315
 }
316 316
 
317
-# Set up the various configuration files used by the qpidd broker
318
-function _configure_qpid {
319
-
320
-    # the location of the configuration files have changed since qpidd 0.14
321
-    local qpid_conf_file
322
-    if [ -e /etc/qpid/qpidd.conf ]; then
323
-        qpid_conf_file=/etc/qpid/qpidd.conf
324
-    elif [ -e /etc/qpidd.conf ]; then
325
-        qpid_conf_file=/etc/qpidd.conf
326
-    else
327
-        exit_distro_not_supported "qpidd.conf file not found!"
328
-    fi
329
-
330
-    # force the ACL file to a known location
331
-    local qpid_acl_file=/etc/qpid/qpidd.acl
332
-    if [ ! -e $qpid_acl_file ]; then
333
-        sudo mkdir -p -m 755 `dirname $qpid_acl_file`
334
-        sudo touch $qpid_acl_file
335
-        sudo chmod o+r $qpid_acl_file
336
-    fi
337
-    sudo sed -i.bak '/^acl-file=/d' $qpid_conf_file
338
-    echo "acl-file=$qpid_acl_file" | sudo tee --append $qpid_conf_file
339
-
340
-    sudo sed -i '/^auth=/d' $qpid_conf_file
341
-    if [ -z "$QPID_USERNAME" ]; then
342
-        # no QPID user configured, so disable authentication
343
-        # and access control
344
-        echo "auth=no" | sudo tee --append $qpid_conf_file
345
-        cat <<EOF | sudo tee $qpid_acl_file
346
-acl allow all all
347
-EOF
348
-    else
349
-        # Configure qpidd to use PLAIN authentication, and add
350
-        # QPID_USERNAME to the ACL:
351
-        echo "auth=yes" | sudo tee --append $qpid_conf_file
352
-        if [ -z "$QPID_PASSWORD" ]; then
353
-            read_password QPID_PASSWORD "ENTER A PASSWORD FOR QPID USER $QPID_USERNAME"
354
-        fi
355
-        # Create ACL to allow $QPID_USERNAME full access
356
-        cat <<EOF | sudo tee $qpid_acl_file
357
-group admin ${QPID_USERNAME}@QPID
358
-acl allow admin all
359
-acl deny all all
360
-EOF
361
-        # Add user to SASL database
362
-        if is_ubuntu; then
363
-            install_package sasl2-bin
364
-        elif is_fedora; then
365
-            install_package cyrus-sasl-lib
366
-            install_package cyrus-sasl-plain
367
-        fi
368
-        local sasl_conf_file=/etc/sasl2/qpidd.conf
369
-        sudo sed -i.bak '/PLAIN/!s/mech_list: /mech_list: PLAIN /' $sasl_conf_file
370
-        local sasl_db=`sudo grep sasldb_path $sasl_conf_file | cut -f 2 -d ":" | tr -d [:blank:]`
371
-        if [ ! -e $sasl_db ]; then
372
-            sudo mkdir -p -m 755 `dirname $sasl_db`
373
-        fi
374
-        echo $QPID_PASSWORD | sudo saslpasswd2 -c -p -f $sasl_db -u QPID $QPID_USERNAME
375
-        sudo chmod o+r $sasl_db
376
-    fi
377
-
378
-    # If AMQP 1.0 is specified, ensure that the version of the
379
-    # broker can support AMQP 1.0 and configure the queue and
380
-    # topic address patterns used by oslo.messaging.
381
-    if [ "$RPC_MESSAGING_PROTOCOL" == "AMQP1" ]; then
382
-        QPIDD=$(type -p qpidd)
383
-        if ! $QPIDD --help | grep -q "queue-patterns"; then
384
-            exit_distro_not_supported "qpidd with AMQP 1.0 support"
385
-        fi
386
-        if ! grep -q "queue-patterns=exclusive" $qpid_conf_file; then
387
-            cat <<EOF | sudo tee --append $qpid_conf_file
388
-queue-patterns=exclusive
389
-queue-patterns=unicast
390
-topic-patterns=broadcast
391
-EOF
392
-        fi
393
-    fi
394
-}
395
-
396 317
 # Restore xtrace
397 318
 $XTRACE
398 319
 
... ...
@@ -128,10 +128,9 @@ function configure_zaqar {
128 128
         configure_redis
129 129
     fi
130 130
 
131
-    if is_service_enabled qpid || [ -n "$RABBIT_HOST" ] && [ -n "$RABBIT_PASSWORD" ]; then
132
-        iniset $ZAQAR_CONF DEFAULT notification_driver messaging
133
-        iniset $ZAQAR_CONF DEFAULT control_exchange zaqar
134
-    fi
131
+    iniset $ZAQAR_CONF DEFAULT notification_driver messaging
132
+    iniset $ZAQAR_CONF DEFAULT control_exchange zaqar
133
+
135 134
     iniset_rpc_backend zaqar $ZAQAR_CONF
136 135
 
137 136
     cleanup_zaqar
... ...
@@ -500,10 +500,6 @@ rm -f $SSL_BUNDLE_FILE
500 500
 source $TOP_DIR/lib/database
501 501
 source $TOP_DIR/lib/rpc_backend
502 502
 
503
-# Make sure we only have one rpc backend enabled,
504
-# and the specified rpc backend is available on your platform.
505
-check_rpc_backend
506
-
507 503
 # Service to enable with SSL if ``USE_SSL`` is True
508 504
 SSL_ENABLED_SERVICES="key,nova,cinder,glance,s-proxy,neutron,sahara"
509 505
 
... ...
@@ -1014,15 +1010,6 @@ if is_service_enabled keystone; then
1014 1014
     export OS_REGION_NAME=$REGION_NAME
1015 1015
 fi
1016 1016
 
1017
-
1018
-# ZeroMQ
1019
-# ------
1020
-if is_service_enabled zeromq; then
1021
-    echo_summary "Starting zeromq receiver"
1022
-    run_process zeromq "$OSLO_BIN_DIR/oslo-messaging-zmq-receiver"
1023
-fi
1024
-
1025
-
1026 1017
 # Horizon
1027 1018
 # -------
1028 1019