|
...
|
...
|
@@ -38,7 +38,7 @@ NOVNC_DIR=$DEST/noVNC
|
|
38
|
38
|
MUNIN_DIR=$DEST/openstack-munin
|
|
39
|
39
|
|
|
40
|
40
|
# Specify which services to launch. These generally correspond to screen tabs
|
|
41
|
|
-ENABLED_SERVICES=${ENABLED_SERVICES:-g-api,g-reg,key,n-api,n-cpu,n-net,n-sch,n-vnc,dash}
|
|
|
41
|
+ENABLED_SERVICES=${ENABLED_SERVICES:-g-api,g-reg,key,n-api,n-cpu,n-net,n-sch,n-vnc,dash,mysql,rabbit}
|
|
42
|
42
|
|
|
43
|
43
|
# Use the first IP unless an explicit is set by ``HOST_IP`` environment variable
|
|
44
|
44
|
if [ ! -n "$HOST_IP" ]; then
|
|
...
|
...
|
@@ -130,17 +130,6 @@ cd $API_DIR; sudo python setup.py develop
|
|
130
|
130
|
cd $DASH_DIR/django-openstack; sudo python setup.py develop
|
|
131
|
131
|
cd $DASH_DIR/openstack-dashboard; sudo python setup.py develop
|
|
132
|
132
|
|
|
133
|
|
-# attempt to load modules: kvm (hardware virt) and nbd (network block
|
|
134
|
|
-# device - used to manage qcow images)
|
|
135
|
|
-sudo modprobe nbd || true
|
|
136
|
|
-sudo modprobe kvm || true
|
|
137
|
|
-# user needs to be member of libvirtd group for nova-compute to use libvirt
|
|
138
|
|
-sudo usermod -a -G libvirtd `whoami`
|
|
139
|
|
-# if kvm wasn't running before we need to restart libvirt to enable it
|
|
140
|
|
-sudo /etc/init.d/libvirt-bin restart
|
|
141
|
|
-
|
|
142
|
|
-## FIXME(ja): should LIBVIRT_TYPE be kvm if kvm module is loaded?
|
|
143
|
|
-
|
|
144
|
133
|
# add useful screenrc
|
|
145
|
134
|
cp $DIR/files/screenrc ~/.screenrc
|
|
146
|
135
|
|
|
...
|
...
|
@@ -155,76 +144,89 @@ cp $DIR/files/screenrc ~/.screenrc
|
|
155
|
155
|
# Dash currently imports quantum even if you aren't using it. Instead
|
|
156
|
156
|
# of installing quantum we can create a simple module that will pass the
|
|
157
|
157
|
# initial imports
|
|
158
|
|
-sudo mkdir -p $DASH_DIR/openstack-dashboard/quantum || true
|
|
159
|
|
-sudo touch $DASH_DIR/openstack-dashboard/quantum/__init__.py
|
|
160
|
|
-sudo touch $DASH_DIR/openstack-dashboard/quantum/client.py
|
|
161
|
|
-
|
|
162
|
|
-cd $DASH_DIR/openstack-dashboard
|
|
163
|
|
-sudo cp local/local_settings.py.example local/local_settings.py
|
|
164
|
|
-dashboard/manage.py syncdb
|
|
165
|
|
-
|
|
166
|
|
-# create an empty directory that apache uses as docroot
|
|
167
|
|
-sudo mkdir -p $DASH_DIR/.blackhole
|
|
|
158
|
+if [[ "$ENABLED_SERVICES" =~ "dash" ]]; then
|
|
|
159
|
+ sudo mkdir -p $DASH_DIR/openstack-dashboard/quantum || true
|
|
|
160
|
+ sudo touch $DASH_DIR/openstack-dashboard/quantum/__init__.py
|
|
|
161
|
+ sudo touch $DASH_DIR/openstack-dashboard/quantum/client.py
|
|
|
162
|
+
|
|
|
163
|
+ cd $DASH_DIR/openstack-dashboard
|
|
|
164
|
+ sudo cp local/local_settings.py.example local/local_settings.py
|
|
|
165
|
+ dashboard/manage.py syncdb
|
|
|
166
|
+
|
|
|
167
|
+ # create an empty directory that apache uses as docroot
|
|
|
168
|
+ sudo mkdir -p $DASH_DIR/.blackhole
|
|
|
169
|
+
|
|
|
170
|
+ ## Configure apache's 000-default to run dashboard
|
|
|
171
|
+ sudo cp $DIR/files/000-default.template /etc/apache2/sites-enabled/000-default
|
|
|
172
|
+ sudo sed -e "s,%DASH_DIR%,$DASH_DIR,g" -i /etc/apache2/sites-enabled/000-default
|
|
|
173
|
+
|
|
|
174
|
+ # ``python setup.py develop`` left some files owned by root in ``DASH_DIR`` and
|
|
|
175
|
+ # others by the original owner. We need to change the owner to apache so
|
|
|
176
|
+ # dashboard can run
|
|
|
177
|
+ sudo chown -R www-data:www-data $DASH_DIR
|
|
|
178
|
+fi
|
|
168
|
179
|
|
|
169
|
|
-## Configure apache's 000-default to run dashboard
|
|
170
|
|
-sudo cp $DIR/files/000-default.template /etc/apache2/sites-enabled/000-default
|
|
171
|
|
-sudo sed -e "s,%DASH_DIR%,$DASH_DIR,g" -i /etc/apache2/sites-enabled/000-default
|
|
172
|
180
|
|
|
173
|
|
-# ``python setup.py develop`` left some files owned by root in ``DASH_DIR`` and
|
|
174
|
|
-# others by the original owner. We need to change the owner to apache so
|
|
175
|
|
-# dashboard can run
|
|
176
|
|
-sudo chown -R www-data:www-data $DASH_DIR
|
|
|
181
|
+# Mysql
|
|
|
182
|
+# ---------
|
|
|
183
|
+#
|
|
|
184
|
+if [[ "$ENABLED_SERVICES" =~ "mysql" ]]; then
|
|
|
185
|
+ # Update the DB to give user ‘$MYSQL_USER’@’%’ full control of the all databases:
|
|
|
186
|
+ sudo mysql -uroot -p$MYSQL_PASS -e "GRANT ALL PRIVILEGES ON *.* TO '$MYSQL_USER'@'%' identified by '$MYSQL_PASS';"
|
|
177
|
187
|
|
|
178
|
|
-# Update the DB to give user ‘$MYSQL_USER’@’%’ full control of the all databases:
|
|
179
|
|
-sudo mysql -uroot -p$MYSQL_PASS -e "GRANT ALL PRIVILEGES ON *.* TO '$MYSQL_USER'@'%' identified by '$MYSQL_PASS';"
|
|
|
188
|
+ # Edit /etc/mysql/my.cnf to change ‘bind-address’ from localhost (127.0.0.1) to any (0.0.0.0) and restart the mysql service:
|
|
|
189
|
+ sudo sed -i 's/127.0.0.1/0.0.0.0/g' /etc/mysql/my.cnf
|
|
|
190
|
+ sudo service mysql restart
|
|
|
191
|
+fi
|
|
180
|
192
|
|
|
181
|
|
-# Edit /etc/mysql/my.cnf to change ‘bind-address’ from localhost (127.0.0.1) to any (0.0.0.0) and restart the mysql service:
|
|
182
|
|
-sudo sed -i 's/127.0.0.1/0.0.0.0/g' /etc/mysql/my.cnf
|
|
183
|
|
-sudo service mysql restart
|
|
184
|
193
|
|
|
185
|
194
|
# Munin
|
|
186
|
195
|
# -----
|
|
187
|
196
|
|
|
188
|
|
-# allow connections from other hosts
|
|
189
|
|
-sudo sed -i -e '/Allow from localhost/s/localhost.*$/all/' /etc/munin/apache.conf
|
|
190
|
197
|
|
|
191
|
|
-cat >/tmp/nova <<EOF
|
|
|
198
|
+if [[ "$ENABLED_SERVICES" =~ "munin" ]]; then
|
|
|
199
|
+ # allow connections from other hosts
|
|
|
200
|
+ sudo sed -i -e '/Allow from localhost/s/localhost.*$/all/' /etc/munin/apache.conf
|
|
|
201
|
+
|
|
|
202
|
+ cat >/tmp/nova <<EOF
|
|
192
|
203
|
[keystone_*]
|
|
193
|
204
|
user `whoami`
|
|
194
|
205
|
|
|
195
|
206
|
[nova_*]
|
|
196
|
207
|
user `whoami`
|
|
197
|
208
|
EOF
|
|
198
|
|
-sudo mv /tmp/nova /etc/munin/plugin-conf.d/nova
|
|
199
|
|
-
|
|
200
|
|
-# configure Munin for Nova plugins
|
|
201
|
|
-PLUGINS="keystone_stats nova_floating_ips nova_instance_launched nova_instance_ nova_instance_timing nova_services"
|
|
202
|
|
-for i in $PLUGINS; do
|
|
203
|
|
- sudo cp -p $MUNIN_DIR/$i /usr/share/munin/plugins
|
|
204
|
|
- sudo ln -sf /usr/share/munin/plugins/$i /etc/munin/plugins
|
|
205
|
|
-done
|
|
206
|
|
-sudo mv /etc/munin/plugins/nova_instance_ /etc/munin/plugins/nova_instance_launched
|
|
207
|
|
-sudo restart munin-node
|
|
|
209
|
+ sudo mv /tmp/nova /etc/munin/plugin-conf.d/nova
|
|
|
210
|
+ # configure Munin for Nova plugins
|
|
|
211
|
+ PLUGINS="keystone_stats nova_floating_ips nova_instance_launched nova_instance_ nova_instance_timing nova_services"
|
|
|
212
|
+ for i in $PLUGINS; do
|
|
|
213
|
+ sudo cp -p $MUNIN_DIR/$i /usr/share/munin/plugins
|
|
|
214
|
+ sudo ln -sf /usr/share/munin/plugins/$i /etc/munin/plugins
|
|
|
215
|
+ done
|
|
|
216
|
+ sudo mv /etc/munin/plugins/nova_instance_ /etc/munin/plugins/nova_instance_launched
|
|
|
217
|
+ sudo restart munin-node
|
|
|
218
|
+fi
|
|
208
|
219
|
|
|
209
|
220
|
# Glance
|
|
210
|
221
|
# ------
|
|
211
|
222
|
|
|
212
|
|
-# Glance uses ``/var/lib/glance`` and ``/var/log/glance`` by default, so
|
|
213
|
|
-# we need to insure that our user has permissions to use them.
|
|
214
|
|
-sudo mkdir -p /var/log/glance
|
|
215
|
|
-sudo chown -R `whoami` /var/log/glance
|
|
216
|
|
-sudo mkdir -p /var/lib/glance
|
|
217
|
|
-sudo chown -R `whoami` /var/lib/glance
|
|
218
|
|
-
|
|
219
|
|
-# Delete existing images/database as glance will recreate the db on startup
|
|
220
|
|
-rm -rf /var/lib/glance/images/*
|
|
221
|
|
-# (re)create glance database
|
|
222
|
|
-mysql -u$MYSQL_USER -p$MYSQL_PASS -e 'DROP DATABASE glance;' || true
|
|
223
|
|
-mysql -u$MYSQL_USER -p$MYSQL_PASS -e 'CREATE DATABASE glance;'
|
|
224
|
|
-# Copy over our glance-registry.conf
|
|
225
|
|
-GLANCE_CONF=$GLANCE_DIR/etc/glance-registry.conf
|
|
226
|
|
-cp $DIR/files/glance-registry.conf $GLANCE_CONF
|
|
227
|
|
-sudo sed -e "s,%SQL_CONN%,$BASE_SQL_CONN/glance,g" -i $GLANCE_CONF
|
|
|
223
|
+if [[ "$ENABLED_SERVICES" =~ "g-reg" ]]; then
|
|
|
224
|
+ # Glance uses ``/var/lib/glance`` and ``/var/log/glance`` by default, so
|
|
|
225
|
+ # we need to insure that our user has permissions to use them.
|
|
|
226
|
+ sudo mkdir -p /var/log/glance
|
|
|
227
|
+ sudo chown -R `whoami` /var/log/glance
|
|
|
228
|
+ sudo mkdir -p /var/lib/glance
|
|
|
229
|
+ sudo chown -R `whoami` /var/lib/glance
|
|
|
230
|
+
|
|
|
231
|
+ # Delete existing images/database as glance will recreate the db on startup
|
|
|
232
|
+ rm -rf /var/lib/glance/images/*
|
|
|
233
|
+ # (re)create glance database
|
|
|
234
|
+ mysql -u$MYSQL_USER -p$MYSQL_PASS -e 'DROP DATABASE glance;' || true
|
|
|
235
|
+ mysql -u$MYSQL_USER -p$MYSQL_PASS -e 'CREATE DATABASE glance;'
|
|
|
236
|
+ # Copy over our glance-registry.conf
|
|
|
237
|
+ GLANCE_CONF=$GLANCE_DIR/etc/glance-registry.conf
|
|
|
238
|
+ cp $DIR/files/glance-registry.conf $GLANCE_CONF
|
|
|
239
|
+ sudo sed -e "s,%SQL_CONN%,$BASE_SQL_CONN/glance,g" -i $GLANCE_CONF
|
|
|
240
|
+fi
|
|
228
|
241
|
|
|
229
|
242
|
# Nova
|
|
230
|
243
|
# ----
|
|
...
|
...
|
@@ -260,50 +262,70 @@ fi
|
|
260
|
260
|
screen -d -m -S nova -t nova
|
|
261
|
261
|
sleep 1
|
|
262
|
262
|
|
|
263
|
|
-# setup nova instance directory
|
|
264
|
|
-mkdir -p $NOVA_DIR/instances
|
|
|
263
|
+if [[ "$ENABLED_SERVICES" =~ "n-cpu" ]]; then
|
|
265
|
264
|
|
|
266
|
|
-# if there is a partition labeled nova-instances use it (ext filesystems
|
|
267
|
|
-# can be labeled via e2label)
|
|
268
|
|
-## FIXME: if already mounted this blows up...
|
|
269
|
|
-if [ -L /dev/disk/by-label/nova-instances ]; then
|
|
270
|
|
- sudo mount -L nova-instances $NOVA_DIR/instances
|
|
271
|
|
- sudo chown -R `whoami` $NOVA_DIR/instances
|
|
272
|
|
-fi
|
|
|
265
|
+ # attempt to load modules: kvm (hardware virt) and nbd (network block
|
|
|
266
|
+ # device - used to manage qcow images)
|
|
|
267
|
+ sudo modprobe nbd || true
|
|
|
268
|
+ sudo modprobe kvm || true
|
|
|
269
|
+ # user needs to be member of libvirtd group for nova-compute to use libvirt
|
|
|
270
|
+ sudo usermod -a -G libvirtd `whoami`
|
|
|
271
|
+ # if kvm wasn't running before we need to restart libvirt to enable it
|
|
|
272
|
+ sudo /etc/init.d/libvirt-bin restart
|
|
273
|
273
|
|
|
274
|
|
-# Clean out the instances directory
|
|
275
|
|
-rm -rf $NOVA_DIR/instances/*
|
|
|
274
|
+ ## FIXME(ja): should LIBVIRT_TYPE be kvm if kvm module is loaded?
|
|
276
|
275
|
|
|
277
|
|
-# delete traces of nova networks from prior runs
|
|
278
|
|
-killall dnsmasq || true
|
|
279
|
|
-rm -rf $NOVA_DIR/networks
|
|
280
|
|
-mkdir -p $NOVA_DIR/networks
|
|
|
276
|
+ # setup nova instance directory
|
|
|
277
|
+ mkdir -p $NOVA_DIR/instances
|
|
281
|
278
|
|
|
282
|
|
-# (re)create nova database
|
|
283
|
|
-mysql -u$MYSQL_USER -p$MYSQL_PASS -e 'DROP DATABASE nova;' || true
|
|
284
|
|
-mysql -u$MYSQL_USER -p$MYSQL_PASS -e 'CREATE DATABASE nova;'
|
|
285
|
|
-$NOVA_DIR/bin/nova-manage db sync
|
|
|
279
|
+ # if there is a partition labeled nova-instances use it (ext filesystems
|
|
|
280
|
+ # can be labeled via e2label)
|
|
|
281
|
+ ## FIXME: if already mounted this blows up...
|
|
|
282
|
+ if [ -L /dev/disk/by-label/nova-instances ]; then
|
|
|
283
|
+ sudo mount -L nova-instances $NOVA_DIR/instances
|
|
|
284
|
+ sudo chown -R `whoami` $NOVA_DIR/instances
|
|
|
285
|
+ fi
|
|
|
286
|
+
|
|
|
287
|
+ # Clean out the instances directory
|
|
|
288
|
+ rm -rf $NOVA_DIR/instances/*
|
|
|
289
|
+fi
|
|
|
290
|
+
|
|
|
291
|
+if [[ "$ENABLED_SERVICES" =~ "n-net" ]]; then
|
|
|
292
|
+ # delete traces of nova networks from prior runs
|
|
|
293
|
+ killall dnsmasq || true
|
|
|
294
|
+ rm -rf $NOVA_DIR/networks
|
|
|
295
|
+ mkdir -p $NOVA_DIR/networks
|
|
|
296
|
+fi
|
|
|
297
|
+
|
|
|
298
|
+if [[ "$ENABLED_SERVICES" =~ "mysql" ]]; then
|
|
|
299
|
+ # (re)create nova database
|
|
|
300
|
+ mysql -u$MYSQL_USER -p$MYSQL_PASS -e 'DROP DATABASE nova;' || true
|
|
|
301
|
+ mysql -u$MYSQL_USER -p$MYSQL_PASS -e 'CREATE DATABASE nova;'
|
|
|
302
|
+ $NOVA_DIR/bin/nova-manage db sync
|
|
286
|
303
|
|
|
287
|
|
-# create a small network
|
|
288
|
|
-$NOVA_DIR/bin/nova-manage network create private $FIXED_RANGE 1 32
|
|
|
304
|
+ # create a small network
|
|
|
305
|
+ $NOVA_DIR/bin/nova-manage network create private $FIXED_RANGE 1 32
|
|
289
|
306
|
|
|
290
|
|
-# create some floating ips
|
|
291
|
|
-$NOVA_DIR/bin/nova-manage floating create $FLOATING_RANGE
|
|
|
307
|
+ # create some floating ips
|
|
|
308
|
+ $NOVA_DIR/bin/nova-manage floating create $FLOATING_RANGE
|
|
|
309
|
+fi
|
|
292
|
310
|
|
|
293
|
311
|
# Keystone
|
|
294
|
312
|
# --------
|
|
295
|
313
|
|
|
296
|
|
-# (re)create keystone database
|
|
297
|
|
-mysql -u$MYSQL_USER -p$MYSQL_PASS -e 'DROP DATABASE keystone;' || true
|
|
298
|
|
-mysql -u$MYSQL_USER -p$MYSQL_PASS -e 'CREATE DATABASE keystone;'
|
|
|
314
|
+if [[ "$ENABLED_SERVICES" =~ "key" ]]; then
|
|
|
315
|
+ # (re)create keystone database
|
|
|
316
|
+ mysql -u$MYSQL_USER -p$MYSQL_PASS -e 'DROP DATABASE keystone;' || true
|
|
|
317
|
+ mysql -u$MYSQL_USER -p$MYSQL_PASS -e 'CREATE DATABASE keystone;'
|
|
299
|
318
|
|
|
300
|
|
-# FIXME (anthony) keystone should use keystone.conf.example
|
|
301
|
|
-KEYSTONE_CONF=$KEYSTONE_DIR/etc/keystone.conf
|
|
302
|
|
-cp $DIR/files/keystone.conf $KEYSTONE_CONF
|
|
303
|
|
-sudo sed -e "s,%SQL_CONN%,$BASE_SQL_CONN/keystone,g" -i $KEYSTONE_CONF
|
|
|
319
|
+ # FIXME (anthony) keystone should use keystone.conf.example
|
|
|
320
|
+ KEYSTONE_CONF=$KEYSTONE_DIR/etc/keystone.conf
|
|
|
321
|
+ cp $DIR/files/keystone.conf $KEYSTONE_CONF
|
|
|
322
|
+ sudo sed -e "s,%SQL_CONN%,$BASE_SQL_CONN/keystone,g" -i $KEYSTONE_CONF
|
|
304
|
323
|
|
|
305
|
|
-# initialize keystone with default users/endpoints
|
|
306
|
|
-BIN_DIR=$KEYSTONE_DIR/bin bash $DIR/files/keystone_data.sh
|
|
|
324
|
+ # initialize keystone with default users/endpoints
|
|
|
325
|
+ BIN_DIR=$KEYSTONE_DIR/bin bash $DIR/files/keystone_data.sh
|
|
|
326
|
+fi
|
|
307
|
327
|
|
|
308
|
328
|
|
|
309
|
329
|
# Launch Services
|
|
...
|
...
|
@@ -324,9 +346,7 @@ function screen_it {
|
|
324
|
324
|
|
|
325
|
325
|
screen_it g-api "cd $GLANCE_DIR; bin/glance-api --config-file=etc/glance-api.conf"
|
|
326
|
326
|
screen_it g-reg "cd $GLANCE_DIR; bin/glance-registry --config-file=etc/glance-registry.conf"
|
|
327
|
|
-# keystone drops a keystone.log where if it is run, so change the path to
|
|
328
|
|
-# where it can write
|
|
329
|
|
-screen_it key "cd /tmp; $KEYSTONE_DIR/bin/keystone --config-file $KEYSTONE_CONF"
|
|
|
327
|
+screen_it key "$KEYSTONE_DIR/bin/keystone --config-file $KEYSTONE_CONF"
|
|
330
|
328
|
screen_it n-api "$NOVA_DIR/bin/nova-api"
|
|
331
|
329
|
screen_it n-cpu "$NOVA_DIR/bin/nova-compute"
|
|
332
|
330
|
screen_it n-net "$NOVA_DIR/bin/nova-network"
|
|
...
|
...
|
@@ -338,20 +358,21 @@ screen_it dash "sudo /etc/init.d/apache2 restart; sudo tail -f /var/log/apache2/
|
|
338
|
338
|
# Install Images
|
|
339
|
339
|
# ==============
|
|
340
|
340
|
|
|
341
|
|
-# Downloads a tty image (ami/aki/ari style), then extracts it. Upon extraction
|
|
342
|
|
-# we upload to glance with the glance cli tool.
|
|
343
|
|
-
|
|
344
|
|
-mkdir -p $DEST/images
|
|
345
|
|
-cd $DEST/images
|
|
346
|
|
-if [ ! -f $DEST/tty.tgz ]; then
|
|
347
|
|
- wget -c http://images.ansolabs.com/tty.tgz -O $DEST/tty.tgz
|
|
348
|
|
-fi
|
|
|
341
|
+if [[ "$ENABLED_SERVICES" =~ "g-reg" ]]; then
|
|
|
342
|
+ # Downloads a tty image (ami/aki/ari style), then extracts it. Upon extraction
|
|
|
343
|
+ # we upload to glance with the glance cli tool.
|
|
|
344
|
+ mkdir -p $DEST/images
|
|
|
345
|
+ cd $DEST/images
|
|
|
346
|
+ if [ ! -f $DEST/tty.tgz ]; then
|
|
|
347
|
+ wget -c http://images.ansolabs.com/tty.tgz -O $DEST/tty.tgz
|
|
|
348
|
+ fi
|
|
349
|
349
|
|
|
350
|
|
-# extract ami-tty/image, aki-tty/image & ari-tty/image
|
|
351
|
|
-tar -zxf $DEST/tty.tgz
|
|
|
350
|
+ # extract ami-tty/image, aki-tty/image & ari-tty/image
|
|
|
351
|
+ tar -zxf $DEST/tty.tgz
|
|
352
|
352
|
|
|
353
|
|
-# add images to glance
|
|
354
|
|
-# FIXME: kernel/ramdisk is hardcoded - use return result from add
|
|
355
|
|
-glance add name="tty-kernel" is_public=true container_format=aki disk_format=aki < aki-tty/image
|
|
356
|
|
-glance add name="tty-ramdisk" is_public=true container_format=ari disk_format=ari < ari-tty/image
|
|
357
|
|
-glance add name="tty" is_public=true container_format=ami disk_format=ami kernel_id=1 ramdisk_id=2 < ami-tty/image
|
|
|
353
|
+ # add images to glance
|
|
|
354
|
+ # FIXME: kernel/ramdisk is hardcoded - use return result from add
|
|
|
355
|
+ glance add name="tty-kernel" is_public=true container_format=aki disk_format=aki < aki-tty/image
|
|
|
356
|
+ glance add name="tty-ramdisk" is_public=true container_format=ari disk_format=ari < ari-tty/image
|
|
|
357
|
+ glance add name="tty" is_public=true container_format=ami disk_format=ami kernel_id=1 ramdisk_id=2 < ami-tty/image
|
|
|
358
|
+fi
|