156 lines
4.8 KiB
Python
156 lines
4.8 KiB
Python
# -*- coding: utf-8 -*- #
|
|
# Copyright 2014 Google LLC. 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.
|
|
|
|
"""Helper methods for record-set transactions."""
|
|
|
|
from __future__ import absolute_import
|
|
from __future__ import division
|
|
from __future__ import unicode_literals
|
|
|
|
import os
|
|
|
|
from googlecloudsdk.api_lib.util import apis
|
|
from googlecloudsdk.core import exceptions
|
|
from googlecloudsdk.core import yaml
|
|
from googlecloudsdk.core.resource import resource_printer
|
|
from googlecloudsdk.core.util import files
|
|
|
|
|
|
DEFAULT_PATH = 'transaction.yaml'
|
|
|
|
|
|
class Error(exceptions.Error):
|
|
"""Base exception for all transaction errors."""
|
|
|
|
|
|
class TransactionFileAlreadyExists(Error):
|
|
"""Transaction file already exists."""
|
|
|
|
|
|
class UnableToAccessTransactionFile(Error):
|
|
"""Unable to access transaction file."""
|
|
|
|
|
|
class TransactionFileNotFound(Error):
|
|
"""Transaction file not found."""
|
|
|
|
|
|
class CorruptedTransactionFileError(Error):
|
|
|
|
def __init__(self):
|
|
super(CorruptedTransactionFileError, self).__init__(
|
|
'Corrupted transaction file.\n\n'
|
|
'Please abort and start a new transaction.')
|
|
|
|
|
|
class RecordDoesNotExist(Error):
|
|
"""Specified record-set does not exist."""
|
|
|
|
|
|
def WriteToYamlFile(yaml_file, change):
|
|
"""Writes the given change in yaml format to the given file.
|
|
|
|
Args:
|
|
yaml_file: file, File into which the change should be written.
|
|
change: Change, Change to be written out.
|
|
"""
|
|
resource_printer.Print([change], print_format='yaml', out=yaml_file)
|
|
|
|
|
|
def _RecordSetsFromDictionaries(messages, record_set_dictionaries):
|
|
"""Converts list of record-set dictionaries into list of ResourceRecordSets.
|
|
|
|
Args:
|
|
messages: Messages object for the API with Record Sets to be created.
|
|
record_set_dictionaries: [{str:str}], list of record-sets as dictionaries.
|
|
|
|
Returns:
|
|
list of ResourceRecordSets equivalent to given list of yaml record-sets
|
|
"""
|
|
record_sets = []
|
|
for record_set_dict in record_set_dictionaries:
|
|
record_set = messages.ResourceRecordSet()
|
|
# Need to assign kind to default value for useful equals comparisons.
|
|
record_set.kind = record_set.kind
|
|
record_set.name = record_set_dict['name']
|
|
record_set.ttl = record_set_dict['ttl']
|
|
record_set.type = record_set_dict['type']
|
|
record_set.rrdatas = record_set_dict['rrdatas']
|
|
record_sets.append(record_set)
|
|
return record_sets
|
|
|
|
|
|
def ChangeFromYamlFile(yaml_file, api_version='v1'):
|
|
"""Returns the change contained in the given yaml file.
|
|
|
|
Args:
|
|
yaml_file: file, A yaml file with change.
|
|
api_version: [str], the api version to use for creating the change object.
|
|
|
|
Returns:
|
|
Change, the change contained in the given yaml file.
|
|
|
|
Raises:
|
|
CorruptedTransactionFileError: if the record_set_dictionaries are invalid
|
|
"""
|
|
messages = apis.GetMessagesModule('dns', api_version)
|
|
try:
|
|
change_dict = yaml.load(yaml_file) or {}
|
|
except yaml.YAMLParseError:
|
|
raise CorruptedTransactionFileError()
|
|
if (change_dict.get('additions') is None or
|
|
change_dict.get('deletions') is None):
|
|
raise CorruptedTransactionFileError()
|
|
change = messages.Change()
|
|
change.additions = _RecordSetsFromDictionaries(
|
|
messages, change_dict['additions'])
|
|
change.deletions = _RecordSetsFromDictionaries(
|
|
messages, change_dict['deletions'])
|
|
return change
|
|
|
|
|
|
class TransactionFile(object):
|
|
"""Context for reading/writing from/to a transaction file."""
|
|
|
|
def __init__(self, trans_file_path, mode='r'):
|
|
if not os.path.isfile(trans_file_path):
|
|
raise TransactionFileNotFound(
|
|
'Transaction not found at [{0}]'.format(trans_file_path))
|
|
|
|
self.__trans_file_path = trans_file_path
|
|
|
|
try:
|
|
if mode == 'r':
|
|
self.__trans_file = files.FileReader(trans_file_path)
|
|
elif mode == 'w':
|
|
self.__trans_file = files.FileWriter(trans_file_path)
|
|
else:
|
|
raise ValueError('Unrecognized mode [{}]'.format(mode))
|
|
except IOError as exp:
|
|
msg = 'Unable to open transaction [{0}] because [{1}]'
|
|
msg = msg.format(trans_file_path, exp)
|
|
raise UnableToAccessTransactionFile(msg)
|
|
|
|
def __enter__(self):
|
|
return self.__trans_file
|
|
|
|
def __exit__(self, typ, value, traceback):
|
|
self.__trans_file.close()
|
|
|
|
if typ is IOError or typ is yaml.Error:
|
|
msg = 'Unable to read/write transaction [{0}] because [{1}]'
|
|
msg = msg.format(self.__trans_file_path, value)
|
|
raise UnableToAccessTransactionFile(msg)
|