or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

backup-services.mdcompute-services.mdcontainer-services.mdcore-driver-system.mddns-management.mdindex.mdload-balancer-services.mdstorage-services.md

dns-management.mddocs/

0

# DNS Management

1

2

The DNS service provides a unified interface for DNS zone and record management across 15+ DNS providers including Route 53, CloudFlare, Google DNS, Rackspace DNS, and many others.

3

4

## Providers

5

6

```python { .api }

7

from libcloud.dns.types import Provider

8

9

class Provider:

10

"""Enumeration of supported DNS providers"""

11

ROUTE53 = 'route53'

12

RACKSPACE = 'rackspace'

13

ZERIGO = 'zerigo'

14

DNSIMPLE = 'dnsimple'

15

LINODE = 'linode'

16

DNSPARK = 'dnspark'

17

CLOUDFLARE = 'cloudflare'

18

GOOGLE = 'google'

19

GANDIAPI = 'gandiapi'

20

POINTDNS = 'pointdns'

21

RCODEZERO = 'rcodezero'

22

NFSN = 'nfsn'

23

NSONE = 'nsone'

24

LIQUIDWEB = 'liquidweb'

25

# ... more providers

26

```

27

28

## Driver Factory

29

30

```python { .api }

31

from libcloud.dns.providers import get_driver

32

33

def get_driver(provider: Provider) -> type[DNSDriver]

34

```

35

36

Get the driver class for a specific DNS provider.

37

38

**Parameters:**

39

- `provider`: Provider identifier from the Provider enum

40

41

**Returns:**

42

- Driver class for the specified provider

43

44

**Example:**

45

```python

46

from libcloud.dns.types import Provider

47

from libcloud.dns.providers import get_driver

48

49

# Get Route 53 driver class

50

cls = get_driver(Provider.ROUTE53)

51

52

# Initialize driver with credentials

53

driver = cls('access_key', 'secret_key')

54

```

55

56

## Core Classes

57

58

### DNSDriver

59

60

```python { .api }

61

class DNSDriver(BaseDriver):

62

"""Base class for all DNS drivers"""

63

64

def list_zones(self) -> List[Zone]

65

def get_zone(self, zone_id: str) -> Zone

66

def create_zone(self, domain: str, type: str = 'master', ttl: int = None, extra: Dict = None) -> Zone

67

def update_zone(self, zone: Zone, domain: str = None, type: str = None, ttl: int = None, extra: Dict = None) -> Zone

68

def delete_zone(self, zone: Zone) -> bool

69

def list_records(self, zone: Zone, type: RecordType = None) -> List[Record]

70

def get_record(self, zone_id: str, record_id: str) -> Record

71

def create_record(self, name: str, zone: Zone, type: RecordType, data: str, extra: Dict = None) -> Record

72

def update_record(self, record: Record, name: str = None, type: RecordType = None, data: str = None, extra: Dict = None) -> Record

73

def delete_record(self, record: Record) -> bool

74

def ex_create_multi_value_record(self, name: str, zone: Zone, type: RecordType, data: List[str], extra: Dict = None) -> List[Record]

75

```

76

77

Base class that all DNS drivers inherit from. Provides methods for managing DNS zones and records.

78

79

**Key Methods:**

80

81

- `list_zones()`: List all DNS zones in the account

82

- `create_zone()`: Create a new DNS zone

83

- `list_records()`: List DNS records in a zone

84

- `create_record()`: Create a new DNS record

85

- `update_record()`: Update an existing DNS record

86

- `delete_record()`: Delete a DNS record

87

88

### Zone

89

90

```python { .api }

91

class Zone:

92

"""Represents a DNS zone/domain"""

93

94

id: str

95

domain: str

96

type: str

97

ttl: int

98

driver: DNSDriver

99

extra: Dict[str, Any]

100

101

def list_records(self, type: RecordType = None) -> List[Record]

102

def create_record(self, name: str, type: RecordType, data: str, extra: Dict = None) -> Record

103

def update_record(self, record: Record, name: str = None, type: RecordType = None, data: str = None, extra: Dict = None) -> Record

104

def delete_record(self, record: Record) -> bool

105

def delete(self) -> bool

106

def update(self, domain: str = None, type: str = None, ttl: int = None, extra: Dict = None) -> Zone

107

```

108

109

Represents a DNS zone (domain) that contains DNS records.

110

111

**Properties:**

112

- `id`: Unique zone identifier

113

- `domain`: Domain name (e.g., "example.com")

114

- `type`: Zone type ("master", "slave", etc.)

115

- `ttl`: Time-to-live in seconds

116

- `extra`: Provider-specific metadata

117

118

**Methods:**

119

- `list_records()`: List records in this zone

120

- `create_record()`: Create a record in this zone

121

- `delete()`: Delete the entire zone

122

123

### Record

124

125

```python { .api }

126

class Record:

127

"""Represents a DNS record"""

128

129

id: str

130

name: str

131

type: RecordType

132

data: str

133

zone: Zone

134

driver: DNSDriver

135

ttl: int

136

extra: Dict[str, Any]

137

138

def update(self, name: str = None, type: RecordType = None, data: str = None, extra: Dict = None) -> Record

139

def delete(self) -> bool

140

```

141

142

Represents a DNS record within a zone.

143

144

**Properties:**

145

- `id`: Unique record identifier

146

- `name`: Record name (subdomain or "@" for root)

147

- `type`: Record type (A, AAAA, CNAME, etc.)

148

- `data`: Record data/value

149

- `zone`: Parent zone

150

- `ttl`: Time-to-live in seconds

151

- `extra`: Provider-specific metadata

152

153

**Methods:**

154

- `update()`: Update this record

155

- `delete()`: Delete this record

156

157

### RecordType

158

159

```python { .api }

160

class RecordType:

161

"""DNS record types enumeration"""

162

A = 'A'

163

AAAA = 'AAAA'

164

CNAME = 'CNAME'

165

MX = 'MX'

166

NS = 'NS'

167

PTR = 'PTR'

168

SOA = 'SOA'

169

SRV = 'SRV'

170

TXT = 'TXT'

171

SPF = 'SPF'

172

CAA = 'CAA'

173

DNAME = 'DNAME'

174

NAPTR = 'NAPTR'

175

HINFO = 'HINFO'

176

SSHFP = 'SSHFP'

177

TLSA = 'TLSA'

178

```

179

180

Enumeration of supported DNS record types.

181

182

## Usage Examples

183

184

### Basic Zone Management

185

186

```python

187

from libcloud.dns.types import Provider, RecordType

188

from libcloud.dns.providers import get_driver

189

190

# Initialize driver

191

cls = get_driver(Provider.ROUTE53)

192

driver = cls('access_key', 'secret_key')

193

194

# List existing zones

195

zones = driver.list_zones()

196

for zone in zones:

197

print(f"Zone: {zone.domain} (Type: {zone.type}, TTL: {zone.ttl})")

198

199

# Create a new zone

200

zone = driver.create_zone(

201

domain='example.com',

202

type='master',

203

ttl=3600

204

)

205

print(f"Created zone: {zone.domain} ({zone.id})")

206

207

# Get a specific zone

208

try:

209

zone = driver.get_zone('Z123456789')

210

print(f"Found zone: {zone.domain}")

211

except Exception as e:

212

print(f"Zone not found: {e}")

213

```

214

215

### DNS Record Management

216

217

```python

218

# List all records in a zone

219

records = driver.list_records(zone)

220

print(f"Zone {zone.domain} has {len(records)} records")

221

222

for record in records:

223

print(f" {record.name} {record.type} {record.data} (TTL: {record.ttl})")

224

225

# Create different types of records

226

# A record

227

a_record = driver.create_record(

228

name='www',

229

zone=zone,

230

type=RecordType.A,

231

data='192.168.1.100'

232

)

233

print(f"Created A record: {a_record.name}.{zone.domain} -> {a_record.data}")

234

235

# CNAME record

236

cname_record = driver.create_record(

237

name='blog',

238

zone=zone,

239

type=RecordType.CNAME,

240

data='www.example.com.'

241

)

242

print(f"Created CNAME record: {cname_record.name}.{zone.domain} -> {cname_record.data}")

243

244

# MX record

245

mx_record = driver.create_record(

246

name='@', # Root domain

247

zone=zone,

248

type=RecordType.MX,

249

data='10 mail.example.com.',

250

extra={'priority': 10} # Some providers store priority in extra

251

)

252

print(f"Created MX record with priority 10")

253

254

# TXT record

255

txt_record = driver.create_record(

256

name='@',

257

zone=zone,

258

type=RecordType.TXT,

259

data='v=spf1 include:_spf.google.com ~all'

260

)

261

print(f"Created TXT record for SPF")

262

```

263

264

### Record Operations Using Zone Methods

265

266

```python

267

# Alternative way using zone methods

268

zone = driver.get_zone('Z123456789')

269

270

# Create record using zone method

271

record = zone.create_record(

272

name='api',

273

type=RecordType.A,

274

data='10.0.1.50',

275

extra={'ttl': 300} # 5 minute TTL

276

)

277

278

# List records in zone using zone method

279

records = zone.list_records(type=RecordType.A)

280

print(f"Found {len(records)} A records")

281

282

# Update a record

283

updated_record = record.update(

284

data='10.0.1.51', # New IP address

285

extra={'ttl': 600} # New TTL

286

)

287

print(f"Updated record: {updated_record.data}")

288

289

# Delete a record

290

success = record.delete()

291

print(f"Record deleted: {success}")

292

```

293

294

### Advanced Record Management

295

296

```python

297

# Create multiple A records for load balancing (if supported)

298

try:

299

records = driver.ex_create_multi_value_record(

300

name='lb',

301

zone=zone,

302

type=RecordType.A,

303

data=['192.168.1.10', '192.168.1.11', '192.168.1.12']

304

)

305

print(f"Created {len(records)} load balancer records")

306

except AttributeError:

307

# Fallback: create individual records

308

ips = ['192.168.1.10', '192.168.1.11', '192.168.1.12']

309

for i, ip in enumerate(ips):

310

record = driver.create_record(

311

name='lb',

312

zone=zone,

313

type=RecordType.A,

314

data=ip

315

)

316

print(f"Created LB record {i+1}: {record.data}")

317

318

# Create SRV record for services

319

srv_record = driver.create_record(

320

name='_http._tcp',

321

zone=zone,

322

type=RecordType.SRV,

323

data='10 5 80 web.example.com.',

324

extra={'priority': 10, 'weight': 5, 'port': 80}

325

)

326

print(f"Created SRV record for HTTP service")

327

328

# Create CAA record for certificate authority authorization

329

caa_record = driver.create_record(

330

name='@',

331

zone=zone,

332

type=RecordType.CAA,

333

data='0 issue "letsencrypt.org"'

334

)

335

print(f"Created CAA record for Let's Encrypt")

336

```

337

338

### Bulk Operations and Management

339

340

```python

341

def setup_basic_dns(driver, domain, config):

342

"""Setup basic DNS records for a domain"""

343

344

# Create or get zone

345

try:

346

zone = None

347

for z in driver.list_zones():

348

if z.domain == domain:

349

zone = z

350

break

351

352

if not zone:

353

zone = driver.create_zone(domain, ttl=3600)

354

print(f"Created zone: {domain}")

355

except Exception as e:

356

print(f"Error creating zone: {e}")

357

return

358

359

# Create basic records

360

records_to_create = [

361

# A records

362

('www', RecordType.A, config['web_ip']),

363

('mail', RecordType.A, config['mail_ip']),

364

('ftp', RecordType.A, config['ftp_ip']),

365

366

# CNAME records

367

('blog', RecordType.CNAME, f'www.{domain}.'),

368

('shop', RecordType.CNAME, f'www.{domain}.'),

369

370

# MX record

371

('@', RecordType.MX, f"10 mail.{domain}."),

372

373

# TXT records

374

('@', RecordType.TXT, config['spf_record']),

375

('_dmarc', RecordType.TXT, config['dmarc_record']),

376

]

377

378

created_records = []

379

for name, record_type, data in records_to_create:

380

try:

381

record = driver.create_record(

382

name=name,

383

zone=zone,

384

type=record_type,

385

data=data

386

)

387

created_records.append(record)

388

print(f"Created: {name} {record_type} {data}")

389

except Exception as e:

390

print(f"Failed to create {name} {record_type}: {e}")

391

392

return zone, created_records

393

394

# Usage

395

dns_config = {

396

'web_ip': '192.168.1.100',

397

'mail_ip': '192.168.1.101',

398

'ftp_ip': '192.168.1.102',

399

'spf_record': 'v=spf1 include:_spf.google.com ~all',

400

'dmarc_record': 'v=DMARC1; p=quarantine; rua=mailto:dmarc@example.com'

401

}

402

403

zone, records = setup_basic_dns(driver, 'example.com', dns_config)

404

```

405

406

### Multi-Provider DNS Management

407

408

```python

409

from libcloud.dns.types import Provider

410

from libcloud.dns.providers import get_driver

411

412

# Configure multiple DNS providers

413

dns_providers = {

414

'route53': {

415

'driver': get_driver(Provider.ROUTE53),

416

'credentials': ('aws_access_key', 'aws_secret_key')

417

},

418

'cloudflare': {

419

'driver': get_driver(Provider.CLOUDFLARE),

420

'credentials': ('email', 'api_key')

421

}

422

}

423

424

# Initialize drivers

425

drivers = {}

426

for name, config in dns_providers.items():

427

cls = config['driver']

428

drivers[name] = cls(*config['credentials'])

429

430

# Compare DNS records across providers

431

def compare_dns_records(domain):

432

"""Compare DNS records for a domain across providers"""

433

provider_records = {}

434

435

for provider_name, driver in drivers.items():

436

try:

437

zones = driver.list_zones()

438

zone = None

439

for z in zones:

440

if z.domain == domain:

441

zone = z

442

break

443

444

if zone:

445

records = driver.list_records(zone)

446

provider_records[provider_name] = {

447

(r.name, r.type): r.data for r in records

448

}

449

print(f"{provider_name}: {len(records)} records")

450

else:

451

print(f"{provider_name}: Domain not found")

452

provider_records[provider_name] = {}

453

except Exception as e:

454

print(f"Error accessing {provider_name}: {e}")

455

provider_records[provider_name] = {}

456

457

# Find differences

458

all_record_keys = set()

459

for records in provider_records.values():

460

all_record_keys.update(records.keys())

461

462

print(f"\nRecord comparison for {domain}:")

463

for key in sorted(all_record_keys):

464

name, record_type = key

465

values = []

466

for provider in provider_records:

467

value = provider_records[provider].get(key, 'MISSING')

468

values.append(f"{provider}: {value}")

469

print(f" {name} {record_type}: {', '.join(values)}")

470

471

# Usage

472

compare_dns_records('example.com')

473

```

474

475

### DNS Record Monitoring and Health Checks

476

477

```python

478

import socket

479

import time

480

from typing import Dict, List

481

482

def verify_dns_propagation(domain: str, record_name: str, record_type: str, expected_value: str, nameservers: List[str] = None) -> Dict[str, str]:

483

"""Verify DNS record propagation across nameservers"""

484

import dns.resolver

485

486

if not nameservers:

487

nameservers = ['8.8.8.8', '1.1.1.1', '208.67.222.222'] # Google, Cloudflare, OpenDNS

488

489

results = {}

490

fqdn = f"{record_name}.{domain}" if record_name != '@' else domain

491

492

for ns in nameservers:

493

try:

494

resolver = dns.resolver.Resolver()

495

resolver.nameservers = [ns]

496

497

answers = resolver.query(fqdn, record_type)

498

actual_value = str(answers[0])

499

500

results[ns] = {

501

'value': actual_value,

502

'matches': actual_value == expected_value,

503

'status': 'OK' if actual_value == expected_value else 'MISMATCH'

504

}

505

except Exception as e:

506

results[ns] = {

507

'value': None,

508

'matches': False,

509

'status': f'ERROR: {e}'

510

}

511

512

return results

513

514

def monitor_dns_changes(driver, zone, record_name: str, check_interval: int = 60):

515

"""Monitor DNS record changes"""

516

print(f"Monitoring DNS changes for {record_name}.{zone.domain}")

517

518

last_values = {}

519

520

while True:

521

try:

522

records = driver.list_records(zone)

523

current_values = {}

524

525

for record in records:

526

if record.name == record_name:

527

key = f"{record.name}_{record.type}"

528

current_values[key] = record.data

529

530

# Check for changes

531

for key, value in current_values.items():

532

if key in last_values and last_values[key] != value:

533

print(f"CHANGE DETECTED: {key} changed from {last_values[key]} to {value}")

534

elif key not in last_values:

535

print(f"NEW RECORD: {key} = {value}")

536

537

# Check for deletions

538

for key in last_values:

539

if key not in current_values:

540

print(f"RECORD DELETED: {key} was {last_values[key]}")

541

542

last_values = current_values.copy()

543

544

except Exception as e:

545

print(f"Error monitoring DNS: {e}")

546

547

time.sleep(check_interval)

548

549

# Usage

550

zone = driver.get_zone('Z123456789')

551

552

# Verify propagation after creating a record

553

record = driver.create_record('test', zone, RecordType.A, '192.168.1.50')

554

time.sleep(10) # Wait a bit for propagation

555

556

propagation_results = verify_dns_propagation('example.com', 'test', 'A', '192.168.1.50')

557

for ns, result in propagation_results.items():

558

print(f"Nameserver {ns}: {result['status']} ({result['value']})")

559

```

560

561

### Zone Backup and Restore

562

563

```python

564

import json

565

from datetime import datetime

566

567

def backup_zone(driver, zone) -> Dict:

568

"""Create a backup of all records in a zone"""

569

backup_data = {

570

'zone': {

571

'domain': zone.domain,

572

'type': zone.type,

573

'ttl': zone.ttl,

574

'extra': zone.extra

575

},

576

'records': [],

577

'backup_timestamp': datetime.now().isoformat()

578

}

579

580

records = driver.list_records(zone)

581

for record in records:

582

backup_data['records'].append({

583

'name': record.name,

584

'type': record.type,

585

'data': record.data,

586

'ttl': record.ttl,

587

'extra': record.extra

588

})

589

590

print(f"Backed up {len(records)} records from {zone.domain}")

591

return backup_data

592

593

def restore_zone(driver, backup_data: Dict, domain: str = None):

594

"""Restore a zone from backup data"""

595

domain = domain or backup_data['zone']['domain']

596

597

# Create zone if it doesn't exist

598

zone = None

599

try:

600

zones = driver.list_zones()

601

for z in zones:

602

if z.domain == domain:

603

zone = z

604

break

605

606

if not zone:

607

zone = driver.create_zone(

608

domain=domain,

609

type=backup_data['zone']['type'],

610

ttl=backup_data['zone']['ttl']

611

)

612

print(f"Created zone: {domain}")

613

except Exception as e:

614

print(f"Error creating zone: {e}")

615

return

616

617

# Restore records

618

restored_count = 0

619

for record_data in backup_data['records']:

620

try:

621

record = driver.create_record(

622

name=record_data['name'],

623

zone=zone,

624

type=record_data['type'],

625

data=record_data['data'],

626

extra=record_data.get('extra', {})

627

)

628

restored_count += 1

629

except Exception as e:

630

print(f"Failed to restore record {record_data['name']}: {e}")

631

632

print(f"Restored {restored_count} records to {domain}")

633

634

# Usage

635

zone = driver.get_zone('Z123456789')

636

637

# Create backup

638

backup = backup_zone(driver, zone)

639

640

# Save backup to file

641

with open(f'dns_backup_{zone.domain}_{datetime.now().strftime("%Y%m%d_%H%M%S")}.json', 'w') as f:

642

json.dump(backup, f, indent=2)

643

644

# Restore from backup (to same or different domain)

645

restore_zone(driver, backup, 'new-example.com')

646

```

647

648

## Exception Handling

649

650

```python { .api }

651

from libcloud.dns.types import (

652

ZoneError,

653

RecordError,

654

ZoneDoesNotExistError,

655

RecordDoesNotExistError,

656

ZoneAlreadyExistsError,

657

RecordAlreadyExistsError

658

)

659

660

class ZoneError(LibcloudError):

661

"""Base zone exception"""

662

663

class RecordError(LibcloudError):

664

"""Base record exception"""

665

666

class ZoneDoesNotExistError(ZoneError):

667

"""Zone does not exist"""

668

669

class RecordDoesNotExistError(RecordError):

670

"""Record does not exist"""

671

672

class ZoneAlreadyExistsError(ZoneError):

673

"""Zone already exists"""

674

675

class RecordAlreadyExistsError(RecordError):

676

"""Record already exists"""

677

```

678

679

**Error Handling Example:**

680

```python

681

from libcloud.dns.types import ZoneDoesNotExistError, RecordAlreadyExistsError

682

from libcloud.common.types import InvalidCredsError

683

684

try:

685

# Attempt to get a zone

686

zone = driver.get_zone('nonexistent-zone-id')

687

except ZoneDoesNotExistError:

688

print("Zone not found, creating new zone...")

689

zone = driver.create_zone('example.com')

690

691

try:

692

# Attempt to create a record

693

record = driver.create_record('www', zone, RecordType.A, '192.168.1.1')

694

except RecordAlreadyExistsError:

695

print("Record already exists, updating instead...")

696

existing_records = driver.list_records(zone, type=RecordType.A)

697

for record in existing_records:

698

if record.name == 'www':

699

record.update(data='192.168.1.1')

700

break

701

702

except InvalidCredsError:

703

print("Invalid DNS provider credentials")

704

```

705

706

## Provider-Specific Features

707

708

Different providers offer additional features through the `ex_*` parameter pattern:

709

710

```python

711

# Route 53 specific features

712

route53_driver = get_driver(Provider.ROUTE53)('access_key', 'secret_key')

713

714

# Create record with Route 53 specific options

715

record = route53_driver.create_record(

716

name='www',

717

zone=zone,

718

type=RecordType.A,

719

data='192.168.1.1',

720

extra={

721

'ttl': 300,

722

'health_check_id': 'hc-123456', # Health check integration

723

'set_identifier': 'primary', # For routing policies

724

'weight': 100 # Weighted routing

725

}

726

)

727

728

# CloudFlare specific features

729

cf_driver = get_driver(Provider.CLOUDFLARE)('email', 'api_key')

730

731

# Create record with CloudFlare proxy enabled

732

record = cf_driver.create_record(

733

name='www',

734

zone=zone,

735

type=RecordType.A,

736

data='192.168.1.1',

737

extra={

738

'proxied': True, # Enable CloudFlare proxy

739

'ttl': 1 # Automatic TTL when proxied

740

}

741

)

742

```

743

744

Check provider-specific documentation for additional capabilities available through the `extra` parameter.