Skip to content

Instantly share code, notes, and snippets.

@nitecoder
Last active August 29, 2015 13:57
Show Gist options
  • Save nitecoder/9536584 to your computer and use it in GitHub Desktop.
Save nitecoder/9536584 to your computer and use it in GitHub Desktop.
from starcluster.clustersetup import ClusterSetup
from starcluster.logger import log
import subprocess
from starcluster import threadpool
from boto.route53.connection import Route53Connection
from boto.route53.connection import ResourceRecordSets
class Route53Plugin(ClusterSetup):
def __init__(self,
zone_name,
master_name_template,
node_name_template,
dry_run):
"""Parameters:
zone_name - name of the Route53 zone in which to register A records
master_name_template - register name for master based on this template
node_name_template - register name for nodes based on this template
dry_run - don't commit changes if True
templates can use
{alias} to substitute for node.alias
{zone} to substitute for zone.name
"""
self.zone_name = zone_name
self.master_name_template = master_name_template
self.node_name_template = node_name_template
self.dry_run = dry_run
self.conn = Route53Connection()
log.info("route53_plugin: *********** init: DRY_RUN=%s" % dry_run)
def run (self, nodes, master, user, user_shell, volumes):
log.debug("route53_plugin: *********** run: %s", nodes)
comment="Starcluster route53_plugin run for %s" % [n.alias for n in nodes]
self._registerNodes(nodes, comment)
# def on_restart(self, nodes, master, user, user_shell, volumes):
# log.debug("route53_plugin: *********** run: %s", nodes)
# comment="Starcluster route53_plugin run for %s" % [n.alias for n in nodes]
# _registerNodes(nodes, comment)
def on_add_node(self, node, nodes, master, user, user_shell, volumes):
log.debug("route53_plugin: *********** add_node: %s", node)
comment="Starcluster route53_plugin add_node %s" % node.alias
self._registerNodes([node], comment)
def on_remove_node(self, node, nodes, master, user, user_shell, volumes):
log.debug("route53_plugin: *********** remove_node: %s", node)
comment="Starcluster route53_plugin remove_node %s" % node.alias
self._unregisterNodes([node], comment)
def on_shutdown(self, nodes, master, user, user_shell, volumes):
log.debug("route53_plugin: *********** shutdown: %s", nodes)
comment="Starcluster route53_plugin shutdown %s" % [n.alias for n in nodes]
self._unregisterNodes(nodes, comment)
def _registerNodes(self, nodes, comment):
changeSet = self._startChangeSet(comment)
if changeSet == None:
log.error("route53_plugin: *** unable to create change set")
return
for node in nodes:
log.debug("route53_plugin: checking node %s (is_master %s)", node, node.is_master())
if not node.is_master() and self.master_name_template:
self._registerNode(changeSet, node, self.master_name_template)
elif self.node_name_template:
self._registerNode(changeSet, node, self.node_name_template)
self._commitChangeSet(changeSet)
def _unregisterNodes(self, nodes, comment):
changeSet = self._startChangeSet(comment)
if changeSet == None:
log.error("route53_plugin: *** unable to create change set")
return
for node in nodes:
log.debug("route53_plugin: checking node %s (is_master %s)", node, node.is_master())
if not node.is_master() and self.master_name_template:
self._unregisterNode(changeSet, node)
elif self.node_name_template:
self._unregisterNode(changeSet, node)
self._commitChangeSet(changeSet)
def _startChangeSet(self, comment=None):
zone = self.conn.get_zone(self.zone_name)
if not zone:
log.error("route53_plugin: *** No zone %s, please create", self.zone_name)
return None
log.debug("route53_plugin: *** creating change set for zone %s (%s)", zone, comment)
return ResourceRecordSets(self.conn, zone.id, comment=comment)
def _commitChangeSet(self, changeSet):
log.info("chef_plugin: *** Submitting to route53 %s", changeSet)
if not changeSet.changes:
log.debug("chef_plugin: empty change set - nothing to do")
return
if self.dry_run:
log.warn("chef_plugin: DRY-RUN ONLY - not sending to route53")
return
return changeSet.commit()
# TODO: consider waiting for request to be INSYNC
def _unregisterNode(self, changeSet, node):
hostname = node.tags.get('hostname', None)
if not hostname:
log.info("route53_plugin: *** not hostname tag, nothing to unregister for %s", node)
return
return self._unregisterHostName(changeSet, hostname)
def _registerNode(self, changeSet, node, name_template):
addr = node.ip_address #node.addr
if not addr:
addr = node.private_ip_address
if not addr:
log.error("route53_plugin: *** unable to obtain address for node %s", node)
return
name = name_template.format(
alias=node.alias,
zone=self.zone_name
)
if not name:
log.error("route53_plugin: *** unable to obtain the name for node %s template %s", node, node_template)
return
node.add_tag('hostname', name)
return self._registerHostName(changeSet, name, addr)
def _unregisterHostName(self, changeSet, name):
zone = self.conn.get_zone(self.zone_name)
r = zone.get_a(name)
if not r:
log.warn("route53_plugin: *** no record found for name %s - nothing to unregister", name)
return
req = changeSet.add_change_record('DELETE', r)
#log.info("route53_plugin: *** Deleting A record %s for %s", r, name)
#zone.delete_a(name)
def _registerHostName(self, changeSet, name, addr):
req = changeSet.add_change(action='UPSERT', name=name, type='A', ttl=300)
req.add_value(addr)
@nitecoder
Copy link
Author

This is a plugin for integrating Starcluster with route53. It will publish DNS A entries for the master and all nodes that are created by starcluster. If the node with the same name is started again with another IP, it will update the DNS entry.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment