Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions cloudbridge/providers/azure/azure_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
ImageUpdate, Snapshot, SnapshotUpdate,
VirtualMachine, VirtualMachineUpdate)
from azure.mgmt.devtestlabs.models import GalleryImageReference
from azure.mgmt.dns import DnsManagementClient
from azure.mgmt.dns.models import Zone
from azure.mgmt.network import NetworkManagementClient
from azure.mgmt.network.models import (NetworkInterface,
NetworkSecurityGroup, PublicIPAddress,
Expand Down Expand Up @@ -180,6 +182,7 @@ def __init__(self, config):
self._network_management_client = None
self._subscription_client = None
self._compute_client = None
self._dns_client = None
self._access_key_result = None
self._block_blob_service = None
self._table_service_client = None
Expand Down Expand Up @@ -278,6 +281,13 @@ def network_management_client(self):
self._credentials, self.subscription_id)
return self._network_management_client

@property
def dns_client(self):
if not self._dns_client:
self._dns_client = DnsManagementClient(
self._credentials, self.subscription_id)
return self._dns_client

@property
def blob_service(self):
self._get_or_create_storage_account()
Expand Down Expand Up @@ -985,3 +995,38 @@ def update_route_table_tags(self, route_table_name, tags):
self.network_management_client.route_tables.update_tags(
self.resource_group, route_table_name,
TagsObject(tags=tags))

# DNS operations
def get_dns_zone(self, zone_name):
return self.dns_client.zones.get(self.resource_group, zone_name)

def list_dns_zones(self):
return list(self.dns_client.zones.list_by_resource_group(
self.resource_group))

def create_dns_zone(self, zone_name, params):
return self.dns_client.zones.create_or_update(
self.resource_group, zone_name, Zone(**params))

def delete_dns_zone(self, zone_name):
self.dns_client.zones.begin_delete(
self.resource_group, zone_name).wait()

def get_dns_record(self, zone_name, relative_record_name, record_type):
return self.dns_client.record_sets.get(
self.resource_group, zone_name, relative_record_name, record_type)

def list_dns_records(self, zone_name):
return list(self.dns_client.record_sets.list_all_by_dns_zone(
self.resource_group, zone_name))

def create_dns_record(self, zone_name, relative_record_name,
record_type, params):
from azure.mgmt.dns.models import RecordSet
return self.dns_client.record_sets.create_or_update(
self.resource_group, zone_name, relative_record_name,
record_type, RecordSet(**params))

def delete_dns_record(self, zone_name, relative_record_name, record_type):
self.dns_client.record_sets.delete(
self.resource_group, zone_name, relative_record_name, record_type)
4 changes: 3 additions & 1 deletion cloudbridge/providers/azure/provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from cloudbridge.providers.azure.azure_client import AzureClient

from .services import AzureComputeService
from .services import AzureDnsService
from .services import AzureNetworkingService
from .services import AzureSecurityService
from .services import AzureStorageService
Expand Down Expand Up @@ -79,6 +80,7 @@ def __init__(self, config):
self._storage = AzureStorageService(self)
self._compute = AzureComputeService(self)
self._networking = AzureNetworkingService(self)
self._dns = AzureDnsService(self)

def __get_deprecated_username(self, default):
username = self._get_config_value(
Expand Down Expand Up @@ -115,7 +117,7 @@ def storage(self):

@property
def dns(self):
raise NotImplementedError()
return self._dns

@property
def azure_client(self):
Expand Down
117 changes: 116 additions & 1 deletion cloudbridge/providers/azure/resources.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@

import paramiko
from cloudbridge.base.resources import (BaseAttachmentInfo, BaseBucket,
BaseBucketObject, BaseFloatingIP,
BaseBucketObject, BaseDnsRecord,
BaseDnsZone, BaseFloatingIP,
BaseInstance, BaseInternetGateway,
BaseKeyPair, BaseLaunchConfig,
BaseMachineImage, BaseNetwork,
Expand All @@ -30,6 +31,7 @@

from . import helpers as azure_helpers
from .subservices import (AzureBucketObjectSubService,
AzureDnsRecordSubService,
AzureFloatingIPSubService, AzureGatewaySubService,
AzureSubnetSubService, AzureVMFirewallRuleSubService)

Expand Down Expand Up @@ -1554,3 +1556,116 @@ def delete(self):
@property
def floating_ips(self):
return self._fips_container


# Map Azure record-set type suffix (e.g. 'Microsoft.Network/dnszones/A')
# to cloudbridge DnsRecordType. Used to expose record data in a normalized form.
_AZURE_RECORD_TYPE_ATTR = {
'A': 'a_records',
'AAAA': 'aaaa_records',
'CNAME': 'cname_record',
'MX': 'mx_records',
'NS': 'ns_records',
'PTR': 'ptr_records',
'SRV': 'srv_records',
'TXT': 'txt_records',
}


def _azure_record_type(raw_record):
"""Return the bare type (e.g. 'A') from an Azure RecordSet."""
rec_type = raw_record.type or ''
# Azure formats: 'Microsoft.Network/dnszones/A' or bare 'A'
return rec_type.split('/')[-1] if '/' in rec_type else rec_type


def _azure_record_data(raw_record):
"""Extract the data values from an Azure RecordSet as a list of strings."""
rt = _azure_record_type(raw_record)
attr = _AZURE_RECORD_TYPE_ATTR.get(rt)
if not attr:
return []
value = getattr(raw_record, attr, None)
if value is None:
return []
if rt == 'A':
return [r.ipv4_address for r in value]
if rt == 'AAAA':
return [r.ipv6_address for r in value]
if rt == 'CNAME':
return [value.cname]
if rt == 'MX':
return ['{0} {1}'.format(r.preference, r.exchange) for r in value]
if rt == 'NS':
return [r.nsdname for r in value]
if rt == 'PTR':
return [r.ptrdname for r in value]
if rt == 'SRV':
return ['{0} {1} {2} {3}'.format(r.priority, r.weight, r.port,
r.target) for r in value]
if rt == 'TXT':
# Each TXT record carries a list of strings; join with spaces by convention
return [' '.join(r.value) if isinstance(r.value, list) else r.value
for r in value]
return []


class AzureDnsZone(BaseDnsZone):

def __init__(self, provider, dns_zone):
super(AzureDnsZone, self).__init__(provider)
self._dns_zone = dns_zone
self._dns_record_container = AzureDnsRecordSubService(provider, self)

@property
def id(self):
return self._dns_zone.name

@property
def name(self):
return self._dns_zone.name

@property
def admin_email(self):
tags = self._dns_zone.tags or {}
return tags.get('admin_email')

@property
def records(self):
return self._dns_record_container


class AzureDnsRecord(BaseDnsRecord):

def __init__(self, provider, dns_zone, dns_record):
super(AzureDnsRecord, self).__init__(provider)
self._dns_zone = dns_zone
self._dns_rec = dns_record

@property
def id(self):
return self.name + ":" + self.type

@property
def name(self):
return self._dns_rec.name

@property
def zone_id(self):
return self._dns_zone.id

@property
def type(self):
return _azure_record_type(self._dns_rec)

@property
def data(self):
return _azure_record_data(self._dns_rec)

@property
def ttl(self):
return self._dns_rec.ttl

def delete(self):
# pylint:disable=protected-access
return self._provider.dns._records.delete(self._dns_zone, self)
Loading