#!/usr/bin/python3

# Copyright (c) 2026, Thomas Goirand <zigo@debian.org>
#
# 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.

import sys

from keystoneauth1 import exceptions as ka_exceptions
from openstack.config import OpenStackConfig
from openstack import connection
from openstack import exceptions
from oslo_config import cfg
from oslo_log import log as logging

from vigietools import validate_compute

LOG = logging.getLogger(__name__, project='vigietools')


cli_opts_validate_compute = [
    cfg.StrOpt('hosts-to-validate-aggregate',
               default='hosts-to-validate',
               help='Hosts to validate aggregate name. Will be used with the '
                    'enable_isolated_aggregate_filtering nova.conf option to '
                    'select the correct host'),
    cfg.StrOpt('host',
               help='The hostname to validate. Mandatory parameter.'),
    cfg.StrOpt('custom-trait-name',
               default='CUSTOM_COMPUTE_TO_VALIDATE',
               help='Name of the compute host trait to use when validating.'),
    cfg.StrOpt('project-name',
               default='vgt',
               help='Project name to spawn a test VM.'),
    cfg.StrOpt('user-name',
               default='vgt',
               help='User name to spawn a test VM.'),
    cfg.StrOpt('image-name',
               default='Debian 13',
               help='Image to use to spawn the VM.'),
    cfg.StrOpt('flavor-name',
               default='medium-flavor',
               help='Image to use to spawn the VM.'),
    cfg.StrOpt('network-name',
               default='internal-shared-routable-network',
               help='Network name when creating the VM.'),
    cfg.StrOpt('ssh-username',
               default='debian',
               help='SSH username to use when checking SSH connectivity.'),
    cfg.StrOpt('volume-name',
               default='vgt_validation',
               help='Cinder volume name used for compute validation.'),
    cfg.StrOpt('server-name',
               default='vgt-validation-vm',
               help='VM name used for compute validation.'),
    cfg.StrOpt('keypair-name',
               default='vgt-keypair',
               help='SSH keypair name used for compute validation.'),
    cfg.StrOpt('security-group-name',
               default='vgt-sec-group',
               help='Security group name used for compute validation.'),
    cfg.BoolOpt('keep-enabled',
                default=False,
                help='Keep the compute host enabled after validation. '
                     'By default, the host will be disabled after '
                     'validation.'),
]


def create_connection():
    try:
        cloud_region = OpenStackConfig().get_one_cloud()
        conn = connection.Connection(config=cloud_region)

# Nice to check if it works:
#        projects = list(conn.identity.projects())
#        print(f"Connected OK. Found {len(projects)} projects.")

        return conn

    except ka_exceptions.MissingRequiredOptions as e:
        LOG.error("OpenStack authentication failed: Missing required "
                  "credentials (e.g., auth_url, username): %s", e)
        LOG.error("Ensure you have sourced a valid OpenRC file or "
                  "configured clouds.yaml.")
        sys.exit(1)
    except exceptions.ConfigException as e:
        LOG.error("OpenStack configuration error: %s", e)
        LOG.error("Ensure you have sourced a valid OpenRC file or "
                  "configured clouds.yaml.")
        sys.exit(1)

    except Exception as e:
        # Catch-all for connection timeouts, SSL errors,
        # unreachable endpoints, etc.
        LOG.error("Unexpected error creating OpenStack connection.")
        LOG.error("Error: %s", e)
        sys.exit(1)


def register_cmd_opts(conf, cli_opts=None):
    conf.register_cli_opts(cli_opts)
    return conf


def cmd_validate_compute(conf):
    conf = register_cmd_opts(conf, cli_opts_validate_compute)
    logging.register_options(conf)
    sysargs = [arg for arg in sys.argv[1:] if arg != 'validate-compute']
    conf(args=sysargs, project='vigietools', default_config_files=[])
    logging.setup(conf, 'vigietools')

    if not conf.host:
        LOG.error('The --host parameter is mandatory but was not '
                  'provided.')
        sys.exit(1)

    conn = create_connection()

    validate_compute.validate_compute(
        conn=conn,
        hostname=conf.host,
        c_agg=conf.hosts_to_validate_aggregate,
        trait_name=conf.custom_trait_name,
        project_name=conf.project_name,
        user_name=conf.user_name,
        image_name=conf.image_name,
        flavor_name=conf.flavor_name,
        network_name=conf.network_name,
        ssh_username=conf.ssh_username,
        volume_name=conf.volume_name,
        server_name=conf.server_name,
        keypair_name=conf.keypair_name,
        security_group_name=conf.security_group_name,
        keep_enabled=conf.keep_enabled
    )
    return


def cmd_validate_cleanup(conf):
    conf = register_cmd_opts(conf, cli_opts_validate_compute)
    logging.register_options(conf)
    sysargs = [arg for arg in sys.argv[1:] if arg != 'validate-cleanup']
    conf(args=sysargs, project='vigietools', default_config_files=[])
    logging.setup(conf, 'vigietools')
    conn = create_connection()

    validate_compute.validate_cleanup(
        conn=conn,
        c_agg=conf.hosts_to_validate_aggregate,
        trait_name=conf.custom_trait_name,
        project_name=conf.project_name,
        user_name=conf.user_name,
        volume_name=conf.volume_name,
        server_name=conf.server_name,
        keypair_name=conf.keypair_name,
        security_group_name=conf.security_group_name
    )


def main():
    conf = cfg.ConfigOpts()
    if 'validate-compute' in sys.argv:
        cmd_validate_compute(conf)
    elif 'validate-cleanup' in sys.argv:
        cmd_validate_cleanup(conf)
    else:
        print('Not recogized command line argument.')
        print('Choose one of the below commands:')
        print('- validate-compute')
        exit(1)
    exit(0)


if __name__ == '__main__':
    main()
