You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by al...@apache.org on 2015/08/11 22:44:18 UTC

[3/4] incubator-brooklyn git commit: brooklyn-software-network: add org.apache package prefix

brooklyn-software-network: add org.apache package prefix


Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/689b49c3
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/689b49c3
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/689b49c3

Branch: refs/heads/master
Commit: 689b49c3af742ba600ac814a8bb93ab1077397e3
Parents: 0dc3911
Author: Ciprian Ciubotariu <ch...@gmx.net>
Authored: Tue Aug 11 17:05:15 2015 +0300
Committer: Ciprian Ciubotariu <ch...@gmx.net>
Committed: Tue Aug 11 17:05:15 2015 +0300

----------------------------------------------------------------------
 .../entity/network/bind/BindDnsServer.java      | 156 ---------
 .../network/bind/BindDnsServerDriver.java       |  38 ---
 .../entity/network/bind/BindDnsServerImpl.java  | 339 -------------------
 .../network/bind/BindDnsServerSshDriver.java    | 184 ----------
 .../entity/network/bind/BindOsSupport.java      | 113 -------
 .../entity/network/bind/BindDnsServer.java      | 156 +++++++++
 .../network/bind/BindDnsServerDriver.java       |  38 +++
 .../entity/network/bind/BindDnsServerImpl.java  | 339 +++++++++++++++++++
 .../network/bind/BindDnsServerSshDriver.java    | 184 ++++++++++
 .../entity/network/bind/BindOsSupport.java      | 113 +++++++
 .../brooklyn/entity/network/bind/domain.zone    |  46 ---
 .../brooklyn/entity/network/bind/ifcfg          |  24 --
 .../brooklyn/entity/network/bind/named.conf     |  63 ----
 .../brooklyn/entity/network/bind/named.empty    |  30 --
 .../entity/network/bind/named.localhost         |  32 --
 .../brooklyn/entity/network/bind/named.loopback |  31 --
 .../brooklyn/entity/network/bind/resolv.conf    |  25 --
 .../brooklyn/entity/network/bind/reverse.zone   |  37 --
 .../brooklyn/entity/network/bind/rfc1912.zone   |  52 ---
 .../brooklyn/entity/network/bind/domain.zone    |  46 +++
 .../apache/brooklyn/entity/network/bind/ifcfg   |  24 ++
 .../brooklyn/entity/network/bind/named.conf     |  63 ++++
 .../brooklyn/entity/network/bind/named.empty    |  30 ++
 .../entity/network/bind/named.localhost         |  32 ++
 .../brooklyn/entity/network/bind/named.loopback |  31 ++
 .../brooklyn/entity/network/bind/resolv.conf    |  25 ++
 .../brooklyn/entity/network/bind/reverse.zone   |  37 ++
 .../brooklyn/entity/network/bind/rfc1912.zone   |  52 +++
 .../network/bind/BindDnsServerByonLiveTest.java |  45 ---
 .../network/bind/BindDnsServerEc2LiveTest.java  |  63 ----
 .../bind/BindDnsServerIntegrationTest.java      | 261 --------------
 .../network/bind/BindDnsServerLiveTest.java     | 114 -------
 .../bind/BindDnsServerSoftlayerLiveTest.java    |  33 --
 .../bind/DoNothingSoftwareProcessDriver.java    |  55 ---
 .../network/bind/PrefixAndIdEnricher.java       |  57 ----
 .../network/bind/TestBindDnsServerImpl.java     |  92 -----
 .../network/bind/BindDnsServerByonLiveTest.java |  45 +++
 .../network/bind/BindDnsServerEc2LiveTest.java  |  63 ++++
 .../bind/BindDnsServerIntegrationTest.java      | 261 ++++++++++++++
 .../network/bind/BindDnsServerLiveTest.java     | 114 +++++++
 .../bind/BindDnsServerSoftlayerLiveTest.java    |  33 ++
 .../bind/DoNothingSoftwareProcessDriver.java    |  55 +++
 .../network/bind/PrefixAndIdEnricher.java       |  57 ++++
 .../network/bind/TestBindDnsServerImpl.java     |  90 +++++
 44 files changed, 1888 insertions(+), 1890 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/689b49c3/software/network/src/main/java/brooklyn/entity/network/bind/BindDnsServer.java
----------------------------------------------------------------------
diff --git a/software/network/src/main/java/brooklyn/entity/network/bind/BindDnsServer.java b/software/network/src/main/java/brooklyn/entity/network/bind/BindDnsServer.java
deleted file mode 100644
index f46cd75..0000000
--- a/software/network/src/main/java/brooklyn/entity/network/bind/BindDnsServer.java
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package brooklyn.entity.network.bind;
-
-import java.util.Map;
-
-import com.google.common.base.Predicate;
-import com.google.common.base.Predicates;
-import com.google.common.collect.Multimap;
-import com.google.common.reflect.TypeToken;
-
-import org.apache.brooklyn.catalog.Catalog;
-import brooklyn.config.ConfigKey;
-import brooklyn.entity.Entity;
-import brooklyn.entity.annotation.Effector;
-import brooklyn.entity.basic.ConfigKeys;
-import brooklyn.entity.basic.DynamicGroup;
-import brooklyn.entity.basic.SoftwareProcess;
-import brooklyn.entity.proxying.ImplementedBy;
-import brooklyn.event.AttributeSensor;
-import brooklyn.event.basic.PortAttributeSensorAndConfigKey;
-import brooklyn.event.basic.Sensors;
-import brooklyn.location.basic.PortRanges;
-import brooklyn.util.flags.SetFromFlag;
-import brooklyn.util.net.Cidr;
-
-/**
- * This sets up a BIND DNS server.
- */
-@Catalog(name="BIND", description="BIND is an Internet Domain Name Server.", iconUrl="classpath:///isc-logo.png")
-@ImplementedBy(BindDnsServerImpl.class)
-public interface BindDnsServer extends SoftwareProcess {
-
-    @SetFromFlag("filter")
-    ConfigKey<Predicate<? super Entity>> ENTITY_FILTER = ConfigKeys.newConfigKey(new TypeToken<Predicate<? super Entity>>() {},
-            "bind.entity.filter", "Filter for entities which will use the BIND DNS service for name resolution." +
-                    "Default is all instances of SoftwareProcess in the application.",
-            Predicates.instanceOf(SoftwareProcess.class));
-
-    @SetFromFlag("domainName")
-    ConfigKey<String> DOMAIN_NAME = ConfigKeys.newStringConfigKey(
-            "bind.domain.name", "The DNS domain name to serve", "brooklyn.local");
-
-    @SetFromFlag("reverseLookupNetwork")
-    ConfigKey<String> REVERSE_LOOKUP_NETWORK = ConfigKeys.newStringConfigKey(
-            "bind.reverse-lookup.address", "Network address for reverse lookup zone");
-
-    @SetFromFlag("subnet")
-    ConfigKey<String> MANAGEMENT_CIDR = ConfigKeys.newStringConfigKey(
-            "bind.access.cidr", "Subnet CIDR or ACL allowed to access DNS", "0.0.0.0/0");
-
-    @SetFromFlag("hostnameSensor")
-    ConfigKey<AttributeSensor<String>> HOSTNAME_SENSOR = ConfigKeys.newConfigKey(new TypeToken<AttributeSensor<String>>() {},
-            "bind.sensor.hostname", "Sensor on managed entities that reports the hostname");
-
-    PortAttributeSensorAndConfigKey DNS_PORT =
-            new PortAttributeSensorAndConfigKey("bind.port", "BIND DNS port for TCP and UDP", PortRanges.fromString("53"));
-
-    @SetFromFlag("zoneFileTemplate")
-    ConfigKey<String> DOMAIN_ZONE_FILE_TEMPLATE = ConfigKeys.newStringConfigKey(
-            "bind.template.domain-zone", "The BIND domain zone file to serve (as FreeMarker template)",
-            "classpath://brooklyn/entity/network/bind/domain.zone");
-
-    @SetFromFlag("reverseZoneFileTemplate")
-    ConfigKey<String> REVERSE_ZONE_FILE_TEMPLATE = ConfigKeys.newStringConfigKey(
-            "bind.template.reverse-zone", "The BIND reverse lookup zone file to serve (as FreeMarker template)",
-            "classpath://brooklyn/entity/network/bind/reverse.zone");
-
-    @SetFromFlag("namedConfTemplate")
-    ConfigKey<String> NAMED_CONF_TEMPLATE = ConfigKeys.newStringConfigKey(
-            "bind.template.named-conf", "The BIND named configuration file (as FreeMarker template)",
-            "classpath://brooklyn/entity/network/bind/named.conf");
-
-    @SetFromFlag("updateRootZonesFile")
-    ConfigKey<Boolean> UPDATE_ROOT_ZONES_FILE = ConfigKeys.newBooleanConfigKey(
-            "bind.updateRootZones", "Instructs the entity to fetch the latest root zones file from ftp.rs.internic.net.",
-            Boolean.FALSE);
-
-
-    /* Reverse lookup attributes. */
-
-    AttributeSensor<Cidr> REVERSE_LOOKUP_CIDR = Sensors.newSensor(Cidr.class,
-            "bind.reverse-lookup.cidr", "The network CIDR that hosts must have for reverse lookup entries " +
-            "to be added (default uses server address /24)");
-
-    AttributeSensor<String> REVERSE_LOOKUP_DOMAIN = Sensors.newStringSensor(
-            "bind.reverse-lookup.domain", "The in-addr.arpa reverse lookup domain name");
-
-
-    /* Configuration applicable to clients of the BIND DNS service. */
-
-    @SetFromFlag("replaceResolvConf")
-    ConfigKey<Boolean> REPLACE_RESOLV_CONF = ConfigKeys.newBooleanConfigKey(
-            "bind.resolv-conf.replce", "Set to replace resolv.conf with the template (default is to use eth0 script)", Boolean.FALSE);
-
-    @SetFromFlag("interfaceConfigTemplate")
-    ConfigKey<String> INTERFACE_CONFIG_TEMPLATE = ConfigKeys.newStringConfigKey(
-            "bind.template.interface-cfg", "The network interface configuration file for clients (as FreeMarker template)",
-            "classpath://brooklyn/entity/network/bind/ifcfg");
-
-    @SetFromFlag("interfaceConfigTemplate")
-    ConfigKey<String> RESOLV_CONF_TEMPLATE = ConfigKeys.newStringConfigKey(
-            "bind.template.resolv-conf", "The resolver configuration file for clients (as FreeMarker template)",
-            "classpath://brooklyn/entity/network/bind/resolv.conf");
-
-    AttributeSensor<DynamicGroup> ENTITIES = Sensors.newSensor(DynamicGroup.class,
-            "bind.entities", "The entities being managed by this server");
-
-    AttributeSensor<Multimap<String, String>> ADDRESS_MAPPINGS = Sensors.newSensor(new TypeToken<Multimap<String, String>>() {},
-            "bind.mappings", "All address mappings maintained by the server, in form address -> [names]");
-
-    AttributeSensor<Map<String, String>> A_RECORDS = Sensors.newSensor(new TypeToken<Map<String, String>>() {},
-            "bind.records.a", "All A records for the server, in form name -> address");
-
-    AttributeSensor<Multimap<String, String>> CNAME_RECORDS = Sensors.newSensor(new TypeToken<Multimap<String, String>>() {},
-            "bind.records.cname", "All CNAME records for the server, in form name -> [names]");
-
-    AttributeSensor<Map<String, String>> PTR_RECORDS = Sensors.newSensor(new TypeToken<Map<String, String>>() {},
-            "bind.records.ptr", "All PTR records for the server, in form address -> name. Entries will be in REVERSE_LOOKUP_CIDR. " +
-                    "Entries are guaranteed to have an inverse mapping in A_RECORDS.");
-
-    AttributeSensor<Long> SERIAL = Sensors.newLongSensor(
-            "bind.serial", "A serial number guaranteed to be valid for use in a modified domain.zone or reverse.zone file");
-
-    public Multimap<String, String> getAddressMappings();
-
-    /**
-     * @return the IP to hostname mappings stored in this DNS server's conf file
-     * @deprecated since 0.7.0 use {@link #PTR_RECORDS} instead.
-     */
-    @Deprecated
-    @Effector(description="Gets the IP to hostname mappings stored in this DNS server's conf file")
-    public Map<String, String> getReverseMappings();
-
-    /**
-     * @return the predicate used to filter entities for the Bind server to manage.
-     */
-    Predicate<? super Entity> getEntityFilter();
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/689b49c3/software/network/src/main/java/brooklyn/entity/network/bind/BindDnsServerDriver.java
----------------------------------------------------------------------
diff --git a/software/network/src/main/java/brooklyn/entity/network/bind/BindDnsServerDriver.java b/software/network/src/main/java/brooklyn/entity/network/bind/BindDnsServerDriver.java
deleted file mode 100644
index cbc9423..0000000
--- a/software/network/src/main/java/brooklyn/entity/network/bind/BindDnsServerDriver.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package brooklyn.entity.network.bind;
-
-import com.google.common.annotations.VisibleForTesting;
-
-import brooklyn.entity.basic.SoftwareProcessDriver;
-
-public interface BindDnsServerDriver extends SoftwareProcessDriver {
-
-    /**
-     * Uploads configuration files and restarts the service.
-     */
-    void updateBindConfiguration();
-
-    /**
-     * @return a support class appropriate for the machine the server is running on.
-     */
-    @VisibleForTesting
-    BindOsSupport getOsSupport();
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/689b49c3/software/network/src/main/java/brooklyn/entity/network/bind/BindDnsServerImpl.java
----------------------------------------------------------------------
diff --git a/software/network/src/main/java/brooklyn/entity/network/bind/BindDnsServerImpl.java b/software/network/src/main/java/brooklyn/entity/network/bind/BindDnsServerImpl.java
deleted file mode 100644
index 0b5331f..0000000
--- a/software/network/src/main/java/brooklyn/entity/network/bind/BindDnsServerImpl.java
+++ /dev/null
@@ -1,339 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package brooklyn.entity.network.bind;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-
-import java.io.ByteArrayInputStream;
-import java.io.StringReader;
-import java.util.Collection;
-import java.util.Map;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.google.common.base.CharMatcher;
-import com.google.common.base.Function;
-import com.google.common.base.Joiner;
-import com.google.common.base.Predicate;
-import com.google.common.base.Splitter;
-import com.google.common.collect.BiMap;
-import com.google.common.collect.FluentIterable;
-import com.google.common.collect.HashBiMap;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableListMultimap;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableMultimap;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Multimap;
-import com.google.common.collect.MultimapBuilder;
-import com.google.common.collect.Multimaps;
-
-import brooklyn.entity.Entity;
-import brooklyn.entity.basic.Attributes;
-import brooklyn.entity.basic.DynamicGroup;
-import brooklyn.entity.basic.Lifecycle;
-import brooklyn.entity.basic.SoftwareProcessImpl;
-import brooklyn.entity.group.AbstractMembershipTrackingPolicy;
-import brooklyn.entity.proxying.EntitySpec;
-import brooklyn.event.Sensor;
-import brooklyn.location.basic.Machines;
-import brooklyn.location.basic.SshMachineLocation;
-import brooklyn.policy.PolicySpec;
-import brooklyn.util.guava.Maybe;
-import brooklyn.util.net.Cidr;
-import brooklyn.util.ssh.BashCommands;
-import brooklyn.util.text.Strings;
-
-/**
- * This sets up a BIND DNS server.
- * <p>
- * <b>NOTE</b> This entity has only been certified on <i>CentOS</i>, <i>RHEL</i>,
- * <i>Ubuntu</i> and <i>Debian</i> operating systems.
- */
-public class BindDnsServerImpl extends SoftwareProcessImpl implements BindDnsServer {
-
-    private static final Logger LOG = LoggerFactory.getLogger(BindDnsServerImpl.class);
-    private final Object serialMutex = new Object();
-
-    // As per RFC 952 and RFC 1123.
-    private static final CharMatcher DOMAIN_NAME_FIRST_CHAR_MATCHER = CharMatcher.inRange('a', 'z')
-                .or(CharMatcher.inRange('A', 'Z'))
-                .or(CharMatcher.inRange('0', '9'));
-    private static final CharMatcher DOMAIN_NAME_MATCHER = DOMAIN_NAME_FIRST_CHAR_MATCHER
-            .or(CharMatcher.is('-'));
-
-
-    private class HostnameTransformer implements Function<Entity, String> {
-        @Override
-        public String apply(Entity input) {
-            String hostname = input.getAttribute(getConfig(HOSTNAME_SENSOR));
-            hostname = DOMAIN_NAME_FIRST_CHAR_MATCHER.negate().trimFrom(hostname);
-            hostname = DOMAIN_NAME_MATCHER.negate().trimAndCollapseFrom(hostname, '-');
-            if (hostname.length() > 63) {
-                hostname = hostname.substring(0, 63);
-            }
-            return hostname;
-        }
-    }
-
-    public BindDnsServerImpl() {
-        super();
-    }
-
-    @Override
-    public void init() {
-        super.init();
-        checkNotNull(getConfig(HOSTNAME_SENSOR), "%s requires value for %s", getClass().getName(), HOSTNAME_SENSOR);
-        DynamicGroup entities = addChild(EntitySpec.create(DynamicGroup.class)
-                .configure(DynamicGroup.ENTITY_FILTER, getEntityFilter()));
-        setAttribute(ENTITIES, entities);
-        setAttribute(A_RECORDS, ImmutableMap.<String, String>of());
-        setAttribute(CNAME_RECORDS, ImmutableMultimap.<String, String>of());
-        setAttribute(PTR_RECORDS, ImmutableMap.<String, String>of());
-        setAttribute(ADDRESS_MAPPINGS, ImmutableMultimap.<String, String>of());
-        synchronized (serialMutex) {
-            setAttribute(SERIAL, System.currentTimeMillis());
-        }
-    }
-
-    @Override
-    public void postRebind() {
-        update();
-    }
-
-    @Override
-    public Class<?> getDriverInterface() {
-        return BindDnsServerDriver.class;
-    }
-
-    @Override
-    public Multimap<String, String> getAddressMappings() {
-        return getAttribute(ADDRESS_MAPPINGS);
-    }
-
-    @Override
-    public Map<String, String> getReverseMappings() {
-        return getAttribute(PTR_RECORDS);
-    }
-
-    @Override
-    public BindDnsServerDriver getDriver() {
-        return (BindDnsServerDriver) super.getDriver();
-    }
-
-    @Override
-    public void connectSensors() {
-        connectServiceUpIsRunning();
-    }
-
-    @Override
-    public void disconnectSensors() {
-        super.disconnectSensors();
-        disconnectServiceUpIsRunning();
-    }
-
-    @Override
-    protected void preStart() {
-        String reverse = getConfig(REVERSE_LOOKUP_NETWORK);
-        if (Strings.isBlank(reverse)) reverse = getAttribute(ADDRESS);
-        setAttribute(REVERSE_LOOKUP_CIDR, new Cidr(reverse + "/24"));
-        String reverseLookupDomain = Joiner.on('.').join(Iterables.skip(Lists.reverse(Lists.newArrayList(
-                Splitter.on('.').split(reverse))), 1)) + ".in-addr.arpa";
-        setAttribute(REVERSE_LOOKUP_DOMAIN, reverseLookupDomain);
-
-        addPolicy(PolicySpec.create(MemberTrackingPolicy.class)
-                .displayName("Address tracker")
-                .configure(AbstractMembershipTrackingPolicy.SENSORS_TO_TRACK, ImmutableSet.<Sensor<?>>of(getConfig(HOSTNAME_SENSOR)))
-                .configure(AbstractMembershipTrackingPolicy.GROUP, getEntities()));
-    }
-
-    @Override
-    public void postStart() {
-        update();
-    }
-
-    public static class MemberTrackingPolicy extends AbstractMembershipTrackingPolicy {
-        @Override
-        protected void onEntityChange(Entity member) {
-            if (LOG.isTraceEnabled()) {
-                LOG.trace("State of {} on change: {}", member, member.getAttribute(Attributes.SERVICE_STATE_ACTUAL).name());
-            }
-            ((BindDnsServerImpl) entity).update();
-        }
-        @Override
-        protected void onEntityAdded(Entity member) {
-            if (LOG.isTraceEnabled()) {
-                LOG.trace("State of {} on added: {}", member, member.getAttribute(Attributes.SERVICE_STATE_ACTUAL).name());
-            }
-            ((BindDnsServerImpl) entity).configureResolver(member);
-        }
-    }
-
-    private class HasHostnameAndValidLifecycle implements Predicate<Entity> {
-        @Override
-        public boolean apply(Entity input) {
-            switch (input.getAttribute(Attributes.SERVICE_STATE_ACTUAL)) {
-            case STOPPED:
-            case STOPPING:
-            case DESTROYED:
-                return false;
-            }
-            return input.getAttribute(getConfig(HOSTNAME_SENSOR)) != null;
-        }
-    }
-
-    public void update() {
-        Lifecycle serverState = getAttribute(Attributes.SERVICE_STATE_ACTUAL);
-        if (Lifecycle.STOPPED.equals(serverState) || Lifecycle.STOPPING.equals(serverState)
-                || Lifecycle.DESTROYED.equals(serverState) || !getAttribute(Attributes.SERVICE_UP)) {
-            LOG.debug("Skipped update of {} when service state is {} and running is {}",
-                    new Object[]{this, getAttribute(Attributes.SERVICE_STATE_ACTUAL), getAttribute(SERVICE_UP)});
-            return;
-        }
-        synchronized (this) {
-            Iterable<Entity> availableEntities = FluentIterable.from(getEntities().getMembers())
-                    .filter(new HasHostnameAndValidLifecycle());
-            LOG.debug("{} updating with entities: {}", this, Iterables.toString(availableEntities));
-            ImmutableListMultimap<String, Entity> hostnameToEntity = Multimaps.index(availableEntities,
-                    new HostnameTransformer());
-
-            Map<String, String> octetToName = Maps.newHashMap();
-            BiMap<String, String> ipToARecord = HashBiMap.create();
-            Multimap<String, String> aRecordToCnames = MultimapBuilder.hashKeys().hashSetValues().build();
-            Multimap<String, String> ipToAllNames = MultimapBuilder.hashKeys().hashSetValues().build();
-
-            for (Map.Entry<String, Entity> e : hostnameToEntity.entries()) {
-                String domainName = e.getKey();
-                Maybe<SshMachineLocation> location = Machines.findUniqueSshMachineLocation(e.getValue().getLocations());
-                if (!location.isPresent()) {
-                    LOG.debug("Member {} of {} does not have an SSH location so will not be configured", e.getValue(), this);
-                    continue;
-                } else if (ipToARecord.inverse().containsKey(domainName)) {
-                    continue;
-                }
-
-                String address = location.get().getAddress().getHostAddress();
-                ipToAllNames.put(address, domainName);
-                if (!ipToARecord.containsKey(address)) {
-                    ipToARecord.put(address, domainName);
-                    if (getReverseLookupNetwork().contains(new Cidr(address + "/32"))) {
-                        String octet = Iterables.get(Splitter.on('.').split(address), 3);
-                        if (!octetToName.containsKey(octet)) octetToName.put(octet, domainName);
-                    }
-                } else {
-                    aRecordToCnames.put(ipToARecord.get(address), domainName);
-                }
-            }
-            setAttribute(A_RECORDS, ImmutableMap.copyOf(ipToARecord.inverse()));
-            setAttribute(PTR_RECORDS, ImmutableMap.copyOf(octetToName));
-            setAttribute(CNAME_RECORDS, Multimaps.unmodifiableMultimap(aRecordToCnames));
-            setAttribute(ADDRESS_MAPPINGS, Multimaps.unmodifiableMultimap(ipToAllNames));
-
-            // Update Bind configuration files and restart the service
-            getDriver().updateBindConfiguration();
-       }
-    }
-
-    protected void configureResolver(Entity entity) {
-        Maybe<SshMachineLocation> machine = Machines.findUniqueSshMachineLocation(entity.getLocations());
-        if (machine.isPresent()) {
-            if (getConfig(REPLACE_RESOLV_CONF)) {
-                machine.get().copyTo(new StringReader(getConfig(RESOLV_CONF_TEMPLATE)), "/etc/resolv.conf");
-            } else {
-                appendTemplate(getConfig(INTERFACE_CONFIG_TEMPLATE), "/etc/sysconfig/network-scripts/ifcfg-eth0", machine.get());
-                machine.get().execScript("reload network", ImmutableList.of(BashCommands.sudo("service network reload")));
-            }
-            LOG.info("configured resolver on {}", machine);
-        } else {
-            LOG.debug("{} can't configure resolver at {}: no SshMachineLocation", this, entity);
-        }
-    }
-
-    protected void appendTemplate(String template, String destination, SshMachineLocation machine) {
-        String content = ((BindDnsServerSshDriver) getDriver()).processTemplate(template);
-        String temp = "/tmp/template-" + Strings.makeRandomId(6);
-        machine.copyTo(new ByteArrayInputStream(content.getBytes()), temp);
-        machine.execScript("updating file", ImmutableList.of(
-                BashCommands.sudo(String.format("tee -a %s < %s", destination, temp)),
-                String.format("rm -f %s", temp)));
-    }
-
-
-    @Override
-    public Predicate<? super Entity> getEntityFilter() {
-        return getConfig(ENTITY_FILTER);
-    }
-
-    // Mostly used in templates
-    public String getManagementCidr() {
-        return getConfig(MANAGEMENT_CIDR);
-    }
-
-    public Integer getDnsPort() {
-        return getAttribute(DNS_PORT);
-    }
-
-    public String getDomainName() {
-        return getConfig(DOMAIN_NAME);
-    }
-
-    /**
-     * @return A serial number guaranteed to be valid for use in a modified domain.zone or reverse.zone file.
-     */
-    public long getSerial() {
-        synchronized (serialMutex) {
-            long next = getAttribute(SERIAL) + 1;
-            setAttribute(SERIAL, next);
-            return next;
-        }
-    }
-
-    public Cidr getReverseLookupNetwork() {
-        return getAttribute(REVERSE_LOOKUP_CIDR);
-    }
-
-    public String getReverseLookupDomain() {
-        return getAttribute(REVERSE_LOOKUP_DOMAIN);
-    }
-
-    public DynamicGroup getEntities() {
-        return getAttribute(ENTITIES);
-    }
-
-    public Map<String, String> getAddressRecords() {
-        return getAttribute(A_RECORDS);
-    }
-
-    public Multimap<String, String> getCanonicalNameRecords() {
-        return getAttribute(CNAME_RECORDS);
-    }
-
-    public Map<String, Collection<String>> getCnamesForTemplates() {
-        return getAttribute(CNAME_RECORDS).asMap();
-    }
-
-    public Map<String, String> getPointerRecords() {
-        return getAttribute(PTR_RECORDS);
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/689b49c3/software/network/src/main/java/brooklyn/entity/network/bind/BindDnsServerSshDriver.java
----------------------------------------------------------------------
diff --git a/software/network/src/main/java/brooklyn/entity/network/bind/BindDnsServerSshDriver.java b/software/network/src/main/java/brooklyn/entity/network/bind/BindDnsServerSshDriver.java
deleted file mode 100644
index 983be65..0000000
--- a/software/network/src/main/java/brooklyn/entity/network/bind/BindDnsServerSshDriver.java
+++ /dev/null
@@ -1,184 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package brooklyn.entity.network.bind;
-
-import java.io.ByteArrayInputStream;
-import java.util.List;
-import java.util.Map;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Lists;
-
-import brooklyn.entity.basic.AbstractSoftwareProcessSshDriver;
-import brooklyn.location.basic.SshMachineLocation;
-import brooklyn.util.collections.MutableMap;
-import brooklyn.util.net.Networking;
-import brooklyn.util.net.Protocol;
-import brooklyn.util.os.Os;
-import brooklyn.util.ssh.BashCommands;
-import brooklyn.util.ssh.IptablesCommands;
-import brooklyn.util.ssh.IptablesCommands.Chain;
-import brooklyn.util.ssh.IptablesCommands.Policy;
-import brooklyn.util.text.Strings;
-
-public class BindDnsServerSshDriver extends AbstractSoftwareProcessSshDriver implements BindDnsServerDriver {
-
-    private static final Logger LOG = LoggerFactory.getLogger(BindDnsServerSshDriver.class);
-    private volatile BindOsSupport osSupport;
-    private final Object osSupportMutex = new Object();
-
-    public BindDnsServerSshDriver(BindDnsServerImpl entity, SshMachineLocation machine) {
-        super(entity, machine);
-    }
-
-    @Override
-    public BindDnsServerImpl getEntity() {
-        return (BindDnsServerImpl) super.getEntity();
-    }
-
-    @Override
-    public void install() {
-        List<String> commands = ImmutableList.<String>builder()
-                .add(BashCommands.installPackage(MutableMap.of(
-                        "yum", "bind", "apt", "bind9"), "bind"))
-                .add(BashCommands.ok("which setenforce && " + BashCommands.sudo("setenforce 0")))
-                .build();
-        newScript(INSTALLING)
-                .failOnNonZeroResultCode()
-                .body.append(commands)
-                .execute();
-    }
-
-    @Override
-    public void customize() {
-        Integer dnsPort = getEntity().getDnsPort();
-        Map<String, Object> ports = MutableMap.<String, Object>of("dnsPort", dnsPort);
-        Networking.checkPortsValid(ports);
-
-        List<String> commands = Lists.newArrayList(
-                BashCommands.sudo("mkdir -p " + getDataDirectory() + " " + getDynamicDirectory() + " " + getOsSupport().getConfigDirectory()),
-                BashCommands.sudo("chown -R bind:bind " + getDataDirectory() + " " + getDynamicDirectory()),
-                // TODO determine name of ethernet interface if not eth0?
-                IptablesCommands.insertIptablesRule(Chain.INPUT, "eth0", Protocol.UDP, dnsPort, Policy.ACCEPT),
-                IptablesCommands.insertIptablesRule(Chain.INPUT, "eth0", Protocol.TCP, dnsPort, Policy.ACCEPT),
-                // TODO Iptables is not a service on Ubuntu
-                BashCommands.sudo("service iptables save"),
-                BashCommands.sudo("service iptables restart"));
-        if (getEntity().getConfig(BindDnsServer.UPDATE_ROOT_ZONES_FILE)) {
-            commands.add("wget --user=ftp --password=ftp ftp://ftp.rs.internic.net/domain/db.cache " +
-                    "-O " + getOsSupport().getRootZonesFile());
-        }
-        newScript(CUSTOMIZING)
-                .body.append(commands)
-                // fails if iptables is not a service, e.g. on ubuntu
-                //.failOnNonZeroResultCode()
-                .execute();
-
-        copyAsRoot("classpath://brooklyn/entity/network/bind/rfc1912.zone", getRfc1912ZonesFile());
-        copyAsRoot("classpath://brooklyn/entity/network/bind/named.localhost", Os.mergePathsUnix(getOsSupport().getConfigDirectory(), "named.localhost"));
-        copyAsRoot("classpath://brooklyn/entity/network/bind/named.loopback", Os.mergePathsUnix(getOsSupport().getConfigDirectory(), "named.loopback"));
-        copyAsRoot("classpath://brooklyn/entity/network/bind/named.empty", Os.mergePathsUnix(getOsSupport().getConfigDirectory(), "named.empty"));
-
-        newScript("Checking BIND configuration")
-                .body.append(BashCommands.sudo("named-checkconf"))
-                .failOnNonZeroResultCode()
-                .execute();
-    }
-
-    @Override
-    public void launch() {
-        newScript(MutableMap.of("usePidFile", false), LAUNCHING)
-                .body.append(BashCommands.sudo("service " + getOsSupport().getServiceName() + " start"))
-                .execute();
-    }
-
-    @Override
-    public boolean isRunning() {
-        return newScript(MutableMap.of("usePidFile", false), CHECK_RUNNING)
-                .body.append(BashCommands.sudo("service " + getOsSupport().getServiceName() + " status"))
-                .execute() == 0;
-    }
-
-    @Override
-    public void stop() {
-        newScript(MutableMap.of("usePidFile", false), STOPPING)
-                .body.append(BashCommands.sudo("service " + getOsSupport().getServiceName() + " stop"))
-                .execute();
-    }
-
-    @Override
-    public void updateBindConfiguration() {
-        LOG.debug("Updating bind configuration at " + getMachine());
-        copyAsRoot(entity.getConfig(BindDnsServer.NAMED_CONF_TEMPLATE), getOsSupport().getRootConfigFile());
-        copyAsRoot(entity.getConfig(BindDnsServer.DOMAIN_ZONE_FILE_TEMPLATE), getDomainZoneFile());
-        copyAsRoot(entity.getConfig(BindDnsServer.REVERSE_ZONE_FILE_TEMPLATE), getReverseZoneFile());
-        int result = getMachine().execScript("restart bind",
-                ImmutableList.of(BashCommands.sudo("service " + getOsSupport().getServiceName() + " restart")));
-        LOG.info("Updated named configuration and zone file for '{}' on {} (exit code {}).",
-                new Object[]{entity.getConfig(BindDnsServer.DOMAIN_NAME), entity, result});
-    }
-
-    private void copyAsRoot(String template, String destination) {
-        String content = processTemplate(template);
-        String temp = "/tmp/template-" + Strings.makeRandomId(6);
-        getMachine().copyTo(new ByteArrayInputStream(content.getBytes()), temp);
-        getMachine().execScript("copying file", ImmutableList.of(BashCommands.sudo(String.format("mv %s %s", temp, destination))));
-    }
-
-    /** @return The location on the server of the domain zone file */
-    public String getDomainZoneFile() {
-        return Os.mergePaths(getOsSupport().getConfigDirectory(), "domain.zone");
-    }
-
-    /** @return The location on the server of the reverse zone file */
-    public String getReverseZoneFile() {
-        return Os.mergePaths(getOsSupport().getConfigDirectory(), "reverse.zone");
-    }
-
-    public String getDataDirectory() {
-        return Os.mergePaths(getOsSupport().getWorkingDirectory(), "data");
-    }
-
-    public String getDynamicDirectory() {
-        return Os.mergePaths(getOsSupport().getWorkingDirectory(), "dynamic");
-    }
-
-    public String getRfc1912ZonesFile() {
-        return Os.mergePaths(getOsSupport().getConfigDirectory(), "rfc1912.zone");
-    }
-
-    public BindOsSupport getOsSupport() {
-        BindOsSupport result = osSupport;
-        if (result == null) {
-            synchronized (osSupportMutex) {
-                result = osSupport;
-                if (result == null) {
-                    boolean yumExists = newScript("testing for yum")
-                            .body.append(BashCommands.requireExecutable("yum"))
-                            .execute() == 0;
-                    osSupport = result = yumExists ? BindOsSupport.forRhel() : BindOsSupport.forDebian();
-                }
-            }
-        }
-        return result;
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/689b49c3/software/network/src/main/java/brooklyn/entity/network/bind/BindOsSupport.java
----------------------------------------------------------------------
diff --git a/software/network/src/main/java/brooklyn/entity/network/bind/BindOsSupport.java b/software/network/src/main/java/brooklyn/entity/network/bind/BindOsSupport.java
deleted file mode 100644
index 8ef5f83..0000000
--- a/software/network/src/main/java/brooklyn/entity/network/bind/BindOsSupport.java
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package brooklyn.entity.network.bind;
-
-import javax.annotation.concurrent.Immutable;
-
-/**
- * Provides operating system-specific information for working with the Bind service.
- */
-// Class would be package-private if Freemarker didn't complain vociferously.
-@Immutable
-public class BindOsSupport {
-
-    // Likewise would make these package-private and have no getters if Freemarker was ok with it.
-    private final String packageName;
-    private final String serviceName;
-    private final String rootConfigFile;
-    private final String configDirectory;
-    private final String workingDirectory;
-    private final String rootZonesFile;
-    private final String keysFile;
-
-    private BindOsSupport(
-            String packageName,
-            String serviceName,
-            String rootConfigFile,
-            String configDirectory,
-            String workingDirectory,
-            String rootZonesFile,
-            String keysFile) {
-        this.packageName = packageName;
-        this.serviceName = serviceName;
-        this.rootConfigFile = rootConfigFile;
-        this.configDirectory = configDirectory;
-        this.workingDirectory = workingDirectory;
-        this.rootZonesFile = rootZonesFile;
-        this.keysFile = keysFile;
-    }
-
-    /**
-     * @return support for RHEL-based operating systems.
-     */
-    public static BindOsSupport forRhel() {
-        return new BindOsSupport(
-                "bind",
-                "named",
-                "/etc/named.conf",
-                "/var/named",
-                "/var/named/data",
-                "/var/named/named.ca",
-                "/etc/named.iscdlv.key");
-    }
-
-    /**
-     * @return support for Debian-based operating systems.
-     */
-    public static BindOsSupport forDebian() {
-        return new BindOsSupport(
-                "bind9",
-                "bind9",
-                "/etc/bind/named.conf",
-                "/etc/bind",
-                "/var/cache/bind",
-                "/etc/bind/db.root",
-                "/etc/bind/bind.keys"
-        );
-    }
-
-    public String getPackageName() {
-        return packageName;
-    }
-
-    public String getServiceName() {
-        return serviceName;
-    }
-
-    public String getRootConfigFile() {
-        return rootConfigFile;
-    }
-
-    public String getConfigDirectory() {
-        return configDirectory;
-    }
-
-    public String getWorkingDirectory() {
-        return workingDirectory;
-    }
-
-    public String getRootZonesFile() {
-        return rootZonesFile;
-    }
-
-    public String getKeysFile() {
-        return keysFile;
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/689b49c3/software/network/src/main/java/org/apache/brooklyn/entity/network/bind/BindDnsServer.java
----------------------------------------------------------------------
diff --git a/software/network/src/main/java/org/apache/brooklyn/entity/network/bind/BindDnsServer.java b/software/network/src/main/java/org/apache/brooklyn/entity/network/bind/BindDnsServer.java
new file mode 100644
index 0000000..8534440
--- /dev/null
+++ b/software/network/src/main/java/org/apache/brooklyn/entity/network/bind/BindDnsServer.java
@@ -0,0 +1,156 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.entity.network.bind;
+
+import java.util.Map;
+
+import com.google.common.base.Predicate;
+import com.google.common.base.Predicates;
+import com.google.common.collect.Multimap;
+import com.google.common.reflect.TypeToken;
+
+import org.apache.brooklyn.catalog.Catalog;
+import brooklyn.config.ConfigKey;
+import brooklyn.entity.Entity;
+import brooklyn.entity.annotation.Effector;
+import brooklyn.entity.basic.ConfigKeys;
+import brooklyn.entity.basic.DynamicGroup;
+import brooklyn.entity.basic.SoftwareProcess;
+import brooklyn.entity.proxying.ImplementedBy;
+import brooklyn.event.AttributeSensor;
+import brooklyn.event.basic.PortAttributeSensorAndConfigKey;
+import brooklyn.event.basic.Sensors;
+import brooklyn.location.basic.PortRanges;
+import brooklyn.util.flags.SetFromFlag;
+import brooklyn.util.net.Cidr;
+
+/**
+ * This sets up a BIND DNS server.
+ */
+@Catalog(name="BIND", description="BIND is an Internet Domain Name Server.", iconUrl="classpath:///isc-logo.png")
+@ImplementedBy(BindDnsServerImpl.class)
+public interface BindDnsServer extends SoftwareProcess {
+
+    @SetFromFlag("filter")
+    ConfigKey<Predicate<? super Entity>> ENTITY_FILTER = ConfigKeys.newConfigKey(new TypeToken<Predicate<? super Entity>>() {},
+            "bind.entity.filter", "Filter for entities which will use the BIND DNS service for name resolution." +
+                    "Default is all instances of SoftwareProcess in the application.",
+            Predicates.instanceOf(SoftwareProcess.class));
+
+    @SetFromFlag("domainName")
+    ConfigKey<String> DOMAIN_NAME = ConfigKeys.newStringConfigKey(
+            "bind.domain.name", "The DNS domain name to serve", "brooklyn.local");
+
+    @SetFromFlag("reverseLookupNetwork")
+    ConfigKey<String> REVERSE_LOOKUP_NETWORK = ConfigKeys.newStringConfigKey(
+            "bind.reverse-lookup.address", "Network address for reverse lookup zone");
+
+    @SetFromFlag("subnet")
+    ConfigKey<String> MANAGEMENT_CIDR = ConfigKeys.newStringConfigKey(
+            "bind.access.cidr", "Subnet CIDR or ACL allowed to access DNS", "0.0.0.0/0");
+
+    @SetFromFlag("hostnameSensor")
+    ConfigKey<AttributeSensor<String>> HOSTNAME_SENSOR = ConfigKeys.newConfigKey(new TypeToken<AttributeSensor<String>>() {},
+            "bind.sensor.hostname", "Sensor on managed entities that reports the hostname");
+
+    PortAttributeSensorAndConfigKey DNS_PORT =
+            new PortAttributeSensorAndConfigKey("bind.port", "BIND DNS port for TCP and UDP", PortRanges.fromString("53"));
+
+    @SetFromFlag("zoneFileTemplate")
+    ConfigKey<String> DOMAIN_ZONE_FILE_TEMPLATE = ConfigKeys.newStringConfigKey(
+            "bind.template.domain-zone", "The BIND domain zone file to serve (as FreeMarker template)",
+            "classpath://org/apache/brooklyn/entity/network/bind/domain.zone");
+
+    @SetFromFlag("reverseZoneFileTemplate")
+    ConfigKey<String> REVERSE_ZONE_FILE_TEMPLATE = ConfigKeys.newStringConfigKey(
+            "bind.template.reverse-zone", "The BIND reverse lookup zone file to serve (as FreeMarker template)",
+            "classpath://org/apache/brooklyn/entity/network/bind/reverse.zone");
+
+    @SetFromFlag("namedConfTemplate")
+    ConfigKey<String> NAMED_CONF_TEMPLATE = ConfigKeys.newStringConfigKey(
+            "bind.template.named-conf", "The BIND named configuration file (as FreeMarker template)",
+            "classpath://org/apache/brooklyn/entity/network/bind/named.conf");
+
+    @SetFromFlag("updateRootZonesFile")
+    ConfigKey<Boolean> UPDATE_ROOT_ZONES_FILE = ConfigKeys.newBooleanConfigKey(
+            "bind.updateRootZones", "Instructs the entity to fetch the latest root zones file from ftp.rs.internic.net.",
+            Boolean.FALSE);
+
+
+    /* Reverse lookup attributes. */
+
+    AttributeSensor<Cidr> REVERSE_LOOKUP_CIDR = Sensors.newSensor(Cidr.class,
+            "bind.reverse-lookup.cidr", "The network CIDR that hosts must have for reverse lookup entries " +
+            "to be added (default uses server address /24)");
+
+    AttributeSensor<String> REVERSE_LOOKUP_DOMAIN = Sensors.newStringSensor(
+            "bind.reverse-lookup.domain", "The in-addr.arpa reverse lookup domain name");
+
+
+    /* Configuration applicable to clients of the BIND DNS service. */
+
+    @SetFromFlag("replaceResolvConf")
+    ConfigKey<Boolean> REPLACE_RESOLV_CONF = ConfigKeys.newBooleanConfigKey(
+            "bind.resolv-conf.replce", "Set to replace resolv.conf with the template (default is to use eth0 script)", Boolean.FALSE);
+
+    @SetFromFlag("interfaceConfigTemplate")
+    ConfigKey<String> INTERFACE_CONFIG_TEMPLATE = ConfigKeys.newStringConfigKey(
+            "bind.template.interface-cfg", "The network interface configuration file for clients (as FreeMarker template)",
+            "classpath://org/apache/brooklyn/entity/network/bind/ifcfg");
+
+    @SetFromFlag("interfaceConfigTemplate")
+    ConfigKey<String> RESOLV_CONF_TEMPLATE = ConfigKeys.newStringConfigKey(
+            "bind.template.resolv-conf", "The resolver configuration file for clients (as FreeMarker template)",
+            "classpath://org/apache/brooklyn/entity/network/bind/resolv.conf");
+
+    AttributeSensor<DynamicGroup> ENTITIES = Sensors.newSensor(DynamicGroup.class,
+            "bind.entities", "The entities being managed by this server");
+
+    AttributeSensor<Multimap<String, String>> ADDRESS_MAPPINGS = Sensors.newSensor(new TypeToken<Multimap<String, String>>() {},
+            "bind.mappings", "All address mappings maintained by the server, in form address -> [names]");
+
+    AttributeSensor<Map<String, String>> A_RECORDS = Sensors.newSensor(new TypeToken<Map<String, String>>() {},
+            "bind.records.a", "All A records for the server, in form name -> address");
+
+    AttributeSensor<Multimap<String, String>> CNAME_RECORDS = Sensors.newSensor(new TypeToken<Multimap<String, String>>() {},
+            "bind.records.cname", "All CNAME records for the server, in form name -> [names]");
+
+    AttributeSensor<Map<String, String>> PTR_RECORDS = Sensors.newSensor(new TypeToken<Map<String, String>>() {},
+            "bind.records.ptr", "All PTR records for the server, in form address -> name. Entries will be in REVERSE_LOOKUP_CIDR. " +
+                    "Entries are guaranteed to have an inverse mapping in A_RECORDS.");
+
+    AttributeSensor<Long> SERIAL = Sensors.newLongSensor(
+            "bind.serial", "A serial number guaranteed to be valid for use in a modified domain.zone or reverse.zone file");
+
+    public Multimap<String, String> getAddressMappings();
+
+    /**
+     * @return the IP to hostname mappings stored in this DNS server's conf file
+     * @deprecated since 0.7.0 use {@link #PTR_RECORDS} instead.
+     */
+    @Deprecated
+    @Effector(description="Gets the IP to hostname mappings stored in this DNS server's conf file")
+    public Map<String, String> getReverseMappings();
+
+    /**
+     * @return the predicate used to filter entities for the Bind server to manage.
+     */
+    Predicate<? super Entity> getEntityFilter();
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/689b49c3/software/network/src/main/java/org/apache/brooklyn/entity/network/bind/BindDnsServerDriver.java
----------------------------------------------------------------------
diff --git a/software/network/src/main/java/org/apache/brooklyn/entity/network/bind/BindDnsServerDriver.java b/software/network/src/main/java/org/apache/brooklyn/entity/network/bind/BindDnsServerDriver.java
new file mode 100644
index 0000000..50a1994
--- /dev/null
+++ b/software/network/src/main/java/org/apache/brooklyn/entity/network/bind/BindDnsServerDriver.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.entity.network.bind;
+
+import com.google.common.annotations.VisibleForTesting;
+
+import brooklyn.entity.basic.SoftwareProcessDriver;
+
+public interface BindDnsServerDriver extends SoftwareProcessDriver {
+
+    /**
+     * Uploads configuration files and restarts the service.
+     */
+    void updateBindConfiguration();
+
+    /**
+     * @return a support class appropriate for the machine the server is running on.
+     */
+    @VisibleForTesting
+    BindOsSupport getOsSupport();
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/689b49c3/software/network/src/main/java/org/apache/brooklyn/entity/network/bind/BindDnsServerImpl.java
----------------------------------------------------------------------
diff --git a/software/network/src/main/java/org/apache/brooklyn/entity/network/bind/BindDnsServerImpl.java b/software/network/src/main/java/org/apache/brooklyn/entity/network/bind/BindDnsServerImpl.java
new file mode 100644
index 0000000..05d0787
--- /dev/null
+++ b/software/network/src/main/java/org/apache/brooklyn/entity/network/bind/BindDnsServerImpl.java
@@ -0,0 +1,339 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.entity.network.bind;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.io.ByteArrayInputStream;
+import java.io.StringReader;
+import java.util.Collection;
+import java.util.Map;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.CharMatcher;
+import com.google.common.base.Function;
+import com.google.common.base.Joiner;
+import com.google.common.base.Predicate;
+import com.google.common.base.Splitter;
+import com.google.common.collect.BiMap;
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.HashBiMap;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableListMultimap;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableMultimap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Multimap;
+import com.google.common.collect.MultimapBuilder;
+import com.google.common.collect.Multimaps;
+
+import brooklyn.entity.Entity;
+import brooklyn.entity.basic.Attributes;
+import brooklyn.entity.basic.DynamicGroup;
+import brooklyn.entity.basic.Lifecycle;
+import brooklyn.entity.basic.SoftwareProcessImpl;
+import brooklyn.entity.group.AbstractMembershipTrackingPolicy;
+import brooklyn.entity.proxying.EntitySpec;
+import brooklyn.event.Sensor;
+import brooklyn.location.basic.Machines;
+import brooklyn.location.basic.SshMachineLocation;
+import brooklyn.policy.PolicySpec;
+import brooklyn.util.guava.Maybe;
+import brooklyn.util.net.Cidr;
+import brooklyn.util.ssh.BashCommands;
+import brooklyn.util.text.Strings;
+
+/**
+ * This sets up a BIND DNS server.
+ * <p>
+ * <b>NOTE</b> This entity has only been certified on <i>CentOS</i>, <i>RHEL</i>,
+ * <i>Ubuntu</i> and <i>Debian</i> operating systems.
+ */
+public class BindDnsServerImpl extends SoftwareProcessImpl implements BindDnsServer {
+
+    private static final Logger LOG = LoggerFactory.getLogger(BindDnsServerImpl.class);
+    private final Object serialMutex = new Object();
+
+    // As per RFC 952 and RFC 1123.
+    private static final CharMatcher DOMAIN_NAME_FIRST_CHAR_MATCHER = CharMatcher.inRange('a', 'z')
+                .or(CharMatcher.inRange('A', 'Z'))
+                .or(CharMatcher.inRange('0', '9'));
+    private static final CharMatcher DOMAIN_NAME_MATCHER = DOMAIN_NAME_FIRST_CHAR_MATCHER
+            .or(CharMatcher.is('-'));
+
+
+    private class HostnameTransformer implements Function<Entity, String> {
+        @Override
+        public String apply(Entity input) {
+            String hostname = input.getAttribute(getConfig(HOSTNAME_SENSOR));
+            hostname = DOMAIN_NAME_FIRST_CHAR_MATCHER.negate().trimFrom(hostname);
+            hostname = DOMAIN_NAME_MATCHER.negate().trimAndCollapseFrom(hostname, '-');
+            if (hostname.length() > 63) {
+                hostname = hostname.substring(0, 63);
+            }
+            return hostname;
+        }
+    }
+
+    public BindDnsServerImpl() {
+        super();
+    }
+
+    @Override
+    public void init() {
+        super.init();
+        checkNotNull(getConfig(HOSTNAME_SENSOR), "%s requires value for %s", getClass().getName(), HOSTNAME_SENSOR);
+        DynamicGroup entities = addChild(EntitySpec.create(DynamicGroup.class)
+                .configure(DynamicGroup.ENTITY_FILTER, getEntityFilter()));
+        setAttribute(ENTITIES, entities);
+        setAttribute(A_RECORDS, ImmutableMap.<String, String>of());
+        setAttribute(CNAME_RECORDS, ImmutableMultimap.<String, String>of());
+        setAttribute(PTR_RECORDS, ImmutableMap.<String, String>of());
+        setAttribute(ADDRESS_MAPPINGS, ImmutableMultimap.<String, String>of());
+        synchronized (serialMutex) {
+            setAttribute(SERIAL, System.currentTimeMillis());
+        }
+    }
+
+    @Override
+    public void postRebind() {
+        update();
+    }
+
+    @Override
+    public Class<?> getDriverInterface() {
+        return BindDnsServerDriver.class;
+    }
+
+    @Override
+    public Multimap<String, String> getAddressMappings() {
+        return getAttribute(ADDRESS_MAPPINGS);
+    }
+
+    @Override
+    public Map<String, String> getReverseMappings() {
+        return getAttribute(PTR_RECORDS);
+    }
+
+    @Override
+    public BindDnsServerDriver getDriver() {
+        return (BindDnsServerDriver) super.getDriver();
+    }
+
+    @Override
+    public void connectSensors() {
+        connectServiceUpIsRunning();
+    }
+
+    @Override
+    public void disconnectSensors() {
+        super.disconnectSensors();
+        disconnectServiceUpIsRunning();
+    }
+
+    @Override
+    protected void preStart() {
+        String reverse = getConfig(REVERSE_LOOKUP_NETWORK);
+        if (Strings.isBlank(reverse)) reverse = getAttribute(ADDRESS);
+        setAttribute(REVERSE_LOOKUP_CIDR, new Cidr(reverse + "/24"));
+        String reverseLookupDomain = Joiner.on('.').join(Iterables.skip(Lists.reverse(Lists.newArrayList(
+                Splitter.on('.').split(reverse))), 1)) + ".in-addr.arpa";
+        setAttribute(REVERSE_LOOKUP_DOMAIN, reverseLookupDomain);
+
+        addPolicy(PolicySpec.create(MemberTrackingPolicy.class)
+                .displayName("Address tracker")
+                .configure(AbstractMembershipTrackingPolicy.SENSORS_TO_TRACK, ImmutableSet.<Sensor<?>>of(getConfig(HOSTNAME_SENSOR)))
+                .configure(AbstractMembershipTrackingPolicy.GROUP, getEntities()));
+    }
+
+    @Override
+    public void postStart() {
+        update();
+    }
+
+    public static class MemberTrackingPolicy extends AbstractMembershipTrackingPolicy {
+        @Override
+        protected void onEntityChange(Entity member) {
+            if (LOG.isTraceEnabled()) {
+                LOG.trace("State of {} on change: {}", member, member.getAttribute(Attributes.SERVICE_STATE_ACTUAL).name());
+            }
+            ((BindDnsServerImpl) entity).update();
+        }
+        @Override
+        protected void onEntityAdded(Entity member) {
+            if (LOG.isTraceEnabled()) {
+                LOG.trace("State of {} on added: {}", member, member.getAttribute(Attributes.SERVICE_STATE_ACTUAL).name());
+            }
+            ((BindDnsServerImpl) entity).configureResolver(member);
+        }
+    }
+
+    private class HasHostnameAndValidLifecycle implements Predicate<Entity> {
+        @Override
+        public boolean apply(Entity input) {
+            switch (input.getAttribute(Attributes.SERVICE_STATE_ACTUAL)) {
+            case STOPPED:
+            case STOPPING:
+            case DESTROYED:
+                return false;
+            }
+            return input.getAttribute(getConfig(HOSTNAME_SENSOR)) != null;
+        }
+    }
+
+    public void update() {
+        Lifecycle serverState = getAttribute(Attributes.SERVICE_STATE_ACTUAL);
+        if (Lifecycle.STOPPED.equals(serverState) || Lifecycle.STOPPING.equals(serverState)
+                || Lifecycle.DESTROYED.equals(serverState) || !getAttribute(Attributes.SERVICE_UP)) {
+            LOG.debug("Skipped update of {} when service state is {} and running is {}",
+                    new Object[]{this, getAttribute(Attributes.SERVICE_STATE_ACTUAL), getAttribute(SERVICE_UP)});
+            return;
+        }
+        synchronized (this) {
+            Iterable<Entity> availableEntities = FluentIterable.from(getEntities().getMembers())
+                    .filter(new HasHostnameAndValidLifecycle());
+            LOG.debug("{} updating with entities: {}", this, Iterables.toString(availableEntities));
+            ImmutableListMultimap<String, Entity> hostnameToEntity = Multimaps.index(availableEntities,
+                    new HostnameTransformer());
+
+            Map<String, String> octetToName = Maps.newHashMap();
+            BiMap<String, String> ipToARecord = HashBiMap.create();
+            Multimap<String, String> aRecordToCnames = MultimapBuilder.hashKeys().hashSetValues().build();
+            Multimap<String, String> ipToAllNames = MultimapBuilder.hashKeys().hashSetValues().build();
+
+            for (Map.Entry<String, Entity> e : hostnameToEntity.entries()) {
+                String domainName = e.getKey();
+                Maybe<SshMachineLocation> location = Machines.findUniqueSshMachineLocation(e.getValue().getLocations());
+                if (!location.isPresent()) {
+                    LOG.debug("Member {} of {} does not have an SSH location so will not be configured", e.getValue(), this);
+                    continue;
+                } else if (ipToARecord.inverse().containsKey(domainName)) {
+                    continue;
+                }
+
+                String address = location.get().getAddress().getHostAddress();
+                ipToAllNames.put(address, domainName);
+                if (!ipToARecord.containsKey(address)) {
+                    ipToARecord.put(address, domainName);
+                    if (getReverseLookupNetwork().contains(new Cidr(address + "/32"))) {
+                        String octet = Iterables.get(Splitter.on('.').split(address), 3);
+                        if (!octetToName.containsKey(octet)) octetToName.put(octet, domainName);
+                    }
+                } else {
+                    aRecordToCnames.put(ipToARecord.get(address), domainName);
+                }
+            }
+            setAttribute(A_RECORDS, ImmutableMap.copyOf(ipToARecord.inverse()));
+            setAttribute(PTR_RECORDS, ImmutableMap.copyOf(octetToName));
+            setAttribute(CNAME_RECORDS, Multimaps.unmodifiableMultimap(aRecordToCnames));
+            setAttribute(ADDRESS_MAPPINGS, Multimaps.unmodifiableMultimap(ipToAllNames));
+
+            // Update Bind configuration files and restart the service
+            getDriver().updateBindConfiguration();
+       }
+    }
+
+    protected void configureResolver(Entity entity) {
+        Maybe<SshMachineLocation> machine = Machines.findUniqueSshMachineLocation(entity.getLocations());
+        if (machine.isPresent()) {
+            if (getConfig(REPLACE_RESOLV_CONF)) {
+                machine.get().copyTo(new StringReader(getConfig(RESOLV_CONF_TEMPLATE)), "/etc/resolv.conf");
+            } else {
+                appendTemplate(getConfig(INTERFACE_CONFIG_TEMPLATE), "/etc/sysconfig/network-scripts/ifcfg-eth0", machine.get());
+                machine.get().execScript("reload network", ImmutableList.of(BashCommands.sudo("service network reload")));
+            }
+            LOG.info("configured resolver on {}", machine);
+        } else {
+            LOG.debug("{} can't configure resolver at {}: no SshMachineLocation", this, entity);
+        }
+    }
+
+    protected void appendTemplate(String template, String destination, SshMachineLocation machine) {
+        String content = ((BindDnsServerSshDriver) getDriver()).processTemplate(template);
+        String temp = "/tmp/template-" + Strings.makeRandomId(6);
+        machine.copyTo(new ByteArrayInputStream(content.getBytes()), temp);
+        machine.execScript("updating file", ImmutableList.of(
+                BashCommands.sudo(String.format("tee -a %s < %s", destination, temp)),
+                String.format("rm -f %s", temp)));
+    }
+
+
+    @Override
+    public Predicate<? super Entity> getEntityFilter() {
+        return getConfig(ENTITY_FILTER);
+    }
+
+    // Mostly used in templates
+    public String getManagementCidr() {
+        return getConfig(MANAGEMENT_CIDR);
+    }
+
+    public Integer getDnsPort() {
+        return getAttribute(DNS_PORT);
+    }
+
+    public String getDomainName() {
+        return getConfig(DOMAIN_NAME);
+    }
+
+    /**
+     * @return A serial number guaranteed to be valid for use in a modified domain.zone or reverse.zone file.
+     */
+    public long getSerial() {
+        synchronized (serialMutex) {
+            long next = getAttribute(SERIAL) + 1;
+            setAttribute(SERIAL, next);
+            return next;
+        }
+    }
+
+    public Cidr getReverseLookupNetwork() {
+        return getAttribute(REVERSE_LOOKUP_CIDR);
+    }
+
+    public String getReverseLookupDomain() {
+        return getAttribute(REVERSE_LOOKUP_DOMAIN);
+    }
+
+    public DynamicGroup getEntities() {
+        return getAttribute(ENTITIES);
+    }
+
+    public Map<String, String> getAddressRecords() {
+        return getAttribute(A_RECORDS);
+    }
+
+    public Multimap<String, String> getCanonicalNameRecords() {
+        return getAttribute(CNAME_RECORDS);
+    }
+
+    public Map<String, Collection<String>> getCnamesForTemplates() {
+        return getAttribute(CNAME_RECORDS).asMap();
+    }
+
+    public Map<String, String> getPointerRecords() {
+        return getAttribute(PTR_RECORDS);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/689b49c3/software/network/src/main/java/org/apache/brooklyn/entity/network/bind/BindDnsServerSshDriver.java
----------------------------------------------------------------------
diff --git a/software/network/src/main/java/org/apache/brooklyn/entity/network/bind/BindDnsServerSshDriver.java b/software/network/src/main/java/org/apache/brooklyn/entity/network/bind/BindDnsServerSshDriver.java
new file mode 100644
index 0000000..09451c6
--- /dev/null
+++ b/software/network/src/main/java/org/apache/brooklyn/entity/network/bind/BindDnsServerSshDriver.java
@@ -0,0 +1,184 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.entity.network.bind;
+
+import java.io.ByteArrayInputStream;
+import java.util.List;
+import java.util.Map;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+
+import brooklyn.entity.basic.AbstractSoftwareProcessSshDriver;
+import brooklyn.location.basic.SshMachineLocation;
+import brooklyn.util.collections.MutableMap;
+import brooklyn.util.net.Networking;
+import brooklyn.util.net.Protocol;
+import brooklyn.util.os.Os;
+import brooklyn.util.ssh.BashCommands;
+import brooklyn.util.ssh.IptablesCommands;
+import brooklyn.util.ssh.IptablesCommands.Chain;
+import brooklyn.util.ssh.IptablesCommands.Policy;
+import brooklyn.util.text.Strings;
+
+public class BindDnsServerSshDriver extends AbstractSoftwareProcessSshDriver implements BindDnsServerDriver {
+
+    private static final Logger LOG = LoggerFactory.getLogger(BindDnsServerSshDriver.class);
+    private volatile BindOsSupport osSupport;
+    private final Object osSupportMutex = new Object();
+
+    public BindDnsServerSshDriver(BindDnsServerImpl entity, SshMachineLocation machine) {
+        super(entity, machine);
+    }
+
+    @Override
+    public BindDnsServerImpl getEntity() {
+        return (BindDnsServerImpl) super.getEntity();
+    }
+
+    @Override
+    public void install() {
+        List<String> commands = ImmutableList.<String>builder()
+                .add(BashCommands.installPackage(MutableMap.of(
+                        "yum", "bind", "apt", "bind9"), "bind"))
+                .add(BashCommands.ok("which setenforce && " + BashCommands.sudo("setenforce 0")))
+                .build();
+        newScript(INSTALLING)
+                .failOnNonZeroResultCode()
+                .body.append(commands)
+                .execute();
+    }
+
+    @Override
+    public void customize() {
+        Integer dnsPort = getEntity().getDnsPort();
+        Map<String, Object> ports = MutableMap.<String, Object>of("dnsPort", dnsPort);
+        Networking.checkPortsValid(ports);
+
+        List<String> commands = Lists.newArrayList(
+                BashCommands.sudo("mkdir -p " + getDataDirectory() + " " + getDynamicDirectory() + " " + getOsSupport().getConfigDirectory()),
+                BashCommands.sudo("chown -R bind:bind " + getDataDirectory() + " " + getDynamicDirectory()),
+                // TODO determine name of ethernet interface if not eth0?
+                IptablesCommands.insertIptablesRule(Chain.INPUT, "eth0", Protocol.UDP, dnsPort, Policy.ACCEPT),
+                IptablesCommands.insertIptablesRule(Chain.INPUT, "eth0", Protocol.TCP, dnsPort, Policy.ACCEPT),
+                // TODO Iptables is not a service on Ubuntu
+                BashCommands.sudo("service iptables save"),
+                BashCommands.sudo("service iptables restart"));
+        if (getEntity().getConfig(BindDnsServer.UPDATE_ROOT_ZONES_FILE)) {
+            commands.add("wget --user=ftp --password=ftp ftp://ftp.rs.internic.net/domain/db.cache " +
+                    "-O " + getOsSupport().getRootZonesFile());
+        }
+        newScript(CUSTOMIZING)
+                .body.append(commands)
+                // fails if iptables is not a service, e.g. on ubuntu
+                //.failOnNonZeroResultCode()
+                .execute();
+
+        copyAsRoot("classpath://org/apache/brooklyn/entity/network/bind/rfc1912.zone", getRfc1912ZonesFile());
+        copyAsRoot("classpath://org/apache/brooklyn/entity/network/bind/named.localhost", Os.mergePathsUnix(getOsSupport().getConfigDirectory(), "named.localhost"));
+        copyAsRoot("classpath://org/apache/brooklyn/entity/network/bind/named.loopback", Os.mergePathsUnix(getOsSupport().getConfigDirectory(), "named.loopback"));
+        copyAsRoot("classpath://org/apache/brooklyn/entity/network/bind/named.empty", Os.mergePathsUnix(getOsSupport().getConfigDirectory(), "named.empty"));
+
+        newScript("Checking BIND configuration")
+                .body.append(BashCommands.sudo("named-checkconf"))
+                .failOnNonZeroResultCode()
+                .execute();
+    }
+
+    @Override
+    public void launch() {
+        newScript(MutableMap.of("usePidFile", false), LAUNCHING)
+                .body.append(BashCommands.sudo("service " + getOsSupport().getServiceName() + " start"))
+                .execute();
+    }
+
+    @Override
+    public boolean isRunning() {
+        return newScript(MutableMap.of("usePidFile", false), CHECK_RUNNING)
+                .body.append(BashCommands.sudo("service " + getOsSupport().getServiceName() + " status"))
+                .execute() == 0;
+    }
+
+    @Override
+    public void stop() {
+        newScript(MutableMap.of("usePidFile", false), STOPPING)
+                .body.append(BashCommands.sudo("service " + getOsSupport().getServiceName() + " stop"))
+                .execute();
+    }
+
+    @Override
+    public void updateBindConfiguration() {
+        LOG.debug("Updating bind configuration at " + getMachine());
+        copyAsRoot(entity.getConfig(BindDnsServer.NAMED_CONF_TEMPLATE), getOsSupport().getRootConfigFile());
+        copyAsRoot(entity.getConfig(BindDnsServer.DOMAIN_ZONE_FILE_TEMPLATE), getDomainZoneFile());
+        copyAsRoot(entity.getConfig(BindDnsServer.REVERSE_ZONE_FILE_TEMPLATE), getReverseZoneFile());
+        int result = getMachine().execScript("restart bind",
+                ImmutableList.of(BashCommands.sudo("service " + getOsSupport().getServiceName() + " restart")));
+        LOG.info("Updated named configuration and zone file for '{}' on {} (exit code {}).",
+                new Object[]{entity.getConfig(BindDnsServer.DOMAIN_NAME), entity, result});
+    }
+
+    private void copyAsRoot(String template, String destination) {
+        String content = processTemplate(template);
+        String temp = "/tmp/template-" + Strings.makeRandomId(6);
+        getMachine().copyTo(new ByteArrayInputStream(content.getBytes()), temp);
+        getMachine().execScript("copying file", ImmutableList.of(BashCommands.sudo(String.format("mv %s %s", temp, destination))));
+    }
+
+    /** @return The location on the server of the domain zone file */
+    public String getDomainZoneFile() {
+        return Os.mergePaths(getOsSupport().getConfigDirectory(), "domain.zone");
+    }
+
+    /** @return The location on the server of the reverse zone file */
+    public String getReverseZoneFile() {
+        return Os.mergePaths(getOsSupport().getConfigDirectory(), "reverse.zone");
+    }
+
+    public String getDataDirectory() {
+        return Os.mergePaths(getOsSupport().getWorkingDirectory(), "data");
+    }
+
+    public String getDynamicDirectory() {
+        return Os.mergePaths(getOsSupport().getWorkingDirectory(), "dynamic");
+    }
+
+    public String getRfc1912ZonesFile() {
+        return Os.mergePaths(getOsSupport().getConfigDirectory(), "rfc1912.zone");
+    }
+
+    public BindOsSupport getOsSupport() {
+        BindOsSupport result = osSupport;
+        if (result == null) {
+            synchronized (osSupportMutex) {
+                result = osSupport;
+                if (result == null) {
+                    boolean yumExists = newScript("testing for yum")
+                            .body.append(BashCommands.requireExecutable("yum"))
+                            .execute() == 0;
+                    osSupport = result = yumExists ? BindOsSupport.forRhel() : BindOsSupport.forDebian();
+                }
+            }
+        }
+        return result;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/689b49c3/software/network/src/main/java/org/apache/brooklyn/entity/network/bind/BindOsSupport.java
----------------------------------------------------------------------
diff --git a/software/network/src/main/java/org/apache/brooklyn/entity/network/bind/BindOsSupport.java b/software/network/src/main/java/org/apache/brooklyn/entity/network/bind/BindOsSupport.java
new file mode 100644
index 0000000..c48f8a3
--- /dev/null
+++ b/software/network/src/main/java/org/apache/brooklyn/entity/network/bind/BindOsSupport.java
@@ -0,0 +1,113 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.entity.network.bind;
+
+import javax.annotation.concurrent.Immutable;
+
+/**
+ * Provides operating system-specific information for working with the Bind service.
+ */
+// Class would be package-private if Freemarker didn't complain vociferously.
+@Immutable
+public class BindOsSupport {
+
+    // Likewise would make these package-private and have no getters if Freemarker was ok with it.
+    private final String packageName;
+    private final String serviceName;
+    private final String rootConfigFile;
+    private final String configDirectory;
+    private final String workingDirectory;
+    private final String rootZonesFile;
+    private final String keysFile;
+
+    private BindOsSupport(
+            String packageName,
+            String serviceName,
+            String rootConfigFile,
+            String configDirectory,
+            String workingDirectory,
+            String rootZonesFile,
+            String keysFile) {
+        this.packageName = packageName;
+        this.serviceName = serviceName;
+        this.rootConfigFile = rootConfigFile;
+        this.configDirectory = configDirectory;
+        this.workingDirectory = workingDirectory;
+        this.rootZonesFile = rootZonesFile;
+        this.keysFile = keysFile;
+    }
+
+    /**
+     * @return support for RHEL-based operating systems.
+     */
+    public static BindOsSupport forRhel() {
+        return new BindOsSupport(
+                "bind",
+                "named",
+                "/etc/named.conf",
+                "/var/named",
+                "/var/named/data",
+                "/var/named/named.ca",
+                "/etc/named.iscdlv.key");
+    }
+
+    /**
+     * @return support for Debian-based operating systems.
+     */
+    public static BindOsSupport forDebian() {
+        return new BindOsSupport(
+                "bind9",
+                "bind9",
+                "/etc/bind/named.conf",
+                "/etc/bind",
+                "/var/cache/bind",
+                "/etc/bind/db.root",
+                "/etc/bind/bind.keys"
+        );
+    }
+
+    public String getPackageName() {
+        return packageName;
+    }
+
+    public String getServiceName() {
+        return serviceName;
+    }
+
+    public String getRootConfigFile() {
+        return rootConfigFile;
+    }
+
+    public String getConfigDirectory() {
+        return configDirectory;
+    }
+
+    public String getWorkingDirectory() {
+        return workingDirectory;
+    }
+
+    public String getRootZonesFile() {
+        return rootZonesFile;
+    }
+
+    public String getKeysFile() {
+        return keysFile;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/689b49c3/software/network/src/main/resources/brooklyn/entity/network/bind/domain.zone
----------------------------------------------------------------------
diff --git a/software/network/src/main/resources/brooklyn/entity/network/bind/domain.zone b/software/network/src/main/resources/brooklyn/entity/network/bind/domain.zone
deleted file mode 100644
index 671d234..0000000
--- a/software/network/src/main/resources/brooklyn/entity/network/bind/domain.zone
+++ /dev/null
@@ -1,46 +0,0 @@
-[#ftl]
-;;
-;; Licensed to the Apache Software Foundation (ASF) under one
-;; or more contributor license agreements.  See the NOTICE file
-;; distributed with this work for additional information
-;; regarding copyright ownership.  The ASF licenses this file
-;; to you under the Apache License, Version 2.0 (the
-;; "License"); you may not use this file except in compliance
-;; with the License.  You may obtain a copy of the License at
-;;
-;;  http://www.apache.org/licenses/LICENSE-2.0
-;;
-;; Unless required by applicable law or agreed to in writing,
-;; software distributed under the License is distributed on an
-;; "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-;; KIND, either express or implied.  See the License for the
-;; specific language governing permissions and limitations
-;; under the License.
-;;
-;; Generated by Brooklyn on ${.now?string.short}
-;;
-;;
-[#noparse]$TTL[/#noparse] 86400
-@ IN SOA ns1.${entity.domainName}. root.${entity.domainName}. (
-    ${entity.serial?c} ; serial
-    3600 ; refresh
-    1800 ; retry
-    604800 ; expire
-    86400 ; ttl
-)
-@ IN NS ns1.${entity.domainName}.
-@ IN NS ns2.${entity.domainName}.
-ns1 IN A ${driver.address}
-ns2 IN A ${driver.address}
-
-;; Addresses
-[#list entity.addressRecords?keys as address]
-${address} IN A ${entity.addressRecords[address]}
-[/#list]
-
-;; Canonical names
-[#list entity.cnamesForTemplates?keys as aRecord]
-[#list entity.cnamesForTemplates[aRecord] as cname]
-${cname} IN CNAME ${aRecord}
-[/#list]
-[/#list]

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/689b49c3/software/network/src/main/resources/brooklyn/entity/network/bind/ifcfg
----------------------------------------------------------------------
diff --git a/software/network/src/main/resources/brooklyn/entity/network/bind/ifcfg b/software/network/src/main/resources/brooklyn/entity/network/bind/ifcfg
deleted file mode 100644
index b8b310d..0000000
--- a/software/network/src/main/resources/brooklyn/entity/network/bind/ifcfg
+++ /dev/null
@@ -1,24 +0,0 @@
-[#ftl]
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#  http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-# Generated by Brooklyn on ${.now?string.short}
-#
-DOMAIN=${entity.domainName}
-DNS1=${driver.address}
-DNS2=8.8.8.8
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/689b49c3/software/network/src/main/resources/brooklyn/entity/network/bind/named.conf
----------------------------------------------------------------------
diff --git a/software/network/src/main/resources/brooklyn/entity/network/bind/named.conf b/software/network/src/main/resources/brooklyn/entity/network/bind/named.conf
deleted file mode 100644
index 5dd10d7..0000000
--- a/software/network/src/main/resources/brooklyn/entity/network/bind/named.conf
+++ /dev/null
@@ -1,63 +0,0 @@
-[#ftl]
-//
-// Licensed to the Apache Software Foundation (ASF) under one
-// or more contributor license agreements.  See the NOTICE file
-// distributed with this work for additional information
-// regarding copyright ownership.  The ASF licenses this file
-// to you under the Apache License, Version 2.0 (the
-// "License"); you may not use this file except in compliance
-// with the License.  You may obtain a copy of the License at
-//
-//  http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing,
-// software distributed under the License is distributed on an
-// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-// KIND, either express or implied.  See the License for the
-// specific language governing permissions and limitations
-// under the License.
-//
-// Generated by Brooklyn on ${.now?string.short}
-//
-options {
-    listen-on port ${entity.dnsPort} { 127.0.0.1; ${driver.address}; };
-    listen-on-v6 port ${entity.dnsPort} { ::1; };
-    directory "${driver.osSupport.configDirectory}";
-    dump-file "${driver.dataDirectory}/cache_dump.db";
-    statistics-file "${driver.dataDirectory}/named_stats.txt";
-    memstatistics-file "${driver.dataDirectory}/named_mem_stats.txt";
-    allow-query { localhost; ${entity.managementCidr}; };
-    allow-transfer { localhost; ${driver.address}; };
-    recursion yes;
-    dnssec-enable yes;
-    dnssec-validation yes;
-    dnssec-lookaside auto;
-    bindkeys-file "${driver.osSupport.keysFile}";
-    managed-keys-directory "${driver.dynamicDirectory}";
-};
-
-logging {
-    channel default_debug {
-        file "${driver.dataDirectory}/named.run";
-        severity dynamic;
-    };
-};
-
-zone "." IN {
-    type hint;
-    file "${driver.osSupport.rootZonesFile}";
-};
-
-zone "${entity.reverseLookupDomain}" IN {
-    type master;
-    file "${driver.reverseZoneFile}";
-    allow-update { none; };
-};
-
-zone "${entity.domainName}" IN {
-    type master;
-    file "${driver.domainZoneFile}";
-    allow-update { none; };
-};
-
-include "${driver.rfc1912ZonesFile}";

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/689b49c3/software/network/src/main/resources/brooklyn/entity/network/bind/named.empty
----------------------------------------------------------------------
diff --git a/software/network/src/main/resources/brooklyn/entity/network/bind/named.empty b/software/network/src/main/resources/brooklyn/entity/network/bind/named.empty
deleted file mode 100644
index 3197b41..0000000
--- a/software/network/src/main/resources/brooklyn/entity/network/bind/named.empty
+++ /dev/null
@@ -1,30 +0,0 @@
-;
-; Licensed to the Apache Software Foundation (ASF) under one
-; or more contributor license agreements.  See the NOTICE file
-; distributed with this work for additional information
-; regarding copyright ownership.  The ASF licenses this file
-; to you under the Apache License, Version 2.0 (the
-; "License"); you may not use this file except in compliance
-; with the License.  You may obtain a copy of the License at
-;
-;  http://www.apache.org/licenses/LICENSE-2.0
-;
-; Unless required by applicable law or agreed to in writing,
-; software distributed under the License is distributed on an
-; "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-; KIND, either express or implied.  See the License for the
-; specific language governing permissions and limitations
-; under the License.
-;
-;
-; Reverse data for the broadcast zone
-;
-$TTL    1W
-@       IN      SOA     localhost. root.localhost. (
-                              1         ; serial
-                             1W         ; refresh
-                             1D         ; retry
-                             1M         ; expire
-                             1M )       ; ttl
-;
-@       IN      NS      localhost.

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/689b49c3/software/network/src/main/resources/brooklyn/entity/network/bind/named.localhost
----------------------------------------------------------------------
diff --git a/software/network/src/main/resources/brooklyn/entity/network/bind/named.localhost b/software/network/src/main/resources/brooklyn/entity/network/bind/named.localhost
deleted file mode 100644
index c683a83..0000000
--- a/software/network/src/main/resources/brooklyn/entity/network/bind/named.localhost
+++ /dev/null
@@ -1,32 +0,0 @@
-;
-; Licensed to the Apache Software Foundation (ASF) under one
-; or more contributor license agreements.  See the NOTICE file
-; distributed with this work for additional information
-; regarding copyright ownership.  The ASF licenses this file
-; to you under the Apache License, Version 2.0 (the
-; "License"); you may not use this file except in compliance
-; with the License.  You may obtain a copy of the License at
-;
-;  http://www.apache.org/licenses/LICENSE-2.0
-;
-; Unless required by applicable law or agreed to in writing,
-; software distributed under the License is distributed on an
-; "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-; KIND, either express or implied.  See the License for the
-; specific language governing permissions and limitations
-; under the License.
-;
-;
-; BIND data for the local loopback interface
-;
-$TTL    1W
-@       IN      SOA     localhost. root.localhost. (
-                              1         ; serial
-                             1W         ; refresh
-                             1D         ; retry
-                             1M         ; expire
-                             1M )       ; ttl
-;
-@       IN      NS      localhost.
-@       IN      A       127.0.0.1
-@       IN      AAAA    ::1