Example #1
0
function host_add($options = "")
{
    global $conf, $self, $onadb;
    // Version - UPDATE on every edit!
    $version = '1.11';
    printmsg("DEBUG => host_add({$options}) called", 3);
    // Parse incoming options string to an array
    $options = parse_options($options);
    // Return the usage summary if we need to
    if ($options['help'] or !($options['host'] and $options['type'] and $options['ip'])) {
        // NOTE: Help message lines should not exceed 80 characters for proper display on a console
        $self['error'] = 'ERROR => Insufficient parameters';
        return array(1, <<<EOM

host_add-v{$version}
Add a new host

  Synopsis: host_add [KEY=VALUE] ...

  Required:
    host=NAME[.DOMAIN]        Hostname for new DNS record
    type=TYPE or ID           Device/model type or ID
    ip=ADDRESS                IP address (numeric or dotted)

  Optional:
    notes=NOTES               Textual notes
    location=REF              Reference of location
    device=NAME|ID            The device this host is associated with

  Optional, add an interface too:
    mac=ADDRESS               Mac address (most formats are ok)
    name=NAME                 Interface name (i.e. "FastEthernet0/1.100")
    description=TEXT          Brief description of the interface
    addptr=Y|N                Auto add a PTR record for new host/IP (default: Y)


EOM
);
    }
    // Sanitize addptr.. set it to Y if it is not set
    $options['addptr'] = sanitize_YN($options['addptr'], 'Y');
    // clean up what is passed in
    $options['ip'] = trim($options['ip']);
    $options['mac'] = trim($options['mac']);
    $options['name'] = trim($options['name']);
    $options['host'] = trim($options['host']);
    // Validate that there isn't already another interface with the same IP address
    list($status, $rows, $interface) = ona_get_interface_record(array('ip_addr' => $options['ip']));
    if ($rows) {
        printmsg("DEBUG => host_add() IP conflict: That IP address (" . ip_mangle($orig_ip, 'dotted') . ") is already in use!", 3);
        $self['error'] = "ERROR => host_add() IP conflict: That IP address (" . ip_mangle($orig_ip, 'dotted') . ") is already in use!";
        return array(4, $self['error'] . "\n" . "INFO => Conflicting interface record ID: {$interface['id']}\n");
    }
    // Find the Location ID to use
    if ($options['location']) {
        list($status, $rows, $loc) = ona_find_location($options['location']);
        if ($status or !$rows) {
            printmsg("DEBUG => The location specified, {$options['location']}, does not exist!", 3);
            $self['error'] = "ERROR => The location specified, {$options['location']}, does not exist!";
            return array(2, "{$self['error']}\n");
        }
        printmsg("DEBUG => Location selected: {$loc['reference']}, location name: {$loc['name']}", 3);
    } else {
        $loc['id'] = 0;
    }
    // Find the Device Type ID (i.e. Type) to use
    list($status, $rows, $device_type) = ona_find_device_type($options['type']);
    if ($status or $rows != 1 or !$device_type['id']) {
        printmsg("DEBUG => The device type specified, {$options['type']}, does not exist!", 3);
        return array(3, "ERROR => The device type specified, {$options['type']}, does not exist!\n");
    }
    printmsg("DEBUG => Device type selected: {$device_type['model_description']} Device ID: {$device_type['id']}", 3);
    // Sanitize "security_level" option
    $options['security_level'] = sanitize_security_level($options['security_level']);
    if ($options['security_level'] == -1) {
        printmsg("DEBUG => Sanitize security level failed either ({$options['security_level']}) is invalid or is higher than user's level!", 3);
        return array(3, $self['error'] . "\n");
    }
    // Determine the real hostname to be used --
    // i.e. add .something.com, or find the part of the name provided
    // that will be used as the "domain".  This means testing many
    // domain names against the DB to see what's valid.
    //
    // Find the domain name piece of $search.
    // If we are specifically passing in a domain, use its value.  If we dont have a domain
    // then try to find it in the name that we are setting.
    if ($options['domain']) {
        // Find the domain name piece of $search
        list($status, $rows, $domain) = ona_find_domain($options['domain'], 0);
    } else {
        list($status, $rows, $domain) = ona_find_domain($options['host'], 0);
    }
    if (!isset($domain['id'])) {
        printmsg("ERROR => Unable to determine domain name portion of ({$options['host']})!", 3);
        $self['error'] = "ERROR => Unable to determine domain name portion of ({$options['host']})!";
        return array(3, $self['error'] . "\n");
    }
    printmsg("DEBUG => ona_find_domain({$options['host']}) returned: {$domain['fqdn']}", 3);
    // Now find what the host part of $search is
    $hostname = str_replace(".{$domain['fqdn']}", '', $options['host']);
    // Validate that the DNS name has only valid characters in it
    $hostname = sanitize_hostname($hostname);
    if (!$hostname) {
        printmsg("ERROR => Invalid host name ({$options['host']})!", 3);
        $self['error'] = "ERROR => Invalid host name ({$options['host']})!";
        return array(4, $self['error'] . "\n");
    }
    // Debugging
    printmsg("DEBUG => Using hostname: {$hostname} Domainname: {$domain['fqdn']}, Domain ID: {$domain['id']}", 3);
    // Validate that there isn't already any dns record named $host['name'] in the domain $host_domain_id.
    $h_status = $h_rows = 0;
    // does the domain $host_domain_id even exist?
    list($d_status, $d_rows, $d_record) = ona_get_dns_record(array('name' => $hostname, 'domain_id' => $domain['id']));
    if ($d_status or $d_rows) {
        printmsg("DEBUG => The name {$hostname}.{$domain['fqdn']} is already in use, the primary name for a host should be unique!", 3);
        $self['error'] = "ERROR => Another DNS record named {$hostname}.{$domain['fqdn']} is already in use, the primary name for a host should be unique!";
        return array(5, $self['error'] . "\n");
    }
    // Check permissions
    if (!auth('host_add')) {
        $self['error'] = "Permission denied!";
        printmsg($self['error'], 0);
        return array(10, $self['error'] . "\n");
    }
    // Get the next ID for the new host record
    $id = ona_get_next_id('hosts');
    if (!$id) {
        $self['error'] = "ERROR => The ona_get_next_id('hosts') call failed!";
        printmsg($self['error'], 0);
        return array(7, $self['error'] . "\n");
    }
    printmsg("DEBUG => ID for new host record: {$id}", 3);
    // Get the next ID for the new device record or use the one passed in the CLI
    if (!$options['device']) {
        $host['device_id'] = ona_get_next_id('devices');
        if (!$id) {
            $self['error'] = "ERROR => The ona_get_next_id('device') call failed!";
            printmsg($self['error'], 0);
            return array(7, $self['error'] . "\n");
        }
        printmsg("DEBUG => ID for new device record: {$id}", 3);
    } else {
        list($status, $rows, $devid) = ona_find_device($options['device']);
        if (!$rows) {
            printmsg("DEBUG => The device specified, {$options['device']}, does not exist!", 3);
            $self['error'] = "ERROR => The device specified, {$options['device']}, does not exist!";
            return array(7, $self['error'] . "\n");
        }
        $host['device_id'] = $devid['id'];
    }
    // There is an issue with escaping '=' and '&'.  We need to avoid adding escape characters
    $options['notes'] = str_replace('\\=', '=', $options['notes']);
    $options['notes'] = str_replace('\\&', '&', $options['notes']);
    // Add the device record
    // FIXME: (MP) quick add of device record. more detail should be looked at here to ensure it is done right
    // FIXME: MP this should use the run_module('device_add')!!! when it is ready
    list($status, $rows) = db_insert_record($onadb, 'devices', array('id' => $host['device_id'], 'device_type_id' => $device_type['id'], 'location_id' => $loc['id'], 'primary_host_id' => $id));
    if ($status or !$rows) {
        $self['error'] = "ERROR => host_add() SQL Query failed adding device: " . $self['error'];
        printmsg($self['error'], 0);
        return array(6, $self['error'] . "\n");
    }
    // Add the host record
    // FIXME: (PK) Needs to insert to multiple tables for e.g. name and domain_id.
    list($status, $rows) = db_insert_record($onadb, 'hosts', array('id' => $id, 'primary_dns_id' => '', 'device_id' => $host['device_id'], 'notes' => $options['notes']));
    if ($status or !$rows) {
        $self['error'] = "ERROR => host_add() SQL Query failed adding host: " . $self['error'];
        printmsg($self['error'], 0);
        return array(6, $self['error'] . "\n");
    }
    // Else start an output message
    $text = "INFO => Host ADDED: {$hostname}.{$domain['fqdn']}";
    printmsg($text, 0);
    $text .= "\n";
    // We must always have an IP now to add an interface, call that module now:
    // since we have no name yet, we need to use the ID of the new host as the host option for the following module calls
    $options['host'] = $id;
    // for annoying reasons we need to keep track of what was set first
    $options['addptrsave'] = $options['addptr'];
    // Interface adds can add PTR records, lets let the A record add that happens next add it instead.
    $options['addptr'] = '0';
    printmsg("DEBUG => host_add() ({$hostname}.{$domain['fqdn']}) calling interface_add() ({$options['ip']})", 3);
    list($status, $output) = run_module('interface_add', $options);
    if ($status) {
        return array($status, $output);
    }
    $text .= $output;
    // Find the interface_id for the interface we just added
    list($status, $rows, $int) = ona_find_interface($options['ip']);
    // make the dns record type A
    $options['type'] = 'A';
    // FIXME: MP I had to force the name value here.  name is comming in as the interface name.  this is nasty!
    $options['name'] = "{$hostname}.{$domain['fqdn']}";
    $options['domain'] = $domain['fqdn'];
    // And we will go ahead and auto add the ptr.  the user can remove it later if they dont want it.  FIXME: maybe create a checkbox on the host edit
    $options['addptr'] = $options['addptrsave'];
    // Add the DNS entry with the IP address etc
    printmsg("DEBUG => host_add() ({$hostname}.{$domain['fqdn']}) calling dns_record_add() ({$options['ip']})", 3);
    list($status, $output) = run_module('dns_record_add', $options);
    if ($status) {
        return array($status, $output);
    }
    $text .= $output;
    // find the dns record we just added so we can use its ID as the primary_dns_id for the host.
    list($status, $rows, $dnsrecord) = ona_get_dns_record(array('name' => $hostname, 'domain_id' => $domain['id'], 'interface_id' => $int['id'], 'type' => 'A'));
    // Set the primary_dns_id to the dns record that was just added
    list($status, $rows) = db_update_record($onadb, 'hosts', array('id' => $id), array('primary_dns_id' => $dnsrecord['id']));
    if ($status or !$rows) {
        $self['error'] = "ERROR => host_add() SQL Query failed to update primary_dns_id for host: " . $self['error'];
        printmsg($self['error'], 0);
        return array(8, $self['error'] . "\n");
    }
    return array(0, $text);
}
Example #2
0
function subnet_modify($options = "")
{
    global $conf, $self, $onadb;
    //printmsg('DEBUG => subnet_modify('.implode (";",$options).') called', 3);
    // Version - UPDATE on every edit!
    $version = '1.08';
    // Parse incoming options string to an array
    $options = parse_options($options);
    // Return the usage summary if we need to
    if ($options['help'] or !$options['subnet'] or !($options['set_ip'] or $options['set_netmask'] or $options['set_type'] or $options['set_name'] or array_key_exists('set_vlan', $options) or $options['set_security_level'])) {
        // NOTE: Help message lines should not exceed 80 characters for proper display on a console
        $self['error'] = 'ERROR => Insufficient parameters';
        return array(1, <<<EOM

subnet_modify-v{$version}
Modify a subnet (subnet) record

  Synopsis: subnet_modify [KEY=VALUE] ...

  Where:
    subnet=[ID|IP]           select subnet by search string

  Update:
    set_ip=IP                 change subnet "subnet" address
    set_netmask=MASK          change subnet netmask
    set_name=TEXT      change subnet name (i.e. "LAN-1234")
    set_type=TYPE             change subnet type by name or id
    set_vlan=VLAN             change vlan by name, number
    campus=CAMPUS             vlan campus name or id to help identify vlan
    set_security_level=LEVEL  numeric security level ({$conf['ona_lvl']})



EOM
);
    }
    $check_boundaries = 0;
    // Find the subnet record we're modifying
    list($status, $rows, $subnet) = ona_find_subnet($options['subnet']);
    if ($status or !$rows) {
        $self['error'] = "ERROR => Subnet not found";
        return array(2, $self['error'] . "\n");
    }
    // Check permissions
    if (!auth('subnet_modify')) {
        $self['error'] = "Permission denied!";
        printmsg($self['error'], 0);
        return array(3, $self['error'] . "\n");
    }
    // Validate the ip address
    if (!$options['set_ip']) {
        $options['set_ip'] = $subnet['ip_addr'];
    } else {
        $check_boundaries = 1;
        $options['set_ip'] = $setip = ip_mangle($options['set_ip'], 'numeric');
        // FIXME: what if ip_mangle returns a GMP object?
        if ($options['set_ip'] == -1) {
            $self['error'] = "ERROR => The IP address specified is invalid!";
            return array(4, $self['error'] . "\n");
        }
    }
    // Validate the netmask is okay
    if (!$options['set_netmask']) {
        $options['set_netmask'] = $subnet['ip_mask'];
        $cidr = ip_mangle($options['set_netmask'], 'cidr');
    } else {
        $check_boundaries = 1;
        $cidr = ip_mangle($options['set_netmask'], 'cidr');
        // FIXME: what if ip_mangle returns a GMP object?
        $options['set_netmask'] = ip_mangle($options['set_netmask'], 'numeric');
        if ($cidr == -1 or $options['set_netmask'] == -1) {
            $self['error'] = "ERROR => The netmask specified is invalid!";
            return array(5, $self['error'] . "\n");
        }
    }
    if (is_ipv4($setip)) {
        $padding = 32;
        $fmt = 'dotted';
        $ip1 = ip_mangle($setip, 'binary');
        $num_hosts = 0xffffffff - $options['set_netmask'];
        $first_host = $options['set_ip'] + 1;
        $last_host = $options['set_ip'] + $num_hosts;
        $str_last_host = $last_host;
        $last_last_host = $last_host - 1;
    } else {
        $padding = 128;
        $fmt = 'ipv6gz';
        $ip1 = ip_mangle($setip, 'bin128');
        $first_host = gmp_strval(gmp_add($options['set_ip'], 1));
        $sub = gmp_sub("340282366920938463463374607431768211455", $options['set_netmask']);
        $last_host = gmp_add($options['set_ip'], $sub);
        $str_last_host = gmp_strval($last_host);
        $last_last_host = gmp_strval(gmp_sub($last_host, 1));
    }
    // Validate that the subnet IP & netmask combo are valid together.
    $ip2 = str_pad(substr($ip1, 0, $cidr), $padding, '0');
    $ip1 = ip_mangle($ip1, $fmt);
    $ip2 = ip_mangle($ip2, $fmt);
    if ($ip1 != $ip2) {
        $self['error'] = "ERROR => Invalid subnet specified - did you mean: {$ip2}/{$cidr}?";
        return array(6, $self['error'] . "\n");
    }
    // If our IP or netmask changed we need to make sure that
    // we won't abandon any host interfaces.
    // We also need to verify that the new boundaries are valid and
    // don't interefere with any other subnets.
    if ($check_boundaries == 1) {
        // *** Check to see if the new subnet overlaps any existing ONA subnets *** //
        // I convert the IP address to dotted format when calling ona_find_subnet()
        // because it saves it from doing a few unnecessary sql queries.
        // Look for overlaps like this (where new subnet address starts inside an existing subnet):
        //            [ -- new subnet -- ]
        //    [ -- old subnet --]
        list($status, $rows, $record) = ona_find_subnet(ip_mangle($options['set_ip'], 'dotted'));
        if ($rows and $record['id'] != $subnet['id']) {
            $self['error'] = "ERROR => Subnet address conflict! New subnet starts inside an existing subnet.";
            return array(7, $self['error'] . "\n" . "ERROR  => Conflicting subnet record ID: {$record['id']}\n");
        }
        // Look for overlaps like this (where the new subnet ends inside an existing subnet):
        //    [ -- new subnet -- ]
        //           [ -- old subnet --]
        // Find last address of our subnet, and see if it's inside of any other subnet:
        list($status, $rows, $record) = ona_find_subnet(ip_mangle($str_last_host, 'dotted'));
        if ($rows and $record['id'] != $subnet['id']) {
            $self['error'] = "ERROR => Subnet address conflict! New subnet ends inside an existing subnet.";
            return array(8, $self['error'] . "\n" . "ERROR  => Conflicting subnet record ID: {$record['id']}\n");
        }
        // Look for overlaps like this (where the new subnet entirely overlaps an existing subnet):
        //    [ -------- new subnet --------- ]
        //           [ -- old subnet --]
        //
        // Do a cool SQL query to find all subnets whose start address is >= or <= the
        // new subnet base address.
        $where = "ip_addr >= {$options['set_ip']} AND ip_addr <= {$str_last_host}";
        list($status, $rows, $record) = ona_get_subnet_record($where);
        if ($rows > 1 or $rows == 1 and $record['id'] != $subnet['id']) {
            $self['error'] = "ERROR => Subnet address conflict! New subnet would encompass an existing subnet.";
            return array(9, $self['error'] . "\n" . "ERROR  => Conflicting subnet record ID: {$record['id']}\n");
        }
        // Look for any hosts that are currently in our subnet that would be
        // abandoned if we were to make the proposed changes.
        // Look for hosts on either side of the new subnet boundaries:
        //            [--- new subnet ---]
        //         *      **   *            *   <-- Hosts: the first and last host would be a problem!
        //       [------- old subnet --------]
        //
        $where1 = "subnet_id = {$subnet['id']} AND ip_addr < {$first_host}";
        $where2 = "subnet_id = {$subnet['id']} AND ip_addr > {$last_last_host}";
        list($status, $rows1, $record) = ona_get_interface_record($where1);
        list($status, $rows2, $record) = ona_get_interface_record($where2);
        if ($rows1 or $rows2) {
            $num = $rows1 + $rows2;
            $self['error'] = "ERROR => Changes would abandon {$num} hosts in an unallocated ip space";
            return array(10, $self['error'] . "\n");
        }
        // Look for any dhcp pools that are currently in our subnet that would be
        // abandoned if we were to make the proposed changes.
        // Look for existin pools with start/end values outside of new subnet range
        //            [--- new subnet ---]
        //                      [--cur pool--]
        //       [------- old subnet --------]
        //
        $where1 = "subnet_id = {$subnet['id']} AND ip_addr_start < {$options['set_ip']}";
        $where2 = "subnet_id = {$subnet['id']} AND ip_addr_end > {$str_last_host}";
        list($status, $rows1, $record) = ona_get_dhcp_pool_record($where1);
        list($status, $rows2, $record) = ona_get_dhcp_pool_record($where2);
        if ($rows1 or $rows2) {
            $num = $rows1 + $rows2;
            $self['error'] = "ERROR => Changes would abandon a DHCP pool in an unallocated ip space, adjust pool sizes first";
            return array(10, $self['error'] . "\n");
        }
    }
    //
    // Define the fields we're updating
    //
    // This variable will contain the updated info we'll insert into the DB
    $SET = array();
    $SET['ip_addr'] = $options['set_ip'];
    $SET['ip_mask'] = $options['set_netmask'];
    // Set options['set_security_level']?
    // Sanitize "security_level" option
    if (array_key_exists('set_security_level', $options)) {
        $options['set_security_level'] = sanitize_security_level($options['set_security_level']);
        if ($options['set_security_level'] == -1) {
            return array(11, $self['error'] . "\n");
        }
        $SET['lvl'] = $options['set_security_level'];
    }
    // Set options['set_name']?
    if ($options['set_name']) {
        // BUSINESS RULE: We require subnet names to be in upper case and spaces are converted to -'s.
        $options['set_name'] = trim($options['set_name']);
        $options['set_name'] = preg_replace('/\\s+/', '-', $options['set_name']);
        $options['set_name'] = strtoupper($options['set_name']);
        // Make sure there's not another subnet with this name
        list($status, $rows, $tmp) = ona_get_subnet_record(array('name' => $options['set_name']));
        if ($status or $rows > 1 or $rows == 1 and $tmp['id'] != $subnet['id']) {
            $self['error'] = "ERROR => That name is already used by another subnet!";
            return array(12, $self['error'] . "\n");
        }
        $SET['name'] = $options['set_name'];
    }
    // Set options['set_type']?
    if ($options['set_type']) {
        // Find the type from $options[type]
        list($status, $rows, $subnet_type) = ona_find_subnet_type($options['set_type']);
        if ($status or $rows != 1) {
            $self['error'] = "ERROR => Invalid subnet type specified!";
            return array(13, $self['error'] . "\n");
        }
        printmsg("Subnet type selected: {$subnet_type['display_name']} ({$subnet_type['short_name']})", 1);
        $SET['subnet_type_id'] = $subnet_type['id'];
    }
    // Set options['set_vlan']?
    if (array_key_exists('set_vlan', $options) or $options['campus']) {
        if (!$options['set_vlan']) {
            $SET['vlan_id'] = '';
        } else {
            // Find the VLAN ID from $options[set_vlan] and $options[campus]
            list($status, $rows, $vlan) = ona_find_vlan($options['set_vlan'], $options['campus']);
            if ($status or $rows != 1) {
                $self['error'] = "ERROR => The vlan/campus pair specified is invalid!";
                return array(15, $self['error'] . "\n");
            }
            printmsg("VLAN selected: {$vlan['name']} in {$vlan['vlan_campus_name']} campus", 1);
            $SET['vlan_id'] = $vlan['id'];
        }
    }
    // Update the subnet record
    list($status, $rows) = db_update_record($onadb, 'subnets', array('id' => $subnet['id']), $SET);
    if ($status or !$rows) {
        return array(16, $self['error'] . "\n");
    }
    // Load the updated record for display
    list($status, $rows, $subnet) = ona_get_subnet_record(array('id' => $subnet['id']));
    // Return the (human-readable) success notice
    $text = format_array($SET);
    $self['error'] = "INFO => Subnet UPDATED";
    return array(0, $self['error'] . ":\n{$text}\n");
}