1""" 2Apache Libcloud DNS Management 3============================== 4 5Connection module for Apache Libcloud DNS management 6 7.. versionadded:: 2016.11.0 8 9:configuration: 10 This module uses a configuration profile for one or multiple DNS providers 11 12 .. code-block:: yaml 13 14 libcloud_dns: 15 profile_test1: 16 driver: cloudflare 17 key: 12345 18 secret: mysecret 19 profile_test2: 20 driver: godaddy 21 key: 12345 22 secret: mysecret 23 shopper_id: 12345 24 25:depends: apache-libcloud 26""" 27# keep lint from choking on _get_conn and _cache_id 28# pylint: disable=E0602 29 30 31import logging 32 33from salt.utils.versions import LooseVersion as _LooseVersion 34 35log = logging.getLogger(__name__) 36 37REQUIRED_LIBCLOUD_VERSION = "2.0.0" 38try: 39 # pylint: disable=unused-import 40 import libcloud 41 from libcloud.dns.providers import get_driver 42 from libcloud.dns.types import RecordType 43 44 # pylint: enable=unused-import 45 if hasattr(libcloud, "__version__") and _LooseVersion( 46 libcloud.__version__ 47 ) < _LooseVersion(REQUIRED_LIBCLOUD_VERSION): 48 raise ImportError() 49 logging.getLogger("libcloud").setLevel(logging.CRITICAL) 50 HAS_LIBCLOUD = True 51except ImportError: 52 HAS_LIBCLOUD = False 53 54 55def __virtual__(): 56 """ 57 Only load if libcloud libraries exist. 58 """ 59 if not HAS_LIBCLOUD: 60 return ( 61 False, 62 "A apache-libcloud library with version at least {} was not found".format( 63 REQUIRED_LIBCLOUD_VERSION 64 ), 65 ) 66 return True 67 68 69def _get_driver(profile): 70 config = __salt__["config.option"]("libcloud_dns")[profile] 71 cls = get_driver(config["driver"]) 72 args = config.copy() 73 del args["driver"] 74 args["key"] = config.get("key") 75 args["secret"] = config.get("secret", None) 76 args["secure"] = config.get("secure", True) 77 args["host"] = config.get("host", None) 78 args["port"] = config.get("port", None) 79 return cls(**args) 80 81 82def list_record_types(profile): 83 """ 84 List available record types for the given profile, e.g. A, AAAA 85 86 :param profile: The profile key 87 :type profile: ``str`` 88 89 CLI Example: 90 91 .. code-block:: bash 92 93 salt myminion libcloud_dns.list_record_types profile1 94 """ 95 conn = _get_driver(profile=profile) 96 return conn.list_record_types() 97 98 99def list_zones(profile): 100 """ 101 List zones for the given profile 102 103 :param profile: The profile key 104 :type profile: ``str`` 105 106 CLI Example: 107 108 .. code-block:: bash 109 110 salt myminion libcloud_dns.list_zones profile1 111 """ 112 conn = _get_driver(profile=profile) 113 return [_simple_zone(zone) for zone in conn.list_zones()] 114 115 116def list_records(zone_id, profile, type=None): 117 """ 118 List records for the given zone_id on the given profile 119 120 :param zone_id: Zone to export. 121 :type zone_id: ``str`` 122 123 :param profile: The profile key 124 :type profile: ``str`` 125 126 :param type: The record type, e.g. A, NS 127 :type type: ``str`` 128 129 CLI Example: 130 131 .. code-block:: bash 132 133 salt myminion libcloud_dns.list_records google.com profile1 134 """ 135 conn = _get_driver(profile=profile) 136 zone = conn.get_zone(zone_id) 137 if type is not None: 138 return [ 139 _simple_record(record) 140 for record in conn.list_records(zone) 141 if record.type == type 142 ] 143 else: 144 return [_simple_record(record) for record in conn.list_records(zone)] 145 146 147def get_zone(zone_id, profile): 148 """ 149 Get zone information for the given zone_id on the given profile 150 151 :param zone_id: Zone to export. 152 :type zone_id: ``str`` 153 154 :param profile: The profile key 155 :type profile: ``str`` 156 157 CLI Example: 158 159 .. code-block:: bash 160 161 salt myminion libcloud_dns.get_zone google.com profile1 162 """ 163 conn = _get_driver(profile=profile) 164 return _simple_zone(conn.get_zone(zone_id)) 165 166 167def get_record(zone_id, record_id, profile): 168 """ 169 Get record information for the given zone_id on the given profile 170 171 :param zone_id: Zone to export. 172 :type zone_id: ``str`` 173 174 :param record_id: Record to delete. 175 :type record_id: ``str`` 176 177 :param profile: The profile key 178 :type profile: ``str`` 179 180 CLI Example: 181 182 .. code-block:: bash 183 184 salt myminion libcloud_dns.get_record google.com www profile1 185 """ 186 conn = _get_driver(profile=profile) 187 return _simple_record(conn.get_record(zone_id, record_id)) 188 189 190def create_zone(domain, profile, type="master", ttl=None): 191 """ 192 Create a new zone. 193 194 :param domain: Zone domain name (e.g. example.com) 195 :type domain: ``str`` 196 197 :param profile: The profile key 198 :type profile: ``str`` 199 200 :param type: Zone type (master / slave). 201 :type type: ``str`` 202 203 :param ttl: TTL for new records. (optional) 204 :type ttl: ``int`` 205 206 CLI Example: 207 208 .. code-block:: bash 209 210 salt myminion libcloud_dns.create_zone google.com profile1 211 """ 212 conn = _get_driver(profile=profile) 213 zone = conn.create_record(domain, type=type, ttl=ttl) 214 return _simple_zone(zone) 215 216 217def update_zone(zone_id, domain, profile, type="master", ttl=None): 218 """ 219 Update an existing zone. 220 221 :param zone_id: Zone ID to update. 222 :type zone_id: ``str`` 223 224 :param domain: Zone domain name (e.g. example.com) 225 :type domain: ``str`` 226 227 :param profile: The profile key 228 :type profile: ``str`` 229 230 :param type: Zone type (master / slave). 231 :type type: ``str`` 232 233 :param ttl: TTL for new records. (optional) 234 :type ttl: ``int`` 235 236 CLI Example: 237 238 .. code-block:: bash 239 240 salt myminion libcloud_dns.update_zone google.com google.com profile1 type=slave 241 """ 242 conn = _get_driver(profile=profile) 243 zone = conn.get_zone(zone_id) 244 return _simple_zone(conn.update_zone(zone=zone, domain=domain, type=type, ttl=ttl)) 245 246 247def create_record(name, zone_id, type, data, profile): 248 """ 249 Create a new record. 250 251 :param name: Record name without the domain name (e.g. www). 252 Note: If you want to create a record for a base domain 253 name, you should specify empty string ('') for this 254 argument. 255 :type name: ``str`` 256 257 :param zone_id: Zone where the requested record is created. 258 :type zone_id: ``str`` 259 260 :param type: DNS record type (A, AAAA, ...). 261 :type type: ``str`` 262 263 :param data: Data for the record (depends on the record type). 264 :type data: ``str`` 265 266 :param profile: The profile key 267 :type profile: ``str`` 268 269 CLI Example: 270 271 .. code-block:: bash 272 273 salt myminion libcloud_dns.create_record www google.com A 12.32.12.2 profile1 274 """ 275 conn = _get_driver(profile=profile) 276 record_type = _string_to_record_type(type) 277 zone = conn.get_zone(zone_id) 278 return _simple_record(conn.create_record(name, zone, record_type, data)) 279 280 281def delete_zone(zone_id, profile): 282 """ 283 Delete a zone. 284 285 :param zone_id: Zone to delete. 286 :type zone_id: ``str`` 287 288 :param profile: The profile key 289 :type profile: ``str`` 290 291 :rtype: ``bool`` 292 293 CLI Example: 294 295 .. code-block:: bash 296 297 salt myminion libcloud_dns.delete_zone google.com profile1 298 """ 299 conn = _get_driver(profile=profile) 300 zone = conn.get_zone(zone_id=zone_id) 301 return conn.delete_zone(zone) 302 303 304def delete_record(zone_id, record_id, profile): 305 """ 306 Delete a record. 307 308 :param zone_id: Zone to delete. 309 :type zone_id: ``str`` 310 311 :param record_id: Record to delete. 312 :type record_id: ``str`` 313 314 :param profile: The profile key 315 :type profile: ``str`` 316 317 :rtype: ``bool`` 318 319 CLI Example: 320 321 .. code-block:: bash 322 323 salt myminion libcloud_dns.delete_record google.com www profile1 324 """ 325 conn = _get_driver(profile=profile) 326 record = conn.get_record(zone_id=zone_id, record_id=record_id) 327 return conn.delete_record(record) 328 329 330def get_bind_data(zone_id, profile): 331 """ 332 Export Zone to the BIND compatible format. 333 334 :param zone_id: Zone to export. 335 :type zone_id: ``str`` 336 337 :param profile: The profile key 338 :type profile: ``str`` 339 340 :return: Zone data in BIND compatible format. 341 :rtype: ``str`` 342 343 CLI Example: 344 345 .. code-block:: bash 346 347 salt myminion libcloud_dns.get_bind_data google.com profile1 348 """ 349 conn = _get_driver(profile=profile) 350 zone = conn.get_zone(zone_id) 351 return conn.export_zone_to_bind_format(zone) 352 353 354def extra(method, profile, **libcloud_kwargs): 355 """ 356 Call an extended method on the driver 357 358 :param method: Driver's method name 359 :type method: ``str`` 360 361 :param profile: The profile key 362 :type profile: ``str`` 363 364 :param libcloud_kwargs: Extra arguments for the driver's delete_container method 365 :type libcloud_kwargs: ``dict`` 366 367 CLI Example: 368 369 .. code-block:: bash 370 371 salt myminion libcloud_dns.extra ex_get_permissions google container_name=my_container object_name=me.jpg --out=yaml 372 """ 373 _sanitize_kwargs(libcloud_kwargs) 374 conn = _get_driver(profile=profile) 375 connection_method = getattr(conn, method) 376 return connection_method(**libcloud_kwargs) 377 378 379def _string_to_record_type(string): 380 """ 381 Return a string representation of a DNS record type to a 382 libcloud RecordType ENUM. 383 384 :param string: A record type, e.g. A, TXT, NS 385 :type string: ``str`` 386 387 :rtype: :class:`RecordType` 388 """ 389 string = string.upper() 390 record_type = getattr(RecordType, string) 391 return record_type 392 393 394def _simple_zone(zone): 395 return { 396 "id": zone.id, 397 "domain": zone.domain, 398 "type": zone.type, 399 "ttl": zone.ttl, 400 "extra": zone.extra, 401 } 402 403 404def _simple_record(record): 405 return { 406 "id": record.id, 407 "name": record.name, 408 "type": record.type, 409 "data": record.data, 410 "zone": _simple_zone(record.zone), 411 "ttl": record.ttl, 412 "extra": record.extra, 413 } 414