backend/bms_app/services/ansible/wave.py (161 lines of code) (raw):
# Copyright 2022 Google LLC
#
# 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.
from bms_app.inventory_manager.services import get_client_ip
from .mixins import FileUploadMixin
class AnsibleConfigService(FileUploadMixin):
"""Generate ansible inventory and config files and upload them to GCS.
Files:
- inventory_temp - used by host-provision playbook
- inventory - used by all other playbooks
- asm_config/asm_disk_config_{hostname} - ask disk config
- data_mounts_config/data_mounts_config_{hostname} - data mounts config
"""
INVENTORY_FILE = 'inventory'
INVENTORY_TMP_FILE = 'inventory_temp' # used for host-provision playbook
ASM_CONFIG_DIR = 'asm_config'
DATA_MOUNTS_CONFIG_DIR = 'data_mounts_config'
def __init__(self, db_mappings_objects, gcs_config_dir):
self.db_mappings_objects = db_mappings_objects
self.gcs_config_dir = gcs_config_dir or ''
def run(self):
(main_inventory_data, provision_inventory_data,
asm_configs, data_mounts_configs) = self._generate_configs_data()
self._save_inventories(
main_inventory_data, provision_inventory_data
)
self._save_asm_configs(asm_configs)
self._save_data_mounts_configs(data_mounts_configs)
def _save_inventories(self, main_inventory_data, provision_inventory_data):
self._upload_yaml(
data=main_inventory_data,
file_name=self.INVENTORY_FILE
)
self._upload_yaml(
data=provision_inventory_data,
file_name=self.INVENTORY_TMP_FILE
)
def _save_asm_configs(self, asm_configs):
for host_name, config_content in asm_configs.items():
file_name = f'asm_disk_config_{host_name}.json'
self._upload_json(
data=config_content,
dir_name=self.ASM_CONFIG_DIR,
file_name=file_name
)
def _save_data_mounts_configs(self, data_mounts_configs):
for host_name, config_content in data_mounts_configs.items():
file_name = f'data_mounts_config_{host_name}.json'
self._upload_json(
data=config_content,
dir_name=self.DATA_MOUNTS_CONFIG_DIR,
file_name=file_name
)
def _generate_configs_data(self):
"""Generate all needed ansible configuration data."""
provision_inventory_hosts = {} # hosts by host-provision inventory
main_inventory_hosts = {} # hosts used by all other playbooks
asm_js_configs = {} # asm json config per host
data_js_mounts_configs = {} # data_mounts json config per host
for db_map_dto in self.db_mappings_objects:
config = db_map_dto.db.config
mappings = db_map_dto.mappings
# generate main inventory hosts
main_inventory_hosts.update(
self._generate_main_inventory_hosts(db_map_dto)
)
# generate other configs and provision inventory hosts
for mapping in mappings:
bms = mapping.bms
provision_inventory_hosts.update(
self._generate_single_inventory_host(bms)
)
asm_js_configs[bms.name] = config.asm_config_values
data_js_mounts_configs[bms.name] = config.data_mounts_values
main_inventory_data = self._generate_inventory_file_data(
main_inventory_hosts
)
provision_inventory_data = self._generate_inventory_file_data(
provision_inventory_hosts
)
return (
main_inventory_data,
provision_inventory_data,
asm_js_configs,
data_js_mounts_configs
)
@classmethod
def _generate_main_inventory_hosts(cls, db_map_dto):
if db_map_dto.db.is_rac:
return cls._generate_rac_install_inventory_host(db_map_dto)
return cls._generate_si_install_inventory_host(db_map_dto)
@staticmethod
def _generate_common_inventory_data(source_db, bms):
config = source_db.config
config_data = {
'ansible_ssh_host': get_client_ip(bms),
'ansible_ssh_user': 'customeradmin',
'ansible_ssh_private_key_file': '/root/.ssh/id_rsa_bms_toolkit',
'ansible_ssh_extra_args': '-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o IdentityAgent=no',
'oracle_ver': source_db.oracle_version,
'oracle_edition': source_db.oracle_edition,
'db_name': config.db_params_values['db_name'],
'compatible_rdbms': config.misc_config_values['compatible_rdbms'],
'oracle_user': config.install_config_values['oracle_user'],
'oracle_group': config.install_config_values['oracle_group'],
'oracle_root': config.misc_config_values['oracle_root'],
'home_name': config.install_config_values['home_name'],
'sm_token': bms.secret_name,
}
for param in ('compatible_rdbms', 'swap_blk_device'):
if param in config.misc_config_values:
config_data[param] = config.misc_config_values[param]
return config_data
@classmethod
def _generate_si_install_inventory_host(cls, db_map_dto):
"""Sinlge Instance config."""
source_db = db_map_dto.db
data = {}
for mapping in db_map_dto.mappings:
bms = mapping.bms
config_data = cls._generate_common_inventory_data(source_db, bms)
data[bms.name] = config_data
return data
@classmethod
def _generate_rac_install_inventory_host(cls, db_map_dto):
"""Generate inventory for RAC."""
source_db = db_map_dto.db
config = source_db.config
data = {}
for mapping in db_map_dto.mappings:
bms = mapping.bms
rac_config_values = config.rac_config_values
rc_data = next(
(rc for rc in rac_config_values['rac_nodes']
if rc['node_id'] == bms.id)
)
config_data = cls._generate_common_inventory_data(source_db, bms)
config_data.update({
# rac data
'cluster_type': 'RAC',
'rac_node': mapping.rac_node,
'scan_name': rac_config_values['scan_name'],
'scan_port': rac_config_values['scan_port'],
'cluster_name': rac_config_values['cluster_name'],
'cluster_domain': rac_config_values['cluster_domain'],
'public_net': rac_config_values['public_net'],
'private_net': rac_config_values['private_net'],
'scan_ip1': rac_config_values['scan_ip1'],
'dg_name': rac_config_values['dg_name'],
'vip_name': rc_data['vip_name'],
'vip_ip': rc_data['vip_ip'],
})
for param in ('scan_ip2', 'scan_ip3'):
if param in rac_config_values:
config_data[param] = rac_config_values[param]
data[bms.name] = config_data
return data
@staticmethod
def _generate_single_inventory_host(bms):
"""Generate one host section for host-provision inventory."""
return {
bms.name: {
'ansible_ssh_host': get_client_ip(bms),
'ansible_ssh_user': 'customeradmin',
'sm_token': bms.secret_name or '',
}
}
@staticmethod
def _generate_inventory_file_data(hosts):
"""Generate complete inventory data structure."""
return {
'all': {
'children': {
'dbasm': {
'hosts': hosts
}
}
}
}