bc9a1cc9c1
During doc generation, resources have their own templates and need to be separate from non-resource modules. This removes the ``heat`` ``engine`` and ``plugins`` folders and moves all resources into a ``resources`` folder, effectively separating the custom Rackspace Client from custom Rackspace resources. Partial-Bug: #1271226 Change-Id: Idbdc8400d30e2e745a2dc1b2d2fa1b7cff50d57b
206 lines
7.2 KiB
Python
206 lines
7.2 KiB
Python
# 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.
|
|
|
|
try:
|
|
from pyrax.exceptions import NotFound
|
|
except ImportError:
|
|
#Setup fake exception for testing without pyrax
|
|
class NotFound(Exception):
|
|
pass
|
|
|
|
def resource_mapping():
|
|
return {}
|
|
else:
|
|
|
|
def resource_mapping():
|
|
return {'Rackspace::Cloud::DNS': CloudDns}
|
|
|
|
from heat.common import exception
|
|
from heat.engine import constraints
|
|
from heat.engine import properties
|
|
from heat.engine import resource
|
|
from heat.openstack.common import log as logging
|
|
from heat.openstack.common.gettextutils import _
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class CloudDns(resource.Resource):
|
|
|
|
PROPERTIES = (
|
|
NAME, EMAIL_ADDRESS, TTL, COMMENT, RECORDS,
|
|
) = (
|
|
'name', 'emailAddress', 'ttl', 'comment', 'records',
|
|
)
|
|
|
|
_RECORD_KEYS = (
|
|
RECORD_COMMENT, RECORD_NAME, RECORD_DATA, RECORD_PRIORITY, RECORD_TTL,
|
|
RECORD_TYPE,
|
|
) = (
|
|
'comment', 'name', 'data', 'priority', 'ttl',
|
|
'type',
|
|
)
|
|
|
|
properties_schema = {
|
|
NAME: properties.Schema(
|
|
properties.Schema.STRING,
|
|
_('Specifies the name for the domain or subdomain. Must be a '
|
|
'valid domain name.'),
|
|
required=True,
|
|
constraints=[
|
|
constraints.Length(min=3),
|
|
]
|
|
),
|
|
EMAIL_ADDRESS: properties.Schema(
|
|
properties.Schema.STRING,
|
|
_('Email address to use for contacting the domain administrator.'),
|
|
required=True,
|
|
update_allowed=True
|
|
),
|
|
TTL: properties.Schema(
|
|
properties.Schema.INTEGER,
|
|
_('How long other servers should cache recorddata.'),
|
|
default=3600,
|
|
constraints=[
|
|
constraints.Range(min=301),
|
|
],
|
|
update_allowed=True
|
|
),
|
|
COMMENT: properties.Schema(
|
|
properties.Schema.STRING,
|
|
_('Optional free form text comment'),
|
|
constraints=[
|
|
constraints.Length(max=160),
|
|
],
|
|
update_allowed=True
|
|
),
|
|
RECORDS: properties.Schema(
|
|
properties.Schema.LIST,
|
|
_('Domain records'),
|
|
schema=properties.Schema(
|
|
properties.Schema.MAP,
|
|
schema={
|
|
RECORD_COMMENT: properties.Schema(
|
|
properties.Schema.STRING,
|
|
_('Optional free form text comment'),
|
|
constraints=[
|
|
constraints.Length(max=160),
|
|
]
|
|
),
|
|
RECORD_NAME: properties.Schema(
|
|
properties.Schema.STRING,
|
|
_('Specifies the name for the domain or '
|
|
'subdomain. Must be a valid domain name.'),
|
|
required=True,
|
|
constraints=[
|
|
constraints.Length(min=3),
|
|
]
|
|
),
|
|
RECORD_DATA: properties.Schema(
|
|
properties.Schema.STRING,
|
|
_('Type specific record data'),
|
|
required=True
|
|
),
|
|
RECORD_PRIORITY: properties.Schema(
|
|
properties.Schema.INTEGER,
|
|
_('Required for MX and SRV records, but '
|
|
'forbidden for other record types. If '
|
|
'specified, must be an integer from 0 to '
|
|
'65535.'),
|
|
constraints=[
|
|
constraints.Range(0, 65535),
|
|
]
|
|
),
|
|
RECORD_TTL: properties.Schema(
|
|
properties.Schema.INTEGER,
|
|
_('How long other servers should cache '
|
|
'recorddata.'),
|
|
default=3600,
|
|
constraints=[
|
|
constraints.Range(min=301),
|
|
]
|
|
),
|
|
RECORD_TYPE: properties.Schema(
|
|
properties.Schema.STRING,
|
|
_('Specifies the record type.'),
|
|
required=True,
|
|
constraints=[
|
|
constraints.AllowedValues(['A', 'AAAA', 'NS',
|
|
'MX', 'CNAME',
|
|
'TXT', 'SRV']),
|
|
]
|
|
),
|
|
},
|
|
),
|
|
update_allowed=True
|
|
),
|
|
}
|
|
|
|
update_allowed_keys = ('Properties',)
|
|
|
|
def cloud_dns(self):
|
|
return self.stack.clients.cloud_dns()
|
|
|
|
def handle_create(self):
|
|
"""
|
|
Create a Rackspace CloudDns Instance.
|
|
"""
|
|
# There is no check_create_complete as the pyrax create for DNS is
|
|
# synchronous.
|
|
logger.debug(_("CloudDns handle_create called."))
|
|
args = dict((k, v) for k, v in self.properties.items())
|
|
for rec in args[self.RECORDS] or {}:
|
|
# only pop the priority for the correct types
|
|
rec_type = rec[self.RECORD_TYPE]
|
|
if (rec_type != 'MX') and (rec_type != 'SRV'):
|
|
rec.pop(self.RECORD_PRIORITY, None)
|
|
dom = self.cloud_dns().create(**args)
|
|
self.resource_id_set(dom.id)
|
|
|
|
def handle_update(self, json_snippet, tmpl_diff, prop_diff):
|
|
"""
|
|
Update a Rackspace CloudDns Instance.
|
|
"""
|
|
logger.debug(_("CloudDns handle_update called."))
|
|
if not self.resource_id:
|
|
raise exception.Error(_('Update called on a non-existent domain'))
|
|
if prop_diff:
|
|
dom = self.cloud_dns().get(self.resource_id)
|
|
|
|
# handle records separately
|
|
records = prop_diff.pop(self.RECORDS, {})
|
|
|
|
# Handle top level domain properties
|
|
dom.update(**prop_diff)
|
|
|
|
# handle records
|
|
if records:
|
|
recs = dom.list_records()
|
|
# 1. delete all the current records other than rackspace NS records
|
|
[rec.delete() for rec in recs if rec.type != 'NS' or
|
|
'stabletransit.com' not in rec.data]
|
|
# 2. update with the new records in prop_diff
|
|
dom.add_records(records)
|
|
|
|
def handle_delete(self):
|
|
"""
|
|
Delete a Rackspace CloudDns Instance.
|
|
"""
|
|
logger.debug(_("CloudDns handle_delete called."))
|
|
if self.resource_id:
|
|
try:
|
|
dom = self.cloud_dns().get(self.resource_id)
|
|
dom.delete()
|
|
except NotFound:
|
|
pass
|
|
self.resource_id_set(None)
|