Import plugin's code

Change-Id: Ic93c9f6f322d443ec60f8e39231b71b967267e3a
This commit is contained in:
Ivan Zinoviev 2017-07-10 15:56:30 +03:00
parent 1fafa482b5
commit 87d4536613
39 changed files with 1705 additions and 0 deletions

176
LICENSE Normal file
View File

@ -0,0 +1,176 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.

54
README.md Normal file
View File

@ -0,0 +1,54 @@
DNS Update Plugin for Fuel
=======================
Overview
--------------
DNS Update plugin for Fuel extends Mirantis OpenStack functionality by adding
support for configurable dns servers in murano virtual machines via notifications.
Compatible Fuel versions
--------------
9.0
User Guide
-------------
1. Create an environment.
2. Enable the plugin on the Settings/Other tab of the Fuel web UI and fill in form
fields:
3. Deploy the environment.
DNS Update plugin installation
---------------------------
To install DNS Update plugin, follow these steps:
1. Download the plugin from
[Fuel Plugins Catalog](https://software.mirantis.com/fuel-plugins)
2. Copy the plugin on already installed Fuel Master node; ssh can be used for
that. If you do not have the Fuel Master node yet, see
[Quick Start Guide](https://software.mirantis.com/quick-start/):
# scp dns-update-1.0-1.0.0-0.noarch.rpm root@<Fuel_master_ip>:/tmp
3. Log into the Fuel Master node. Install the plugin:
# cd /tmp
# fuel plugins --install fuel-plugin-dns-update-1.0-1.0.0-0.noarch.rpm
4. Check if the plugin was installed successfully:
# fuel plugins
id | name | version | package_version
---|------------------------|---------|----------------
1 | fuel-plugin-dns-update | 1.0.1 | 4.0.0
Requirements
------------
| Requirement | Version/Comment |
|:---------------------------------|:----------------|
| Mirantis OpenStack compatibility | 9.0 |

View File

@ -0,0 +1,2 @@
include dns_update::controller
notice('MODULAR: fuel-plugin-dns-update/controller.pp')

View File

@ -0,0 +1,2 @@
include dns_update::mysql
notice('MODULAR: fuel-plugin-dns-update/mysql.pp')

View File

@ -0,0 +1,2 @@
include dns_update::neutron
notice('MODULAR: fuel-plugin-dns-update/neutron.pp')

View File

@ -0,0 +1,2 @@
include dns_update::nova
notice('MODULAR: fuel-plugin-dns-update/nova.pp')

View File

@ -0,0 +1,2 @@
include dns_update::pacemaker
notice('MODULAR: fuel-plugin-dns-update/pacemaker.pp')

View File

@ -0,0 +1,2 @@
include dns_update::service
notice('MODULAR: fuel-plugin-dns-update/service.pp')

View File

@ -0,0 +1,10 @@
Metadata-Version: 1.0
Name: os_dns_updater
Version: 0.0.2
Summary: listen to nova RabbitMQ events and update dns records
Home-page: UNKNOWN
Author: Sberbank Technology
Author-email: UNKNOWN
License: UNKNOWN
Description: service to register virtual machines in dns
Platform: UNKNOWN

View File

@ -0,0 +1 @@
# openstack-dns-updater

View File

@ -0,0 +1,41 @@
#!/usr/bin/env python
#define db schema
from sqlalchemy import MetaData, create_engine
from sqlalchemy import Table, Column, ForeignKey
from sqlalchemy import String, Integer, DateTime, Text
from os_dns_updater.utils.cfg import DNS_CONF
dbcred = DNS_CONF["db_user"]+":"+DNS_CONF["db_password"]
dbaddr = "mysql://"+dbcred+"@localhost/"+DNS_CONF["db_name"]
dbengine = create_engine(dbaddr)
dbmeta = MetaData()
#create empty db manually or fuel plugin
def init_tables():
db_instance = Table("instance", dbmeta,
Column("id", Integer, primary_key=True),
Column("ip", String(20)),
Column("name", String(90)),
Column("uuid", String(40)),
Column("state", String(90)),
Column("dns_domain", String(90)),
Column("created_at", DateTime),
Column("updated_at", DateTime),
Column("deleted_at", DateTime)
)
db_event = Table("event", dbmeta,
#will be used for synchronization
Column("id", Integer, primary_key=True),
Column("fk_instance_id", Integer, ForeignKey("instance.id")),
Column("type", String(24)),#enum
Column("description", String(90)),
Column("date", DateTime)
)
dbmeta.drop_all(dbengine)
dbmeta.create_all(dbengine)
init_tables()

View File

@ -0,0 +1,164 @@
#!/bin/sh
#
#
# dnsupdater-failover OCF RA. Does nothing but wait a few seconds, can be
# configured to fail occassionally.
#
# Copyright (c) 2004 SUSE LINUX AG, Lars Marowsky-Bree
# All Rights Reserved.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of version 2 of the GNU General Public License as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it would be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
#
# Further, this software is distributed without any warranty that it is
# free of the rightful claim of any third person regarding infringement
# or the like. Any license provided herein, whether implied or
# otherwise, applies only to this software file. Patent licenses, if
# any, provided herein do not apply to combinations of this program with
# other software, or any other product whatsoever.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write the Free Software Foundation,
# Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
#
#######################################################################
# Initialization:
: ${OCF_FUNCTIONS_DIR=${OCF_ROOT}/lib/heartbeat}
. ${OCF_FUNCTIONS_DIR}/ocf-shellfuncs
#######################################################################
meta_data() {
cat <<END
<?xml version="1.0"?>
<!DOCTYPE resource-agent SYSTEM "ra-api-1.dtd">
<resource-agent name="dnsupdater-failover" version="0.9">
<version>1.0</version>
<longdesc lang="en">
Failover ocf script for sbrf dns-updater
</longdesc>
<shortdesc lang="en">ocf for dnsupdater</shortdesc>
<parameters>
<parameter name="state" unique="1">
<longdesc lang="en">
Location to store the resource state in.
</longdesc>
<shortdesc lang="en">State file</shortdesc>
<content type="string" default="${HA_RSCTMP}/dnsupdater-failover-${OCF_RESOURCE_INSTANCE}.state" />
</parameter>
<parameter name="fake" unique="0">
<longdesc lang="en">
Fake attribute that can be changed to cause a reload
</longdesc>
<shortdesc lang="en">Fake attribute that can be changed to cause a reload</shortdesc>
<content type="string" default="dnsupdater-failover" />
</parameter>
</parameters>
<actions>
<action name="start" timeout="20" />
<action name="stop" timeout="20" />
<action name="monitor" timeout="20" interval="10" depth="0" />
<action name="reload" timeout="20" />
<action name="migrate_to" timeout="20" />
<action name="migrate_from" timeout="20" />
<action name="meta-data" timeout="5" />
<action name="validate-all" timeout="20" />
</actions>
</resource-agent>
END
}
#######################################################################
dnsupdaterfailover_usage() {
cat <<END
usage: $0 {start|stop|monitor|migrate_to|migrate_from|validate-all|meta-data}
Expects to have a fully populated OCF RA-compliant environment set.
END
}
dnsupdaterfailover_start() {
dnsupdater-failover_monitor
/usr/bin/python /usr/lib/python2.7/dist-packages/os_dns_updater/openstack-dns-updater.py
if [ $? = $OCF_SUCCESS ]; then
return $OCF_SUCCESS
fi
touch ${OCF_RESKEY_state}
}
dnsupdaterfailover_stop() {
dnsupdater-failover_monitor
if [ $? = $OCF_SUCCESS ]; then
rm ${OCF_RESKEY_state}
fi
return $OCF_SUCCESS
}
dnsupdaterfailover_monitor() {
# Monitor _MUST!_ differentiate correctly between running
# (SUCCESS), failed (ERROR) or _cleanly_ stopped (NOT RUNNING).
# That is THREE states, not just yes/no.
if [ -f ${OCF_RESKEY_state} ]; then
return $OCF_SUCCESS
fi
if false ; then
return $OCF_ERR_GENERIC
fi
if ! ocf_is_probe && [ "$__OCF_ACTION" = "monitor" ]; then
# set exit string only when NOT_RUNNING occurs during an actual monitor operation.
ocf_exit_reason "No process state file found"
fi
return $OCF_NOT_RUNNING
}
dnsupdaterfailover_validate() {
# Is the state directory writable?
state_dir=`dirname "$OCF_RESKEY_state"`
touch "$state_dir/$$"
if [ $? != 0 ]; then
ocf_exit_reason "State file \"$OCF_RESKEY_state\" is not writable"
return $OCF_ERR_ARGS
fi
rm "$state_dir/$$"
return $OCF_SUCCESS
}
: ${OCF_RESKEY_state=${HA_RSCTMP}/dnsupdater-failover-${OCF_RESOURCE_INSTANCE}.state}
: ${OCF_RESKEY_fake="dnsupdaterfailover"}
case $__OCF_ACTION in
meta-data) meta_data
exit $OCF_SUCCESS
;;
start) dnsupdaterfailover_start;;
stop) dnsupdaterfailover_stop;;
monitor) dnsupdaterfailover_monitor;;
migrate_to) ocf_log info "Migrating ${OCF_RESOURCE_INSTANCE} to ${OCF_RESKEY_CRM_meta_migrate_target}."
dnsupdaterfailover_stop
;;
migrate_from) ocf_log info "Migrating ${OCF_RESOURCE_INSTANCE} from ${OCF_RESKEY_CRM_meta_migrate_source}."
dnsupdaterfailover_start
;;
reload) ocf_log info "Reloading ${OCF_RESOURCE_INSTANCE} ..."
;;
validate-all) dnsupdaterfailover_validate;;
usage|help) dnsupdaterfailover_usage
exit $OCF_SUCCESS
;;
*) dnsupdaterfailover_usage
exit $OCF_ERR_UNIMPLEMENTED
;;
esac
rc=$?
ocf_log debug "${OCF_RESOURCE_INSTANCE} $__OCF_ACTION : $rc"
exit $rc

View File

@ -0,0 +1,29 @@
[DEFAULT]
debug=False
#networks=
#region=
## AMQP configuration parameters
#exchanges=nova,neutron
#routing_key=notifications.info
#queue_name=dns_updater-test
#amqp_hosts=
#failover_strategy=round-robin
#amqp_user=nova
#amqp_password=
#event_create=compute.instance.create.end
#event_delete=compute.instance.delete.start
## DNS configuration parameters
#nameserver=127.0.0.1
#domain=vm.example.net
#ttl=1
#dns_keyfile='/etc/os_dns_updater/example.key'
##db created at setup
#db_name=
#db_user=
#db_password=
#maxcounter=4095
#insttime=120

View File

@ -0,0 +1,224 @@
#!/bin/sh
#
#
# DnsUpdater OCF RA. Does nothing but wait a few seconds, can be
# configured to fail occassionally.
#
# Copyright (c) 2004 SUSE LINUX AG, Lars Marowsky-Brée
# All Rights Reserved.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of version 2 of the GNU General Public License as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it would be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
#
# Further, this software is distributed without any warranty that it is
# free of the rightful claim of any third person regarding infringement
# or the like. Any license provided herein, whether implied or
# otherwise, applies only to this software file. Patent licenses, if
# any, provided herein do not apply to combinations of this program with
# other software, or any other product whatsoever.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write the Free Software Foundation,
# Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
#
#######################################################################
# Initialization:
: ${OCF_FUNCTIONS_DIR=${OCF_ROOT}/lib/heartbeat}
. ${OCF_FUNCTIONS_DIR}/ocf-shellfuncs
: ${OCF_FUEL_FUNCTIONS_DIR=${OCF_ROOT}/resource.d/fuel}
. ${OCF_FUEL_FUNCTIONS_DIR}/ocf-fuel-funcs
#######################################################################
# Fill in some defaults if no values are specified
OCF_RESKEY_binary_default="openstack-dns-updater.py"
#OCF_RESKEY_config_default="/etc/aodh/aodh.conf"
#OCF_RESKEY_user_default="aodh"
OCF_RESKEY_pid_default="${HA_RSCTMP}/${__SCRIPT_NAME}/${__SCRIPT_NAME}.pid"
: ${HA_LOGTAG="ocf-dns-updater"}
: ${HA_LOGFACILITY="daemon"}
: ${OCF_RESKEY_binary=${OCF_RESKEY_binary_default}}
#: ${OCF_RESKEY_config=${OCF_RESKEY_config_default}}
#: ${OCF_RESKEY_user=${OCF_RESKEY_user_default}}
: ${OCF_RESKEY_pid=${OCF_RESKEY_pid_default}}
#######################################################################
usage() {
cat <<UEND
usage: $0 (start|stop|status)
$0 manages an OpenStack DnsUpdater Service process as an HA resource
The 'start' operation starts the service.
The 'stop' operation stops the service.
The 'status' operation reports whether the service is running
UEND
}
meta_data() {
cat <<END
<?xml version="1.0"?>
<!DOCTYPE resource-agent SYSTEM "ra-api-1.dtd">
<resource-agent name="DnsUpdater">
<version>1.0</version>
<longdesc lang="en">
sample dnsupdater resource
</longdesc>
<shortdesc lang="en">Example stateless resource agent</shortdesc>
<parameters>
<parameter name="pid" unique="0" required="0">
<longdesc lang="en">
The pid file to use for this OpenStack Aodh Evaluator Service (aodh-evaluator) instance
</longdesc>
<shortdesc lang="en">OpenStack Aodh Evaluator Service (aodh-evaluator) pid file</shortdesc>
<content type="string" default="${OCF_RESKEY_pid_default}" />
</parameter>
</parameters>
<actions>
<action name="start" timeout="20" />
<action name="stop" timeout="20" />
<action name="status" timeout="20" />
</actions>
</resource-agent>
END
}
#######################################################################
dnsupdater_validate() {
local rc
return ${OCF_SUCCESS}
}
dnsupdater_status() {
local pid
local rc
# check and make PID file dir
local PID_DIR="$( dirname ${OCF_RESKEY_pid} )"
if [ ! -d "${PID_DIR}" ] ; then
ocf_log debug "Create pid file dir: ${PID_DIR}"
mkdir -p "${PID_DIR}"
chmod 755 "${PID_DIR}"
fi
if [ ! -f $OCF_RESKEY_pid ]; then
ocf_log info "DnsUpdater is not running"
return $OCF_NOT_RUNNING
else
pid=`cat $OCF_RESKEY_pid`
fi
if [ -n "${pid}" ]; then
ocf_run -warn kill -s 0 $pid
rc=$?
else
ocf_log err "PID file ${OCF_RESKEY_pid} is empty!"
return $OCF_ERR_GENERIC
fi
if [ $rc -eq 0 ]; then
return $OCF_SUCCESS
else
ocf_log info "Old PID file found, but DnsUpdater not running"
return $OCF_NOT_RUNNING
fi
}
dnsupdater_monitor() {
local rc
local pid
dnsupdater_status
rc=$?
# If status returned anything but success, return that immediately
if [ $rc -ne $OCF_SUCCESS ]; then
return $rc
fi
ocf_log debug "OpenStack DnsUpdater monitor succeeded"
return $OCF_SUCCESS
}
dnsupdater_start() {
dnsupdater_status
rc=$?
if [ $rc -eq $OCF_SUCCESS ]; then
ocf_log info "OpenStack DnsUpdater is already running"
return $OCF_SUCCESS
fi
/usr/bin/python /usr/lib/python2.7/dist-packages/os_dns_updater/openstack-dns-updater.py >> /dev/null 2>&1 & echo $! > $OCF_RESKEY_pid
ocf_log debug "Create pid file: ${OCF_RESKEY_pid} with content $(cat ${OCF_RESKEY_pid})"
# Spin waiting for the server to come up.
while true; do
dnsupdater_monitor
rc=$?
[ $rc -eq $OCF_SUCCESS ] && break
if [ $rc -ne $OCF_NOT_RUNNING ]; then
ocf_log err "OpenStack DnsUpdater start failed"
exit $OCF_ERR_GENERIC
fi
sleep 1
done
ocf_log info "OpenStack DnsUpdater started"
return $OCF_SUCCESS
}
dnsupdater_stop() {
local rc
local shutdown_timeout=15
if [ -n "$OCF_RESKEY_CRM_meta_timeout" ]; then
shutdown_timeout=$(( ($OCF_RESKEY_CRM_meta_timeout/1000) ))
fi
dnsupdater_status
rc="${?}"
if [ "${rc}" -eq "${OCF_NOT_RUNNING}" ]; then
ocf_log info "OpenStack DnsUpdater (${OCF_RESKEY_binary}) already stopped"
return "${OCF_SUCCESS}"
fi
proc_stop "${OCF_RESKEY_pid}" "${OCF_RESKEY_binary}" $shutdown_timeout
return "${?}"
}
#######################################################################
case "$1" in
meta-data) meta_data
exit $OCF_SUCCESS;;
usage|help) usage
exit $OCF_SUCCESS;;
esac
# Anything except meta-data and help must pass validation
dnsupdater_validate || exit $?
# What kind of method was invoked?
case "$1" in
start) dnsupdater_start;;
stop) dnsupdater_stop;;
status) dnsupdater_status;;
monitor) dnsupdater_monitor;;
validate-all) ;;
*) usage
exit $OCF_ERR_UNIMPLEMENTED;;
esac

View File

@ -0,0 +1,79 @@
#! /bin/sh
### BEGIN INIT INFO
# Provides: skeleton
# Required-Start: $remote_fs $syslog
# Required-Stop: $remote_fs $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Example initscript
# Description: This file should be used to construct scripts to be
# placed in /etc/init.d.
### END INIT INFO
# Do NOT "set -e"
# PATH should only include /usr/* if it runs after the mountnfs.sh script
PATH=/sbin:/usr/sbin:/bin:/usr/bin
DIR=/usr/lib/python2.7/dist-packages/os_dns_updater
DESC="Description of the service"
DAEMON_NAME=openstack-dns-updater
DAEMON="/usr/bin/python $DIR/openstack-dns-updater.py"
DAEMON_ARGS=""
PIDFILE=/var/run/$DAEMON_NAME.pid
. /lib/lsb/init-functions
# Function that starts the daemon/service
#
do_start()
{
logger "DEBUG: entered start function"
log_daemon_msg "Starting openstack-dns-updater"
start-stop-daemon --start --background --pidfile $PIDFILE --make-pidfile --startas $DAEMON
ret=$?
logger "DEBUG: return code: $ret"
logger "DEBUG: vars: pidfile - $PIDFILE daemon - $DAEMON"
log_end_msg $?
}
do_stop()
{
#AP_RET=1 - running
#AP_RET=0 - not running
if [ -f $PIDFILE ];
then
AP_RET=1
else
AP_RET=0
fi
if [ $AP_RET = 0 ];
then
log_daemon_msg "Openstack-dns-updater daemon is already stopped"
exit 0
elif [ $AP_RET = 1 ];
then
log_daemon_msg "Stopping openstack-dns-updater"
start-stop-daemon --stop --signal 2 --pidfile $PIDFILE --retry 10
rm -f $PIDFILE
log_end_msg $?
fi
}
case "$1" in
start|stop)
do_${1}
;;
restart|reload|force-reload)
do_stop
do_start
;;
status)
status_of_proc "$DAEMON_NAME" "$DAEMON" && exit 0 || exit 3
;;
*)
#echo "Usage: $SCRIPTNAME {start|stop|restart|reload|force-reload}" >&2
echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2
exit 1
;;
esac

View File

@ -0,0 +1,19 @@
# openstack-dns-updater.upstart - run DNS updater script
description "OpenStack DNS Updater"
start on runlevel [2345]
stop on runlevel [!2345]
respawn
respawn limit 10 5
umask 022
# The following is not needed when using /usr/bin/python
# expect fork
# /var/log/upstart/openstack-dns-updater.log should be empty
# Logs to /var/log/nova/dns-updater.log
console log
exec /usr/bin/python /usr/lib/python2.7/dist-packages/os_dns_updater/openstack-dns-updater.py

View File

@ -0,0 +1,18 @@
#!/usr/bin/env python
# module to clear db every hour_of_day
import time, datetime
from os_dns_updater.utils import db_lib
from os_dns_updater.utils.cfg import DNS_CONF
from os_dns_updater.utils.cfg import LOG_FILE
import logging as log
log_level = log.DEBUG if (DNS_CONF["debug"] == "True") else log.INFO
log.basicConfig(filename=LOG_FILE, level=log_level,
format="%(levelname)s %(asctime)s %(message)s")
while 1:
hour_of_day = datetime.datetime.timetuple(datetime.datetime.now())[3]
if hour_of_day == 21:
db_lib.check_and_clear()
time.sleep(3599)

View File

@ -0,0 +1,157 @@
#!/usr/bin/env python
# module to work with rabbitmq (main)
#TODO!!! delete old records on time
from kombu import BrokerConnection
from kombu import Exchange
from kombu import Queue
from kombu.mixins import ConsumerMixin
from os_dns_updater.utils import dns_lib
from os_dns_updater.utils import db_lib
from os_dns_updater.utils.cfg import LOG_FILE, DNS_CONF
import json
import logging as log
log_level = log.DEBUG if (DNS_CONF["debug"] == "True") else log.INFO
log.basicConfig(filename=LOG_FILE, level=log_level,
format="%(levelname)s %(name)s %(asctime)s %(message)s")
class DnsUpdater(ConsumerMixin):
def __init__(self, connection):
log.debug("service started with configuration:")
log.debug(str(json.dumps(DNS_CONF,indent=4,separators=(",",":"))))
log.debug("initialize rabbit")
self.connection = connection
return
def get_consumers(self, consumer, channel):
consumers = []
exchanges = DNS_CONF["exchanges"]
exchanges = exchanges.split(",")
for exch in exchanges:
exchange = Exchange(exch, type="topic", durable=False)
queue = Queue(DNS_CONF["queue_name"], exchange,
routing_key=DNS_CONF["routing_key"],
durable=False, auto_delete=True, no_ack=True)
consumers.append(consumer(queue, callbacks=[self.on_message]))
return consumers
def on_message(self, body, message):
try:
res = self._handle_message(body)
except Exception as e:
log.error(repr(e))
def _handle_message(self, body):
events = [DNS_CONF["event_create"],DNS_CONF["event_delete"]]
v_key='oslo.message'
if isinstance(body,dict):
if v_key in body:
log.debug("received message in v2 format")
rdict=dict(body)
jbody = rdict.get(v_key)
if isinstance(jbody,str) or isinstance(jbody,unicode):
msgbody = json.loads(jbody)
else:
msgbody = dict(jbody)
else:
log.debug("received message in v1 format")
msgbody = dict(body)
log.debug(str(json.dumps(msgbody,indent=4,separators=(",",":"))))
else:
log.warning("incorrect data type in oslo message: {}".format(type(body)))
return False
if ('event_type' in msgbody) and ('payload' in msgbody):
event_type = msgbody["event_type"]
payload = msgbody["payload"]
else:
log.warning("unexpected message: {}".format(msgbody))
return False
if event_type in events:
if "metadata" in payload:
if "use_dns" in payload["metadata"]:
use_dns = (payload["metadata"]["use_dns"] == "yes")
else:
log.debug("no dns metadata for event {}".format(event_type))
return False
else:
log.debug("No metadata for event {}".format(event_type))
return False
else:
log.debug("event type ignored: {}".format(event_type))
return False
if use_dns:
nova_hostname = payload["hostname"]
nova_os_id = payload["instance_id"]
if event_type == DNS_CONF["event_create"]:
if not len(payload["fixed_ips"]):
log.warning("instance with no network")
return False
#always use first interface (todo change this)
nova_ip = payload["fixed_ips"][0]["address"]
netname = payload["fixed_ips"][0]["label"]
if netname not in DNS_CONF["networks"]:
log.warning("dns will not be used for {}".format(netname))
return False
if "message" in payload:
if payload["message"] == "Success":
log.debug("Instance successfully created")
else:
log.warning("Error creating instance")
return False
else:
log.warning("unknown instance state")
lookup_addr = dns_lib.lookup_hostname(nova_hostname)
if lookup_addr == nova_ip:
log.warning("Instance already has address : {}".format(nova_ip))
log.warning("Nothing to do for instance {}".format(nova_hostname))
elif lookup_addr is not None:
log.error("Instance has different address: {}".format(lookup_addr))
log.error("Refused to add instance: {}".format(nova_hostname))
return False
dns_lib.add_hostname(nova_hostname, nova_ip)
assigned_addr = dns_lib.lookup_hostname(nova_hostname)
if assigned_addr is None:
log.error("Dns server denied request for instance: {}".format(nova_hostname))
return False
else:
log.info("Instance added to dns: {} {}".format(nova_hostname, assigned_addr))
#db_lib.create_instance(nova_hostname, nova_ip, nova_os_id)
if event_type == DNS_CONF["event_delete"]:
lookup_addr = dns_lib.lookup_hostname(nova_hostname)
if lookup_addr is not None:
dns_lib.del_hostname(nova_hostname)
#db_lib.delete_instance(nova_hostname, nova_os_id)
else:
log.warning("instance {} not found in dns".format(nova_hostname))
log.warning("nothing to do")
return False
log.info("event of type {} processed".format(event_type))
return True
else:
log.debug("Instance will not use dns for {}".format(event_type))
return False
if __name__ == "__main__":
amqp_hosts = DNS_CONF["amqp_hosts"].split(",")
BROKER_LIST = []
amqp_user = DNS_CONF["amqp_user"]
amqp_password = DNS_CONF["amqp_password"]
fs = DNS_CONF["failover_strategy"]
for amqp_host in amqp_hosts:
broker_uri = "amqp://{}:{}@{}//".format(amqp_user,amqp_password,amqp_host)
BROKER_LIST.append(broker_uri)
log.info("Connecting to broker {}".format(BROKER_LIST))
with BrokerConnection(BROKER_LIST, failover_strategy=fs ) as connection:
try:
DnsUpdater(connection).run()
except Exception, e:
log.error(repr(e))
#TODO use oslo for config, log, db(?), messaging(?)

View File

@ -0,0 +1,48 @@
#!/usr/bin/env python
from ConfigParser import SafeConfigParser
LOG_FILE = "/var/log/dns-updater.log"
CONF_PATH = "/etc/os_dns_updater/dns-updater.conf"
conflist = [
"debug",
"db_name",
"db_user",
"db_password",
"exchanges",
"queue_name",
"routing_key",
"event_create",
"event_delete",
"amqp_user",
"amqp_password",
"amqp_hosts",
"failover_strategy",
"domain",
"networks",
"region",
"dns_keyfile",
"nameserver",
"ttl",
"maxcounter",
"insttime"
]
def _parse_config(confname, conflist):
config = SafeConfigParser()
try:
config.read(confname)
except Exception as e:
pass
cf = {}
for item in conflist:
try:
value = config.get("DEFAULT", item)
except Exception as e:
value = None
cf[item] = value
return cf
DNS_CONF = _parse_config(CONF_PATH,conflist)
#TODO use neutron port update events

View File

@ -0,0 +1,215 @@
#!/usr/bin/env python
# module to work with db
from sqlalchemy import MetaData, create_engine
from sqlalchemy import Column
from sqlalchemy import String, Integer, DateTime
from sqlalchemy.orm import sessionmaker, scoped_session
from sqlalchemy.ext.declarative import declarative_base
from os_dns_updater.utils.cfg import DNS_CONF
from os_dns_updater.utils.cfg import LOG_FILE
import re
import time
import datetime
import dateutil.relativedelta as timedelta
import hashlib
import logging as log
log_level = log.DEBUG if (DNS_CONF["debug"] == "True") else log.INFO
log.basicConfig(filename=LOG_FILE, level=log_level,
format="%(levelname)s %(asctime)s %(message)s")
MAX_INST = int(DNS_CONF["maxcounter"]) # max number of instances of type in tenant
if not MAX_INST:
MAX_INST = 4095
DBTTL = int(DNS_CONF["insttime"]) #time to store old data in db (state!=added)
if not DBTTL:
DBTTL = 90
dbcred = DNS_CONF["db_user"]+":"+DNS_CONF["db_password"]
dbaddr = "mysql://"+dbcred+"@localhost/"+DNS_CONF["db_name"]
dbengine = create_engine(dbaddr, pool_recycle=120)
dbmeta = MetaData()
Base = declarative_base()
class Instance(Base):
__tablename__ = "instance"
id = Column(Integer, primary_key=True)
ip = Column(String(20))
name = Column(String(90))
dns_domain = Column(String(90))
uuid = Column(String(40))
state = Column(String(40))
created_at = Column(DateTime)
updated_at = Column(DateTime)
deleted_at = Column(DateTime)
def __init__(self, name, uuid, ip):
self.ip = ip
self.name = name
self.uuid = uuid
self.created_at = datetime.datetime.now()
#class Event(Base):
# __tablename__ = "event"
# id = Column(Integer, primary_key=True)
# def __init__(self):
# pass
Base.metadata.create_all(dbengine)
def check_and_clear():
Session = scoped_session(sessionmaker(bind=dbengine))
dbsession = Session()
log.info("db check_and_clear started with options {} {}".format(MAX_INST,DBTTL))
oldtime = datetime.datetime.today() - timedelta.relativedelta(days=DBTTL)
log.info("looking for unregistered records older than {}".format(oldtime))
inst = dbsession.query(Instance).filter(
Instance.state != 'added',
Instance.created_at <= oldtime
).delete()
try:
dbsession.commit()
log.info("{} records removed from db".format(len(inst)))
except Exception as e:
log.error(repr(e))
dbsession.rollback()
dbsession.bind.dispose()
Session.remove()
def create_instance(iname, ip, uuid):
Session = scoped_session(sessionmaker(bind=dbengine))
dbsession = Session()
inst = dbsession.query(Instance).filter(
Instance.name == iname,
Instance.state != 'added').first()
if not inst:
log.warning("instance {} was not found in db".format(iname))
new_inst = Instance(iname, uuid, ip)
dbsession.add(new_inst)
new_inst.state = 'added'
new_inst.dns_domain = DNS_CONF["domain"]
else:
inst.state = 'added'
inst.dns_domain = DNS_CONF["domain"]
inst.uuid = uuid
inst.ip = ip
inst.updated_at = datetime.datetime.now()
try:
dbsession.commit()
except Exception as e:
log.error(repr(e))
dbsession.rollback()
dbsession.bind.dispose()
Session.remove()
def delete_instance(iname, uuid):
Session = scoped_session(sessionmaker(bind=dbengine))
dbsession = Session()
inst = dbsession.query(Instance).filter(Instance.name == iname).first()
if not inst:
log.warning("instance {} was not found in db".format(iname))
else:
inst.state = 'deleted'
inst.deleted_at = datetime.datetime.now()
try:
dbsession.commit()
except Exception as e:
log.error(repr(e))
dbsession.rollback()
dbsession.bind.dispose()
Session.remove()
def generate_name(fqn, pname, dname):
fqn = str(fqn)
log.info("generating instance name for: {} {} {}".format(fqn,pname,dname))
app = fqn.rsplit('.',1)[-1]
app = app[:3]
name = pname.split('-')[0]
name = name[:9]
pat = re.compile(r'^\D\d+')
if pat.search(name) is None:
name = 'None'
log.warning("incorrect project name {}".format(pname))
patt = "{}-{}-{}".format(DNS_CONF['region'],name,app)
try:
instnumber = num_from_patt(patt)
except:
instnumber = rstr(5)
res = "{}-{}".format(patt,instnumber)
return res
def num_from_patt(patt):
patt1 = patt + '%'
Session = scoped_session(sessionmaker(bind=dbengine))
dbsession = Session()
instances = dbsession.query(Instance).filter(
Instance.name.like(patt1)).all()
nums = []
if not instances:
log.debug("new instance of type {}".format(patt))
res = 1
else:
res = 0
for inst in instances:
log.debug("found instance {}".format(inst.name))
d = inst.name
d = d.split('-')[3]
d = int(d,16)
nums.append(d)
if not len(nums):
res = 1
else:
rlist = list(set(range(1,MAX_INST)) - set(nums))
res = rlist[0]
log.debug("found new number: {}".format(res))
res = str(hex(res))[2:]
if len(res) == 1:
res = '00'+res
if len(res) == 2:
res = '0'+res
new_inst = Instance("{}-{}".format(patt,res), '', '')
dbsession.add(new_inst)
new_inst.state = 'reserved'
new_inst.dns_domain = DNS_CONF["domain"]
try:
dbsession.commit()
except Exception as e:
dbsession.rollback()
dbsession.bind.dispose()
Session.remove()
return res
def rstr(i):
#random string of length i
h = hashlib.sha1()
h.update(str(time.time()*10000))
rs = h.hexdigest()[:i]
return rs
#unused right now:
def instance_exists(iname):
Session = scoped_session(sessionmaker(bind=dbengine))
dbsession = Session()
inst = dbsession.query(Instance).filter(
Instance.name==iname,
Instance.state!='deleted',
Instance.dns_domain==DNS_CONF['domain']).first()
if not inst:
log.debug("instance {} was not found in db".format(iname))
res = False
else:
res = True
dbsession.bind.dispose()
Session.remove()
return res
def update_instance():
pass

View File

@ -0,0 +1,78 @@
#!/usr/bin/env python
# module to work with dns
import dns.resolver
import dns.reversename
import dns.query
import dns.zone
import dns.exception
import dns.name
from subprocess import Popen, PIPE
import logging as log
from os_dns_updater.utils.cfg import LOG_FILE
from os_dns_updater.utils.cfg import DNS_CONF
log_level = log.DEBUG if (DNS_CONF["debug"] == "True") else log.INFO
log.basicConfig(filename=LOG_FILE, level=log_level,
format="%(levelname)s %(asctime)s %(message)s")
NSUPDATE_ADD = "\
server {nameserver}\n\
update add {hostname} {ttl} A {hostaddr}\n\
send"
NSUPDATE_DEL = "\
server {nameserver}\n\
update delete {hostname} A\n\
send"
def dns_zone_to_text(dnsconf=DNS_CONF):
z = dns.zone.from_xfr(dns.query.xfr(dnsconf["nameserver"],dnsconf["domain"]))
names = z.nodes.keys()
names.sort()
res = []
for n in names:
res.append(z[n].to_text(n))
log.info("dns zone loaded{}".format(res))
return res
def lookup_hostname(name,dnsconf=DNS_CONF):
try:
resolv = dns.resolver.Resolver(configure=False)
log.debug("initialize resolver")
resolv.nameservers.append(dnsconf["nameserver"])
resolv.domain = dns.name.from_text(dnsconf["domain"])
resolv.search.append(dns.name.from_text(dnsconf["domain"]))
log.debug("resolver {} {}".format(dnsconf["nameserver"],dnsconf["domain"]))
try:
answer = resolv.query(name)
addr = answer.rrset[0]
log.info("lookup host {} at {}".format(name, addr))
except dns.exception.DNSException as e:#handle nxdomain - not an error
log.info("instance not found: {}".format(repr(e)))
addr = None
except dns.exception.DNSException as e:
log.error(repr(e))
addr = None
return addr
def dns_update(hostname, script, hostaddr=""):
hostname = hostname + "." + DNS_CONF["domain"]
p = Popen(["/usr/bin/nsupdate", "-k", DNS_CONF["dns_keyfile"]], stdin=PIPE)
inp = script.format(
nameserver=DNS_CONF["nameserver"],
hostname=hostname, ttl=DNS_CONF["ttl"], hostaddr=hostaddr)
p.communicate(input=inp)
def add_hostname(name, ip):
log.info("adding to dns {} {}".format(name, ip))
dns_update(name, NSUPDATE_ADD, ip)
def del_hostname(name):
log.info("deleting from dns {}".format(name))
dns_update(name, NSUPDATE_DEL)
#TODO always use dnspython do not use subprocess

View File

@ -0,0 +1,4 @@
requests>=2.0.0
dnspython>=1.12.0
kombu>=3.0.2
SQLAlchemy>=1.0.12

View File

@ -0,0 +1,14 @@
[metadata]
name = os_dns_updater
description = service to register virtual machines in dns
summary = listen to nova RabbitMQ events and update dns records
author = Sberbank Technology
[files]
packages = os_dns_updater
[global]
setup-hooks = pbr.hooks.setup_hook
[pbr]
warnerrors = True

View File

@ -0,0 +1,5 @@
import setuptools
# all other params will be taken from setup.cfg
setuptools.setup(packages=setuptools.find_packages(),
setup_requires=['pbr>=1.8'], pbr=True)

View File

@ -0,0 +1,107 @@
class dns_update::controller {
$plugin_hash = hiera('fuel-plugin-dns-update')
$amqp_hosts = hiera('amqp_hosts')
$net1 = $plugin_hash['net1']
$net2 = $plugin_hash['net2']
$dns_key_hash=$plugin_hash['dns_key']
$dns_key=$dns_key_hash['content']
$rabbit_hash = hiera_hash('rabbit')
$amqp_user = $rabbit_hash['user']
$amqp_password = $rabbit_hash['password']
$node_hash = hiera('node')
$node_net = $node_hash['network_roles']
$node_management = $node_net['management']
$dnsupdate_mysql_exist = inline_template("<% if File.exist?('/tmp/dnsupdate-mysql.lock') -%>true<% end -%>")
if $management_vip == $service_endpoint { #in case of local keystone
$region='RegionOne'
}
else { #in case of detach keystone
$region=hiera(region)
}
Exec { path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin' }
file {'os_dns_updater':
path => '/tmp/os_dns_updater',
source => 'puppet:///modules/dns_update/',
recurse => 'true',
}
package {'python-pip':
ensure => 'installed',
}
package {'python-dnspython':
ensure => 'installed',
}
package {'python-pymysql':
ensure => 'installed'
}
package {'pycrypto':
ensure => 'installed',
provider => 'pip',
}
file {'/etc/os_dns_updater':
ensure => 'directory',
}
if $dnsupdate_mysql_exist != 'true' {
exec {"install dns_updater":
command => "pip install -e /tmp/os_dns_updater/",
require => File['os_dns_updater'],
}
}
file {'openstack-dns-updater.conf':
path => '/etc/os_dns_updater/dns-updater.conf',
source => 'puppet:///modules/dns_update/etc/dns-updater.conf',
require => File['/etc/os_dns_updater'],
}
file {'example.key':
path => '/etc/os_dns_updater/exaple.key',
content => $dns_key,
require => File['/etc/os_dns_updater'],
}
file_line { "vm networks configuration":
path => "/etc/os_dns_updater/dns-updater.conf",
line => "networks=$net1,$net2",
match => "networks=.*",
require => File['openstack-dns-updater.conf'],
}
file_line { "region configuration":
path => "/etc/os_dns_updater/dns-updater.conf",
line => "region=$region",
match => "region=.*",
require => File['openstack-dns-updater.conf'],
}
file_line { "amqp host configuration":
path => "/etc/os_dns_updater/dns-updater.conf",
line => "amqp_hosts=$amqp_hosts",
match => "amqp_hosts=.*",
require => File['openstack-dns-updater.conf'],
}
file_line { "amqp password configuration":
path => "/etc/os_dns_updater/dns-updater.conf",
line => "amqp_password=$amqp_password",
match => "amqp_password=.*",
require => File['openstack-dns-updater.conf'],
}
file_line { "amqp user configuration":
path => "/etc/os_dns_updater/dns-updater.conf",
line => "amqp_user=$amqp_user",
match => "amqp_user=.*",
require => File['openstack-dns-updater.conf'],
}
file {'added module to dist-packages':
path => '/usr/lib/python2.7/dist-packages/os_dns_updater',
source => '/tmp/os_dns_updater/os_dns_updater',
recurse => 'true',
}
File['os_dns_updater']->
Package['python-pip']->
Package['python-pymysql']->
Package['pycrypto']->
File['/etc/os_dns_updater']
}

View File

@ -0,0 +1 @@
class dns_update {}

View File

@ -0,0 +1,25 @@
class dns_update::mysql {
$mysql_hash = hiera_hash('mysql')
$mysql_pass = $mysql_hash['root_password']
Exec { path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin' }
$dnsupdate_mysql_exist = inline_template("<% if File.exist?('/tmp/dnsupdate-mysql.lock') -%>true<% end -%>")
if $dnsupdate_mysql_exist != 'true' {
exec {"create dns_updater database":
command => "mysql --host=localhost --user=root --password=$mysql_pass -e 'create database dnsupdate;'",
} ->
exec {"create dns_updater user":
command => "mysql --host=localhost --user=root --password=$mysql_pass -e 'create user \"dnsupdate\"@\"%\" identified by \"ai2o3nvsjS3cvm\";'",
} ->
exec {"grant privileges to dns_updater user":
command => "mysql --host=localhost --user=root --password=$mysql_pass -e 'grant all privileges on dnsupdate.* to \"dnsupdate\"@\"%\";'",
} ->
exec {"create tables":
command => "python /tmp/os_dns_updater/db_setup.py",
} ->
file {"/tmp/dnsupdate-mysql.lock":
path => "/tmp/dnsupdate-mysql.lock",
ensure => "file",
}
}
}

View File

@ -0,0 +1,31 @@
class dns_update::neutron {
Exec { path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin' }
$neutron_adv_con=hiera('neutron_advanced_configuration')
$ha=$neutron_adv_con['dhcp_agent_ha']
$neutron_dhcp_exist = inline_template("<% if File.exist?('/etc/init.d/neutron-dhcp-agent') -%>true<% end -%>")
neutron_config {
"DEFAULT/dns_domain": value => "vm.example.net";
}
if $neutron_dhcp_exist == "true" {
neutron_dhcp_agent_config {
"DEFAULT/dhcp_domain": value => "vm.example.net";
}
if ($ha == true) {
exec{"neutron-dhcp-agent crm restart":
command => "crm resource restart clone_neutron-dhcp-agent",
}
Neutron_config <||> ~> Exec['neutron-dhcp-agent crm restart']
Neutron_dhcp_agent_config <||> ~> Exec['neutron-dhcp-agent crm restart']
}
elsif ($ha == false) {
exec{"neutron-dhcp-agent service restart":
command => "service neutron-dhcp-agent restart",
}
Neutron_config <||> ~> Exec['neutron-dhcp-agent service restart']
Neutron_dhcp_agent_config <||> ~> Exec['neutron-dhcp-agent service restart']
}
}
Neutron_config <||> ~> service { 'neutron-server': }
}

View File

@ -0,0 +1,19 @@
class dns_update::nova {
$nova_compute_exist = inline_template("<% if File.exist?('/etc/init.d/nova-compute') -%>true<% end -%>")
$nova_api_exist = inline_template("<% if File.exist?('/etc/init.d/nova-api') -%>true<% end -%>")
nova_config {
"DEFAULT/dhcp_domain": value => "vm.example.net";
}
if $nova_compute_exist == "true"
{
Nova_config <||> ~> service { 'nova-compute': }
}
if $nova_api_exist == "true"
{
Nova_config <||> ~> service { 'nova-api': }
}
}

View File

@ -0,0 +1,38 @@
class dns_update::pacemaker {
Exec { path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin' }
$operations = {
'monitor' => {'interval' => '5s', 'timeout' => '30s' },
'start' => {'interval' => '0s', timeout => '30s' },
'stop' => {'interval' => '0s', timeout => '30s' }
}
file {'DnsUpdater':
path => '/usr/lib/ocf/resource.d/fuel/DnsUpdater',
source => '/tmp/os_dns_updater/ocf/DnsUpdater',
}
pacemaker::service { "DnsUpdater":
primitive_class => 'ocf',
primitive_provider => 'fuel',
primitive_type => 'DnsUpdater',
operations => $operations,
use_handler => false,
complex_type => 'clone',
}
service { "DnsUpdater":
ensure => running,
name => 'DnsUpdater',
enable => true,
provider => 'pacemaker'
}
exec {"Cleanup resources":
command => "crm resource cleanup clone_p_DnsUpdater",
}
File['DnsUpdater'] ->
Service['DnsUpdater'] ->
Exec['Cleanup resources']
}

View File

@ -0,0 +1,16 @@
class dns_update::service {
Exec { path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin' }
file {'init openstack-dns-updater.conf':
path => '/etc/init/openstack-dns-updater.conf',
source => '/tmp/os_dns_updater/openstack-dns-updater.conf',
}
exec{"start openstack-dns-updater":
command => "service openstack-dns-updater start",
require => File['init openstack-dns-updater.conf'],
returns => [0,1],
}
}

64
deployment_tasks.yaml Normal file
View File

@ -0,0 +1,64 @@
- id: fp-dns-update-controller
role: ['primary-controller','controller']
version: 2.0.0
type: puppet
parameters:
puppet_manifest: puppet/manifests/controller.pp
puppet_modules: puppet/modules:/etc/puppet/modules
timeout: 360
requires: ['post_deployment_start']
required_for: ['post_deployment_end']
- id: fp-dns-update-mysql
role: ['primary-controller']
version: 2.0.0
type: puppet
parameters:
puppet_manifest: puppet/manifests/mysql.pp
puppet_modules: puppet/modules:/etc/puppet/modules
timeout: 360
requires: ['post_deployment_start','fp-dns-update-controller']
required_for: ['post_deployment_end']
- id: fp-dns-update-service
role: ['primary-controller','controller']
version: 2.0.0
type: puppet
cross-depends:
- name: fp-dns-update-controller
role: 'controller'
parameters:
puppet_manifest: puppet/manifests/service.pp
puppet_modules: puppet/modules:/etc/puppet/modules
timeout: 360
requires: ['post_deployment_start','fp-dns-update-mysql']
required_for: ['post_deployment_end']
- id: fp-dns-update-nova
role: ['primary-controller','controller','compute']
version: 2.0.0
type: puppet
parameters:
puppet_manifest: puppet/manifests/nova.pp
puppet_modules: puppet/modules:/etc/puppet/modules
timeout: 360
requires: ['post_deployment_start']
required_for: ['post_deployment_end']
- id: fp-dns-update-neutron
role: ['primary-controller','controller','primary-network-node','network-node']
version: 2.0.0
type: puppet
parameters:
puppet_manifest: puppet/manifests/neutron.pp
puppet_modules: puppet/modules:/etc/puppet/modules
timeout: 360
requires: ['post_deployment_start']
required_for: ['post_deployment_end']
- id: fp-dns-update-pacemaker
role: ['primary-controller','controller']
version: 2.0.0
type: puppet
parameters:
puppet_manifest: puppet/manifests/pacemaker.pp
puppet_modules: puppet/modules:/etc/puppet/modules
timeout: 360
requires: ['fp-dns-update-mysql','fp-dns-update-service']
required_for: ['post_deployment_end']

19
environment_config.yaml Normal file
View File

@ -0,0 +1,19 @@
attributes:
metadata:
group: 'other'
net1:
type: "text"
weight: 10
value: ""
label: "Net1 Network name"
net2:
type: "text"
weight: 11
value: ""
label: "Net2 Network name"
dns_key:
type: "file"
weight: 12
value: ""
label: "Key for DNS server"

36
metadata.yaml Normal file
View File

@ -0,0 +1,36 @@
# Plugin name
name: fuel-plugin-dns-update
title: DNS Update plugin
# Plugin version
version: 0.2.3
# Description
description: Enables custom dns servers
# Required fuel version
fuel_version: ['9.0']
# Groups
groups: []
# Licenses
licenses: ['Apache License, Version 2.0']
# Homepage
homepage: https://github.com/openstack/fuel-plugin-dns-updater
# Authors
authors: ['Ivan Zinoviev <zinoviev.ii@gmail.com>']
# Change `false` to `true` if the plugin can be installed in the environment
# after the deployment.
is_hotpluggable: true
# The plugin is compatible with releases in the list
releases:
- os: ubuntu
version: liberty-8.0
mode: ['ha']
deployment_scripts_path: deployment_scripts/
repository_path: repositories/ubuntu
- os: ubuntu
version: mitaka-9.0
mode: ['ha']
deployment_scripts_path: deployment_scripts/
repository_path: repositories/ubuntu
# Version of plugin package
package_version: '4.0.0'

View File

View File

1
tasks.yaml Normal file
View File

@ -0,0 +1 @@
[]