def main(profile, is_project_router, only_when_required, cleanup, dry_run,
         router):
    """Router restart and upgrade script"""

    click_log.basic_config()

    log_to_slack = True

    if dry_run:
        log_to_slack = False
        logging.warning('Running in dry-run mode, will only show changes')

    co = CosmicOps(profile=profile, dry_run=dry_run, log_to_slack=log_to_slack)

    router = co.get_router(name=router, is_project_router=is_project_router)
    if not router:
        sys.exit(1)

    logging.instance_name = router['name']
    logging.slack_title = 'Domain'
    logging.slack_value = router['domain']

    host = co.get_host(id=router['hostid'])
    if not host:
        sys.exit(1)

    cluster = co.get_cluster(id=host['clusterid'])
    if not cluster:
        sys.exit(1)

    logging.cluster = cluster['name']

    if only_when_required and not router['requiresupgrade']:
        logging.info(
            f"Router '{router['name']}' does not need to be upgraded. Will not reboot because --only-when-required was specified."
        )
        sys.exit(0)

    if cleanup:
        if not router['vpcid']:
            logging.error(
                f"Cleanup specified but no VPC ID found for router '{router['name']}'"
            )
            sys.exit(1)

        logging.task = 'Restart VPC with clean up'

        vpc = co.get_vpc(id=router['vpcid'])
        if not vpc:
            sys.exit(1)

        if not vpc.restart():
            sys.exit(1)

        logging.info(
            f"Successfully restarted VPC '{vpc['name']}' with cleanup for router '{router['name']}'"
        )
    else:
        logging.task = 'Reboot virtual router'

        if not router.reboot():
            sys.exit(1)

        logging.info(f"Successfully rebooted router '{router['name']}'",
                     log_to_slack)
class TestCosmicOps(TestCase):
    def setUp(self):
        cs_patcher = patch('cosmicops.ops.CloudStack')
        self.mock_cs = cs_patcher.start()
        self.addCleanup(cs_patcher.stop)
        self.cs_instance = self.mock_cs.return_value

        slack_patcher = patch('cosmicops.log.Slack')
        self.mock_slack = slack_patcher.start()
        self.addCleanup(slack_patcher.stop)

        sleep_patcher = patch('time.sleep', return_value=None)
        self.mock_sleep = sleep_patcher.start()
        self.addCleanup(sleep_patcher.stop)

        self.co = CosmicOps(endpoint='https://localhost',
                            key='key',
                            secret='secret')

    @patch('cosmicops.ops._load_cloud_monkey_profile')
    def test_init_with_profile(self, mock_load):
        mock_load.return_value = ('profile_endpoint', 'profile_key',
                                  'profile_secret')
        CosmicOps(profile='config')
        self.mock_cs.assert_called_with('profile_endpoint', 'profile_key',
                                        'profile_secret', 60)

    @tempdir()
    def test_load_cloud_monkey_profile(self, tmp):
        config = (b"[testprofile]\n"
                  b"url = http://localhost:8000/client/api\n"
                  b"apikey = test_api_key\n"
                  b"secretkey = test_secret_key\n")

        tmp.makedir('.cloudmonkey')
        tmp.write('.cloudmonkey/config', config)
        with patch('pathlib.Path.home') as path_home_mock:
            path_home_mock.return_value = Path(tmp.path)
            (endpoint, key, secret) = _load_cloud_monkey_profile('testprofile')

        self.assertEqual('http://localhost:8000/client/api', endpoint)
        self.assertEqual('test_api_key', key)
        self.assertEqual('test_secret_key', secret)

    @tempdir()
    def test_load_cloud_monkey_profile_with_default(self, tmp):
        config = (b"[core]\n"
                  b"profile = profile2\n\n"
                  b"[profile1]\n"
                  b"url = http://localhost:8000/client/api/1\n"
                  b"apikey = test_api_key_1\n"
                  b"secretkey = test_secret_key_1\n\n"
                  b"[profile2]\n"
                  b"url = http://localhost:8000/client/api/2\n"
                  b"apikey = test_api_key_2\n"
                  b"secretkey = test_secret_key_2\n")

        tmp.makedir('.cloudmonkey')
        tmp.write('.cloudmonkey/config', config)
        with patch('pathlib.Path.home') as path_home_mock:
            path_home_mock.return_value = Path(tmp.path)
            (endpoint, key, secret) = _load_cloud_monkey_profile('config')

        self.assertEqual('http://localhost:8000/client/api/2', endpoint)
        self.assertEqual('test_api_key_2', key)
        self.assertEqual('test_secret_key_2', secret)

    def test_cs_get_single_result(self):
        self.cs_instance.listFunction.return_value = [{
            'id': 'id_field',
            'name': 'name_field'
        }]

        result = self.co._cs_get_single_result('listFunction',
                                               {'name': 'name_field'},
                                               CosmicObject, 'type')
        self.cs_instance.listFunction.assert_called_with(fetch_list=True,
                                                         name='name_field')
        self.assertIsInstance(result, CosmicObject)
        self.assertDictEqual({
            'id': 'id_field',
            'name': 'name_field'
        }, result._data)

    def test_get_get_single_result_failure(self):
        self.cs_instance.listFunction.return_value = []
        self.assertIsNone(
            self.co._cs_get_single_result('listFunction', {}, CosmicObject,
                                          'type'))

        self.cs_instance.listFunction.return_value = [{}, {}]
        self.assertIsNone(
            self.co._cs_get_single_result('listFunction', {}, CosmicObject,
                                          'type'))

    def test_cs_get_all_results(self):
        self.cs_instance.listFunction.return_value = [{
            'id': 'id1',
            'name': 'name1'
        }, {
            'id': 'id2',
            'name': 'name2'
        }]

        result = self.co._cs_get_all_results('listFunction', {}, CosmicObject,
                                             'type')
        self.cs_instance.listFunction.assert_called_with(fetch_list=True)
        for i, item in enumerate(result):
            self.assertIsInstance(item, CosmicObject)
            self.assertDictEqual({
                'id': f'id{i + 1}',
                'name': f'name{i + 1}'
            }, item._data)

    def test_get_vm(self):
        self.co._cs_get_single_result = Mock()

        self.co.get_vm(name='vm1')
        self.assertDictEqual({
            'name': 'vm1',
            'listall': True
        }, self.co._cs_get_single_result.call_args[0][1])

        self.co.get_vm(name='i-VM-1')
        self.assertDictEqual({
            'keyword': 'i-VM-1',
            'listall': True
        }, self.co._cs_get_single_result.call_args[0][1])

        self.co.get_vm(name='project_vm1', is_project_vm=True)
        self.assertDictEqual(
            {
                'name': 'project_vm1',
                'projectid': '-1',
                'listall': True
            }, self.co._cs_get_single_result.call_args[0][1])

    def test_get_project_vm(self):
        self.co._cs_get_single_result = Mock()

        self.co.get_project_vm(name='project_vm1')
        self.assertDictEqual(
            {
                'name': 'project_vm1',
                'projectid': '-1',
                'listall': True
            }, self.co._cs_get_single_result.call_args[0][1])

    def test_get_router(self):
        self.co._cs_get_single_result = Mock()

        self.co.get_router(name='router1')
        self.assertDictEqual({
            'name': 'router1',
            'listall': True
        }, self.co._cs_get_single_result.call_args[0][1])

        self.co.get_router(name='project_router1', is_project_router=True)
        self.assertDictEqual(
            {
                'name': 'project_router1',
                'projectid': '-1',
                'listall': True
            }, self.co._cs_get_single_result.call_args[0][1])

    def test_get_cluster(self):
        self.co._cs_get_single_result = Mock()
        self.co.get_zone = Mock(
            return_value=CosmicZone(Mock(), {
                'id': 'z1',
                'name': 'zone1'
            }))

        self.co.get_cluster(name='cluster1')
        self.assertDictEqual({'name': 'cluster1'},
                             self.co._cs_get_single_result.call_args[0][1])

        self.co.get_cluster(name='cluster1', zone='zone1')
        self.assertDictEqual({
            'name': 'cluster1',
            'zoneid': 'z1'
        }, self.co._cs_get_single_result.call_args[0][1])

        self.co.get_zone.return_value = None
        self.assertIsNone(self.co.get_cluster(name='cluster1', zone='zone1'))

    def test_get_all_clusters(self):
        self.co._cs_get_all_results = Mock()

        self.co.get_all_clusters()
        self.assertDictEqual({}, self.co._cs_get_all_results.call_args[0][1])

        self.co.get_all_clusters(zone=CosmicZone(Mock(), {'id': 'z1'}))
        self.assertDictEqual({'zoneid': 'z1'},
                             self.co._cs_get_all_results.call_args[0][1])

        self.co.get_all_clusters(pod=CosmicPod(Mock(), {'id': 'p1'}))
        self.assertDictEqual({'podid': 'p1'},
                             self.co._cs_get_all_results.call_args[0][1])

    def test_get_service_offering(self):
        self.co._cs_get_single_result = Mock()

        self.co.get_service_offering(name='so1')
        self.assertDictEqual({'name': 'so1'},
                             self.co._cs_get_single_result.call_args[0][1])

        self.co.get_service_offering(name='so1', system=True)
        self.assertDictEqual({
            'name': 'so1',
            'issystem': True
        }, self.co._cs_get_single_result.call_args[0][1])

    def test_get_all_vms(self):
        self.co._cs_get_all_results = Mock()

        self.co.get_all_vms()
        self.assertDictEqual({'listall': True},
                             self.co._cs_get_all_results.call_args[0][1])

    def test_get_all_project_vms(self):
        self.co._cs_get_all_results = Mock()

        self.co.get_all_project_vms()
        self.assertDictEqual({
            'projectid': '-1',
            'listall': True
        }, self.co._cs_get_all_results.call_args[0][1])

    def test_wait_for_job(self):
        self.cs_instance.queryAsyncJobResult.return_value = {'jobstatus': '1'}
        self.assertTrue(self.co.wait_for_job('job'))

    def test_wait_for_job_cloud_stack_exception(self):
        self.cs_instance.queryAsyncJobResult.side_effect = CloudStackException(
            response=Mock())
        self.assertRaises(CloudStackException, self.co.wait_for_job, 'job')

    def test_wait_for_job_connection_error(self):
        self.cs_instance.queryAsyncJobResult.side_effect = ConnectionError
        self.assertRaises(ConnectionError, self.co.wait_for_job, 'job')

    def test_wait_for_job_retries(self):
        self.cs_instance.queryAsyncJobResult.side_effect = [
            CloudStackException('multiple JSON fields named jobstatus',
                                response=Mock()),
            ConnectionError('Connection aborted'),
            ConnectionError('Connection aborted')
        ]
        self.assertFalse(self.co.wait_for_job('job', 3))

    def test_wait_for_job_failure(self):
        self.cs_instance.queryAsyncJobResult.return_value = {'jobstatus': '2'}
        self.assertFalse(self.co.wait_for_job('job'))