191 lines
7.3 KiB
Python
191 lines
7.3 KiB
Python
|
# Copyright 2016 Cloudbase Solutions Srl
|
||
|
# All Rights Reserved.
|
||
|
#
|
||
|
# Licensed 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.
|
||
|
|
||
|
from oslo_log import log as logging
|
||
|
|
||
|
from os_win._i18n import _
|
||
|
from os_win import constants
|
||
|
from os_win import exceptions
|
||
|
from os_win.utils import baseutils
|
||
|
|
||
|
LOG = logging.getLogger(__name__)
|
||
|
|
||
|
|
||
|
class DNSUtils(baseutils.BaseUtils):
|
||
|
|
||
|
_DNS_NAMESPACE = '//%s/root/MicrosoftDNS'
|
||
|
|
||
|
def __init__(self, host='.'):
|
||
|
self._dns_manager_attr = None
|
||
|
self._host = host
|
||
|
|
||
|
@property
|
||
|
def _dns_manager(self):
|
||
|
if not self._dns_manager_attr:
|
||
|
try:
|
||
|
namespace = self._DNS_NAMESPACE % self._host
|
||
|
self._dns_manager_attr = self._get_wmi_obj(namespace)
|
||
|
except Exception:
|
||
|
raise exceptions.DNSException(
|
||
|
_("Namespace %(namespace)s not found. Make sure "
|
||
|
"DNS Server feature is installed.") %
|
||
|
{'namespace': namespace})
|
||
|
|
||
|
return self._dns_manager_attr
|
||
|
|
||
|
def _get_zone(self, zone_name, ignore_missing=True):
|
||
|
zones = self._dns_manager.MicrosoftDNS_Zone(Name=zone_name)
|
||
|
if zones:
|
||
|
return zones[0]
|
||
|
if not ignore_missing:
|
||
|
raise exceptions.DNSZoneNotFound(zone_name=zone_name)
|
||
|
|
||
|
def zone_list(self):
|
||
|
"""Returns the current list of DNS Zones.
|
||
|
|
||
|
"""
|
||
|
|
||
|
zones = self._dns_manager.MicrosoftDNS_Zone()
|
||
|
return [x.Name for x in zones]
|
||
|
|
||
|
def zone_exists(self, zone_name):
|
||
|
return self._get_zone(zone_name) is not None
|
||
|
|
||
|
def get_zone_properties(self, zone_name):
|
||
|
zone = self._get_zone(zone_name, ignore_missing=False)
|
||
|
|
||
|
zone_properties = {}
|
||
|
zone_properties['zone_type'] = zone.ZoneType
|
||
|
zone_properties['ds_integrated'] = zone.DsIntegrated
|
||
|
zone_properties['data_file_name'] = zone.DataFile
|
||
|
zone_properties['master_servers'] = zone.MasterServers or []
|
||
|
|
||
|
return zone_properties
|
||
|
|
||
|
def zone_create(self, zone_name, zone_type, ds_integrated,
|
||
|
data_file_name=None, ip_addrs=None,
|
||
|
admin_email_name=None):
|
||
|
"""Creates a DNS Zone and returns the path to the associated object.
|
||
|
|
||
|
:param zone_name: string representing the name of the zone.
|
||
|
:param zone_type: type of zone
|
||
|
0 = Primary zone
|
||
|
1 = Secondary zone, MUST include at least one master IP
|
||
|
2 = Stub zone, MUST include at least one master IP
|
||
|
3 = Zone forwarder, MUST include at least one master IP
|
||
|
:param ds_integrated: Only Primary zones can be stored in AD
|
||
|
True = the zone data is stored in the Active Directory
|
||
|
False = the data zone is stored in files
|
||
|
:param data_file_name(Optional): name of the data file associated
|
||
|
with the zone.
|
||
|
:param ip_addrs(Optional): IP addresses of the master DNS servers
|
||
|
for this zone. Parameter type MUST be list
|
||
|
:param admin_email_name(Optional): email address of the administrator
|
||
|
responsible for the zone.
|
||
|
"""
|
||
|
LOG.debug("Creating DNS Zone '%s'" % zone_name)
|
||
|
if self.zone_exists(zone_name):
|
||
|
raise exceptions.DNSZoneAlreadyExists(zone_name=zone_name)
|
||
|
|
||
|
dns_zone_manager = self._dns_manager.MicrosoftDNS_Zone
|
||
|
(zone_path,) = dns_zone_manager.CreateZone(
|
||
|
ZoneName=zone_name,
|
||
|
ZoneType=zone_type,
|
||
|
DsIntegrated=ds_integrated,
|
||
|
DataFileName=data_file_name,
|
||
|
IpAddr=ip_addrs,
|
||
|
AdminEmailname=admin_email_name)
|
||
|
return zone_path
|
||
|
|
||
|
def zone_delete(self, zone_name):
|
||
|
LOG.debug("Deleting DNS Zone '%s'" % zone_name)
|
||
|
|
||
|
zone_to_be_deleted = self._get_zone(zone_name)
|
||
|
if zone_to_be_deleted:
|
||
|
zone_to_be_deleted.Delete_()
|
||
|
|
||
|
def zone_modify(self, zone_name, allow_update=None, disable_wins=None,
|
||
|
notify=None, reverse=None, secure_secondaries=None):
|
||
|
"""Modifies properties of an existing zone. If any parameter is None,
|
||
|
|
||
|
then that parameter will be skipped and will not be taken into
|
||
|
consideration.
|
||
|
|
||
|
:param zone_name: string representing the name of the zone.
|
||
|
:param allow_update:
|
||
|
0 = No updates allowed.
|
||
|
1 = Zone accepts both secure and nonsecure updates.
|
||
|
2 = Zone accepts secure updates only.
|
||
|
:param disable_wins: Indicates whether the WINS record is replicated.
|
||
|
If set to TRUE, WINS record replication is disabled.
|
||
|
:param notify:
|
||
|
0 = Do not notify secondaries
|
||
|
1 = Notify Servers listed on the Name Servers Tab
|
||
|
2 = Notify the specified servers
|
||
|
:param reverse: Indicates whether the Zone is reverse (TRUE)
|
||
|
or forward (FALSE).
|
||
|
:param secure_secondaries:
|
||
|
0 = Allowed to Any host
|
||
|
1 = Only to the Servers listed on the Name Servers tab
|
||
|
2 = To the following servers (destination servers IP addresses
|
||
|
are specified in SecondaryServers value)
|
||
|
3 = Zone transfers not allowed
|
||
|
"""
|
||
|
|
||
|
zone = self._get_zone(zone_name, ignore_missing=False)
|
||
|
|
||
|
if allow_update is not None:
|
||
|
zone.AllowUpdate = allow_update
|
||
|
if disable_wins is not None:
|
||
|
zone.DisableWINSRecordReplication = disable_wins
|
||
|
if notify is not None:
|
||
|
zone.Notify = notify
|
||
|
if reverse is not None:
|
||
|
zone.Reverse = reverse
|
||
|
if secure_secondaries is not None:
|
||
|
zone.SecureSecondaries = secure_secondaries
|
||
|
|
||
|
zone.put()
|
||
|
|
||
|
def zone_update(self, zone_name):
|
||
|
LOG.debug("Updating DNS Zone '%s'" % zone_name)
|
||
|
|
||
|
zone = self._get_zone(zone_name, ignore_missing=False)
|
||
|
if (zone.DsIntegrated and
|
||
|
zone.ZoneType == constants.DNS_ZONE_TYPE_PRIMARY):
|
||
|
zone.UpdateFromDS()
|
||
|
elif zone.ZoneType in [constants.DNS_ZONE_TYPE_SECONDARY,
|
||
|
constants.DNS_ZONE_TYPE_STUB]:
|
||
|
zone.ForceRefresh()
|
||
|
elif zone.ZoneType in [constants.DNS_ZONE_TYPE_PRIMARY,
|
||
|
constants.DNS_ZONE_TYPE_FORWARD]:
|
||
|
zone.ReloadZone()
|
||
|
|
||
|
def get_zone_serial(self, zone_name):
|
||
|
# Performing a manual check to make sure the zone exists before
|
||
|
# trying to retrieve the MicrosoftDNS_SOAType object. Otherwise,
|
||
|
# the query for MicrosoftDNS_SOAType will fail with "Generic Failure"
|
||
|
if not self.zone_exists(zone_name):
|
||
|
# Return None if zone was not found
|
||
|
return None
|
||
|
|
||
|
zone_soatype = self._dns_manager.MicrosoftDNS_SOAType(
|
||
|
ContainerName=zone_name)
|
||
|
if not zone_soatype:
|
||
|
return None
|
||
|
# Serial number of the SOA record
|
||
|
SOA = zone_soatype[0].SerialNumber
|
||
|
return int(SOA)
|