Esempio n. 1
0
 def setUp(self):
     GoogleDNSMockHttp.test = self
     GoogleDNSDriver.connectionCls.conn_class = GoogleDNSMockHttp
     GoogleBaseAuthConnection.conn_class = GoogleAuthMockHttp
     GoogleDNSMockHttp.type = None
     kwargs = DNS_KEYWORD_PARAMS_GOOGLE.copy()
     kwargs["auth_type"] = "IA"
     self.driver = GoogleDNSDriver(*DNS_PARAMS_GOOGLE, **kwargs)
Esempio n. 2
0
 def setUp(self):
     GoogleDNSMockHttp.test = self
     GoogleDNSDriver.connectionCls.conn_classes = (GoogleDNSMockHttp,
                                                   GoogleDNSMockHttp)
     GoogleBaseAuthConnection.conn_classes = (GoogleAuthMockHttp,
                                              GoogleAuthMockHttp)
     GoogleDNSMockHttp.type = None
     kwargs = DNS_KEYWORD_PARAMS_GOOGLE.copy()
     kwargs['auth_type'] = 'IA'
     self.driver = GoogleDNSDriver(*DNS_PARAMS_GOOGLE, **kwargs)
Esempio n. 3
0
 def __init__(self, config, gcp_credentials_file):
     self.config = config
     creds = read_json(gcp_credentials_file)
     self._compute = GCENodeDriver(
         creds['client_email'],
         gcp_credentials_file,
         project=creds['project_id'],
         datacenter=self.config['gcp_compute_zone'],
         timeout=self.config['compute_timeout'])
     self._dns = GoogleDNSDriver(creds['client_email'],
                                 gcp_credentials_file,
                                 project=creds['project_id'])
     self._dns_zone = self._dns.get_zone(config['gcp_dns_zone'])
     self.log = logging.getLogger(__name__ + '.' + self._name)
Esempio n. 4
0
 def setUp(self):
     GoogleDNSMockHttp.test = self
     GoogleDNSDriver.connectionCls.conn_classes = (GoogleDNSMockHttp, GoogleDNSMockHttp)
     GoogleBaseAuthConnection.conn_classes = (GoogleAuthMockHttp, GoogleAuthMockHttp)
     GoogleDNSMockHttp.type = None
     kwargs = DNS_KEYWORD_PARAMS_GOOGLE.copy()
     kwargs["auth_type"] = "IA"
     self.driver = GoogleDNSDriver(*DNS_PARAMS_GOOGLE, **kwargs)
Esempio n. 5
0
 def setUp(self):
     GoogleDNSMockHttp.test = self
     GoogleDNSDriver.connectionCls.conn_class = GoogleDNSMockHttp
     GoogleBaseAuthConnection.conn_class = GoogleAuthMockHttp
     GoogleDNSMockHttp.type = None
     kwargs = DNS_KEYWORD_PARAMS_GOOGLE.copy()
     kwargs['auth_type'] = 'IA'
     self.driver = GoogleDNSDriver(*DNS_PARAMS_GOOGLE, **kwargs)
Esempio n. 6
0
    def create_driver(self, auth_file):
        """
        Generates driver class which interacts with GCP Directly

        (Args):
            auth_file (string path) relative path for google_auth construct
                (ie config/google_auth.file)(https://cloud.google.com/compute/docs/access/service-accounts)

        """
        log.info("Creating Driver")
        return GoogleDNSDriver(self.google_auth['client_email'], auth_file,
                               self.google_auth['project_id'])
Esempio n. 7
0
class GoogleTests(LibcloudTestCase):
    GoogleBaseConnection._get_token_info_from_file = lambda x: None
    GoogleBaseConnection._write_token_info_to_file = lambda x: None
    GoogleInstalledAppAuthConnection.get_code = lambda x: '1234'

    def setUp(self):
        GoogleDNSMockHttp.test = self
        GoogleDNSDriver.connectionCls.conn_classes = (GoogleDNSMockHttp,
                                                      GoogleDNSMockHttp)
        GoogleBaseAuthConnection.conn_classes = (GoogleAuthMockHttp,
                                                 GoogleAuthMockHttp)
        GoogleDNSMockHttp.type = None
        kwargs = DNS_KEYWORD_PARAMS_GOOGLE.copy()
        kwargs['auth_type'] = 'IA'
        self.driver = GoogleDNSDriver(*DNS_PARAMS_GOOGLE, **kwargs)

    def test_default_scopes(self):
        self.assertEqual(self.driver.scopes, None)

    def test_list_zones(self):
        zones = self.driver.list_zones()
        self.assertEqual(len(zones), 2)

    def test_list_records(self):
        zone = self.driver.list_zones()[0]
        records = self.driver.list_records(zone=zone)
        self.assertEqual(len(records), 3)

    def test_get_zone(self):
        zone = self.driver.get_zone('example-com')
        self.assertEqual(zone.id, 'example-com')
        self.assertEqual(zone.domain, 'example.com.')

    def test_get_zone_does_not_exist(self):
        GoogleDNSMockHttp.type = 'ZONE_DOES_NOT_EXIST'

        try:
            self.driver.get_zone('example-com')
        except ZoneDoesNotExistError:
            e = sys.exc_info()[1]
            self.assertEqual(e.zone_id, 'example-com')
        else:
            self.fail('Exception not thrown')

    def test_get_record(self):
        GoogleDNSMockHttp.type = 'FILTER_ZONES'
        zone = self.driver.list_zones()[0]
        record = self.driver.get_record(zone.id, "A:foo.example.com.")
        self.assertEqual(record.id, 'A:foo.example.com.')
        self.assertEqual(record.name, 'foo.example.com.')
        self.assertEqual(record.type, 'A')
        self.assertEqual(record.zone.id, 'example-com')

    def test_get_record_zone_does_not_exist(self):
        GoogleDNSMockHttp.type = 'ZONE_DOES_NOT_EXIST'

        try:
            self.driver.get_record('example-com', 'a:a')
        except ZoneDoesNotExistError:
            e = sys.exc_info()[1]
            self.assertEqual(e.zone_id, 'example-com')
        else:
            self.fail('Exception not thrown')

    def test_get_record_record_does_not_exist(self):
        GoogleDNSMockHttp.type = 'RECORD_DOES_NOT_EXIST'
        try:
            self.driver.get_record('example-com', "A:foo")
        except RecordDoesNotExistError:
            e = sys.exc_info()[1]
            self.assertEqual(e.record_id, 'A:foo')
        else:
            self.fail('Exception not thrown')

    def test_create_zone(self):
        extra = {'description': 'new domain for example.org'}
        zone = self.driver.create_zone('example.org.', extra)
        self.assertEqual(zone.domain, 'example.org.')
        self.assertEqual(zone.extra['description'], extra['description'])
        self.assertEqual(len(zone.extra['nameServers']), 4)

    def test_delete_zone(self):
        zone = self.driver.get_zone('example-com')
        res = self.driver.delete_zone(zone)
        self.assertTrue(res)
Esempio n. 8
0
class GoogleTests(LibcloudTestCase):
    GoogleBaseConnection._get_token_info_from_file = lambda x: None
    GoogleBaseConnection._write_token_info_to_file = lambda x: None
    GoogleInstalledAppAuthConnection.get_code = lambda x: "1234"

    def setUp(self):
        GoogleDNSMockHttp.test = self
        GoogleDNSDriver.connectionCls.conn_classes = (GoogleDNSMockHttp, GoogleDNSMockHttp)
        GoogleBaseAuthConnection.conn_classes = (GoogleAuthMockHttp, GoogleAuthMockHttp)
        GoogleDNSMockHttp.type = None
        kwargs = DNS_KEYWORD_PARAMS_GOOGLE.copy()
        kwargs["auth_type"] = "IA"
        self.driver = GoogleDNSDriver(*DNS_PARAMS_GOOGLE, **kwargs)

    def test_default_scopes(self):
        self.assertEqual(self.driver.scopes, None)

    def test_list_zones(self):
        zones = self.driver.list_zones()
        self.assertEqual(len(zones), 2)

    def test_list_records(self):
        zone = self.driver.list_zones()[0]
        records = self.driver.list_records(zone=zone)
        self.assertEqual(len(records), 3)

    def test_get_zone(self):
        zone = self.driver.get_zone("example-com")
        self.assertEqual(zone.id, "example-com")
        self.assertEqual(zone.domain, "example.com.")

    def test_get_zone_does_not_exist(self):
        GoogleDNSMockHttp.type = "ZONE_DOES_NOT_EXIST"

        try:
            self.driver.get_zone("example-com")
        except ZoneDoesNotExistError:
            e = sys.exc_info()[1]
            self.assertEqual(e.zone_id, "example-com")
        else:
            self.fail("Exception not thrown")

    def test_get_record(self):
        GoogleDNSMockHttp.type = "FILTER_ZONES"
        zone = self.driver.list_zones()[0]
        record = self.driver.get_record(zone.id, "A:foo.example.com.")
        self.assertEqual(record.id, "A:foo.example.com.")
        self.assertEqual(record.name, "foo.example.com.")
        self.assertEqual(record.type, "A")
        self.assertEqual(record.zone.id, "example-com")

    def test_get_record_zone_does_not_exist(self):
        GoogleDNSMockHttp.type = "ZONE_DOES_NOT_EXIST"

        try:
            self.driver.get_record("example-com", "a:a")
        except ZoneDoesNotExistError:
            e = sys.exc_info()[1]
            self.assertEqual(e.zone_id, "example-com")
        else:
            self.fail("Exception not thrown")

    def test_get_record_record_does_not_exist(self):
        GoogleDNSMockHttp.type = "RECORD_DOES_NOT_EXIST"
        try:
            self.driver.get_record("example-com", "A:foo")
        except RecordDoesNotExistError:
            e = sys.exc_info()[1]
            self.assertEqual(e.record_id, "A:foo")
        else:
            self.fail("Exception not thrown")

    def test_create_zone(self):
        extra = {"description": "new domain for example.org"}
        zone = self.driver.create_zone("example.org.", extra)
        self.assertEqual(zone.domain, "example.org.")
        self.assertEqual(zone.extra["description"], extra["description"])
        self.assertEqual(len(zone.extra["nameServers"]), 4)

    def test_delete_zone(self):
        zone = self.driver.get_zone("example-com")
        res = self.driver.delete_zone(zone)
        self.assertTrue(res)
Esempio n. 9
0
class GoogleTests(GoogleTestCase):
    def setUp(self):
        GoogleDNSMockHttp.test = self
        GoogleDNSDriver.connectionCls.conn_class = GoogleDNSMockHttp
        GoogleBaseAuthConnection.conn_class = GoogleAuthMockHttp
        GoogleDNSMockHttp.type = None
        kwargs = DNS_KEYWORD_PARAMS_GOOGLE.copy()
        kwargs["auth_type"] = "IA"
        self.driver = GoogleDNSDriver(*DNS_PARAMS_GOOGLE, **kwargs)

    def test_default_scopes(self):
        self.assertIsNone(self.driver.scopes)

    def test_list_zones(self):
        zones = self.driver.list_zones()
        self.assertEqual(len(zones), 2)

    def test_list_records(self):
        zone = self.driver.list_zones()[0]
        records = self.driver.list_records(zone=zone)
        self.assertEqual(len(records), 3)

    def test_get_zone(self):
        zone = self.driver.get_zone("example-com")
        self.assertEqual(zone.id, "example-com")
        self.assertEqual(zone.domain, "example.com.")

    def test_get_zone_does_not_exist(self):
        GoogleDNSMockHttp.type = "ZONE_DOES_NOT_EXIST"

        try:
            self.driver.get_zone("example-com")
        except ZoneDoesNotExistError as e:
            self.assertEqual(e.zone_id, "example-com")
        else:
            self.fail("Exception not thrown")

    def test_get_record(self):
        GoogleDNSMockHttp.type = "FILTER_ZONES"
        zone = self.driver.list_zones()[0]
        record = self.driver.get_record(zone.id, "A:foo.example.com.")
        self.assertEqual(record.id, "A:foo.example.com.")
        self.assertEqual(record.name, "foo.example.com.")
        self.assertEqual(record.type, "A")
        self.assertEqual(record.zone.id, "example-com")

    def test_get_record_zone_does_not_exist(self):
        GoogleDNSMockHttp.type = "ZONE_DOES_NOT_EXIST"

        try:
            self.driver.get_record("example-com", "a:a")
        except ZoneDoesNotExistError as e:
            self.assertEqual(e.zone_id, "example-com")
        else:
            self.fail("Exception not thrown")

    def test_get_record_record_does_not_exist(self):
        GoogleDNSMockHttp.type = "RECORD_DOES_NOT_EXIST"
        try:
            self.driver.get_record("example-com", "A:foo")
        except RecordDoesNotExistError as e:
            self.assertEqual(e.record_id, "A:foo")
        else:
            self.fail("Exception not thrown")

    def test_create_zone(self):
        extra = {"description": "new domain for example.org"}
        zone = self.driver.create_zone("example.org.", extra)
        self.assertEqual(zone.domain, "example.org.")
        self.assertEqual(zone.extra["description"], extra["description"])
        self.assertEqual(len(zone.extra["nameServers"]), 4)

    def test_delete_zone(self):
        zone = self.driver.get_zone("example-com")
        res = self.driver.delete_zone(zone)
        self.assertTrue(res)

    def test_ex_bulk_record_changes(self):
        zone = self.driver.get_zone("example-com")
        records = self.driver.ex_bulk_record_changes(zone, {})

        self.assertEqual(records["additions"][0].name, "foo.example.com.")
        self.assertEqual(records["additions"][0].type, "A")

        self.assertEqual(records["deletions"][0].name, "bar.example.com.")
        self.assertEqual(records["deletions"][0].type, "A")
Esempio n. 10
0
class GoogleTests(GoogleTestCase):

    def setUp(self):
        GoogleDNSMockHttp.test = self
        GoogleDNSDriver.connectionCls.conn_class = GoogleDNSMockHttp
        GoogleBaseAuthConnection.conn_class = GoogleAuthMockHttp
        GoogleDNSMockHttp.type = None
        kwargs = DNS_KEYWORD_PARAMS_GOOGLE.copy()
        kwargs['auth_type'] = 'IA'
        self.driver = GoogleDNSDriver(*DNS_PARAMS_GOOGLE, **kwargs)

    def test_default_scopes(self):
        self.assertEqual(self.driver.scopes, None)

    def test_list_zones(self):
        zones = self.driver.list_zones()
        self.assertEqual(len(zones), 2)

    def test_list_records(self):
        zone = self.driver.list_zones()[0]
        records = self.driver.list_records(zone=zone)
        self.assertEqual(len(records), 3)

    def test_get_zone(self):
        zone = self.driver.get_zone('example-com')
        self.assertEqual(zone.id, 'example-com')
        self.assertEqual(zone.domain, 'example.com.')

    def test_get_zone_does_not_exist(self):
        GoogleDNSMockHttp.type = 'ZONE_DOES_NOT_EXIST'

        try:
            self.driver.get_zone('example-com')
        except ZoneDoesNotExistError:
            e = sys.exc_info()[1]
            self.assertEqual(e.zone_id, 'example-com')
        else:
            self.fail('Exception not thrown')

    def test_get_record(self):
        GoogleDNSMockHttp.type = 'FILTER_ZONES'
        zone = self.driver.list_zones()[0]
        record = self.driver.get_record(zone.id, "A:foo.example.com.")
        self.assertEqual(record.id, 'A:foo.example.com.')
        self.assertEqual(record.name, 'foo.example.com.')
        self.assertEqual(record.type, 'A')
        self.assertEqual(record.zone.id, 'example-com')

    def test_get_record_zone_does_not_exist(self):
        GoogleDNSMockHttp.type = 'ZONE_DOES_NOT_EXIST'

        try:
            self.driver.get_record('example-com', 'a:a')
        except ZoneDoesNotExistError:
            e = sys.exc_info()[1]
            self.assertEqual(e.zone_id, 'example-com')
        else:
            self.fail('Exception not thrown')

    def test_get_record_record_does_not_exist(self):
        GoogleDNSMockHttp.type = 'RECORD_DOES_NOT_EXIST'
        try:
            self.driver.get_record('example-com', "A:foo")
        except RecordDoesNotExistError:
            e = sys.exc_info()[1]
            self.assertEqual(e.record_id, 'A:foo')
        else:
            self.fail('Exception not thrown')

    def test_create_zone(self):
        extra = {'description': 'new domain for example.org'}
        zone = self.driver.create_zone('example.org.', extra)
        self.assertEqual(zone.domain, 'example.org.')
        self.assertEqual(zone.extra['description'], extra['description'])
        self.assertEqual(len(zone.extra['nameServers']), 4)

    def test_delete_zone(self):
        zone = self.driver.get_zone('example-com')
        res = self.driver.delete_zone(zone)
        self.assertTrue(res)

    def test_ex_bulk_record_changes(self):
        zone = self.driver.get_zone('example-com')
        records = self.driver.ex_bulk_record_changes(zone, {})

        self.assertEqual(records['additions'][0].name, 'foo.example.com.')
        self.assertEqual(records['additions'][0].type, 'A')

        self.assertEqual(records['deletions'][0].name, 'bar.example.com.')
        self.assertEqual(records['deletions'][0].type, 'A')
class GoogleDNSUpdater:
    dns_driver = None

    def __init__(self):
        self.create_dns_driver()

    def create_dns_driver(self):
        self.dns_driver = GoogleDNSDriver(Config.DNS_USER_ID, Config.DNS_KEY, Config.DNS_PROJECT_NAME)

    def list_zones(self):
        return self.dns_driver.list_zones()

    def format_record_name(self, name, zoneDomain):
        return "{}.{}".format(name, zoneDomain)

    def get_record_id_for_record_name(self, zone, name):
        for record in self.dns_driver.list_records(zone):
            if record.type != RecordType.A:
                continue
            if self.format_record_name(name, zone.domain) == record.name:
                return record.id

    def create_or_update_record(self, zone=None, record_name=None, a_record_value=None, ttl_seconds=3600):
        if None in (zone, record_name, a_record_value):
            return False

        formatted_record_name = self.format_record_name(record_name, zone.domain)

        # Try locating existing record with the same name
        dns_record = None
        try:
            record_id = self.get_record_id_for_record_name(zone, record_name)
            if record_id:
                dns_record = self.dns_driver.get_record(zone.id, record_id)
        except LibcloudError as e:
            print("Error locating record: {}".format(e.message))

        # Set record data
        record_data = {
            "ttl": ttl_seconds,
            "rrdatas": [a_record_value]
        }

        # Create or update an existing record with record_data
        if not dns_record:
            return self.dns_driver.create_record(formatted_record_name, zone, RecordType.A, record_data)
        elif a_record_value in dns_record.data['rrdatas']:
            print(f"{dns_record.name} is already set to {a_record_value}. Not updating.")
            return True
        else:
            if self.dns_driver.delete_record(dns_record):
                return self.dns_driver.create_record(formatted_record_name, zone, RecordType.A, record_data)
            else:
                return False

    def update_record_ip(self, zone_name, record_name, ip, ttl_seconds):
        for zone in self.list_zones():
            if zone.domain == zone_name:
                print("Setting A record: {}.{} to point: {}".format(record_name, zone.domain, ip))
                return gdns.create_or_update_record(zone, record_name, ip, ttl_seconds)
        return False
 def create_dns_driver(self):
     self.dns_driver = GoogleDNSDriver(Config.DNS_USER_ID, Config.DNS_KEY, Config.DNS_PROJECT_NAME)
Esempio n. 13
0
 def create_dns_driver(self):
     self.dns_driver = GoogleDNSDriver(Config.DNS_USER_ID, Config.DNS_KEY,
                                       args.dns_project_name)
Esempio n. 14
0
class GoogleTests(GoogleTestCase):
    def setUp(self):
        GoogleDNSMockHttp.test = self
        GoogleDNSDriver.connectionCls.conn_class = GoogleDNSMockHttp
        GoogleBaseAuthConnection.conn_class = GoogleAuthMockHttp
        GoogleDNSMockHttp.type = None
        kwargs = DNS_KEYWORD_PARAMS_GOOGLE.copy()
        kwargs['auth_type'] = 'IA'
        self.driver = GoogleDNSDriver(*DNS_PARAMS_GOOGLE, **kwargs)

    def test_default_scopes(self):
        self.assertIsNone(self.driver.scopes)

    def test_list_zones(self):
        zones = self.driver.list_zones()
        self.assertEqual(len(zones), 2)

    def test_list_records(self):
        zone = self.driver.list_zones()[0]
        records = self.driver.list_records(zone=zone)
        self.assertEqual(len(records), 3)

    def test_get_zone(self):
        zone = self.driver.get_zone('example-com')
        self.assertEqual(zone.id, 'example-com')
        self.assertEqual(zone.domain, 'example.com.')

    def test_get_zone_does_not_exist(self):
        GoogleDNSMockHttp.type = 'ZONE_DOES_NOT_EXIST'

        try:
            self.driver.get_zone('example-com')
        except ZoneDoesNotExistError as e:
            self.assertEqual(e.zone_id, 'example-com')
        else:
            self.fail('Exception not thrown')

    def test_get_record(self):
        GoogleDNSMockHttp.type = 'FILTER_ZONES'
        zone = self.driver.list_zones()[0]
        record = self.driver.get_record(zone.id, "A:foo.example.com.")
        self.assertEqual(record.id, 'A:foo.example.com.')
        self.assertEqual(record.name, 'foo.example.com.')
        self.assertEqual(record.type, 'A')
        self.assertEqual(record.zone.id, 'example-com')

    def test_get_record_zone_does_not_exist(self):
        GoogleDNSMockHttp.type = 'ZONE_DOES_NOT_EXIST'

        try:
            self.driver.get_record('example-com', 'a:a')
        except ZoneDoesNotExistError as e:
            self.assertEqual(e.zone_id, 'example-com')
        else:
            self.fail('Exception not thrown')

    def test_get_record_record_does_not_exist(self):
        GoogleDNSMockHttp.type = 'RECORD_DOES_NOT_EXIST'
        try:
            self.driver.get_record('example-com', "A:foo")
        except RecordDoesNotExistError as e:
            self.assertEqual(e.record_id, 'A:foo')
        else:
            self.fail('Exception not thrown')

    def test_create_zone(self):
        extra = {'description': 'new domain for example.org'}
        zone = self.driver.create_zone('example.org.', extra)
        self.assertEqual(zone.domain, 'example.org.')
        self.assertEqual(zone.extra['description'], extra['description'])
        self.assertEqual(len(zone.extra['nameServers']), 4)

    def test_delete_zone(self):
        zone = self.driver.get_zone('example-com')
        res = self.driver.delete_zone(zone)
        self.assertTrue(res)

    def test_ex_bulk_record_changes(self):
        zone = self.driver.get_zone('example-com')
        records = self.driver.ex_bulk_record_changes(zone, {})

        self.assertEqual(records['additions'][0].name, 'foo.example.com.')
        self.assertEqual(records['additions'][0].type, 'A')

        self.assertEqual(records['deletions'][0].name, 'bar.example.com.')
        self.assertEqual(records['deletions'][0].type, 'A')
Esempio n. 15
0
class HostGCP():
    def __init__(self, config, gcp_credentials_file):
        self.config = config
        creds = read_json(gcp_credentials_file)
        self._compute = GCENodeDriver(
            creds['client_email'],
            gcp_credentials_file,
            project=creds['project_id'],
            datacenter=self.config['gcp_compute_zone'],
            timeout=self.config['compute_timeout'])
        self._dns = GoogleDNSDriver(creds['client_email'],
                                    gcp_credentials_file,
                                    project=creds['project_id'])
        self._dns_zone = self._dns.get_zone(config['gcp_dns_zone'])
        self.log = logging.getLogger(__name__ + '.' + self._name)

    @property
    def _name(self):
        return self.config['resources_name']

    async def start(self):
        self.log.info('start() begins')
        try:
            await self._start()
        except:
            self.log.exception('start() failed')
            raise
        self.log.info('start() finished')

    async def _start(self):
        try:
            self.log.info('Creating external static IP address')
            addr = await run_async(self._compute.ex_get_address, self._name)
            self.log.info('Exists')
        except ResourceNotFoundError:
            addr = await run_async(self._compute.ex_create_address, self._name)
            self.log.info('Created')

        self.log.info('External static IP address: %s', addr.address)

        try:
            self.log.info('Creating DNS record: %s', self.config['hostname'])
            await run_async(self._dns.create_record, self.config['hostname'],
                            self._dns_zone, 'A', {
                                'ttl': self.config['hostname_ttl'],
                                'rrdatas': [addr.address]
                            })
            self.log.info('Created')
        except ResourceExistsError:
            self.log.info('Exists')

        try:
            self.log.info('Creating data disk')
            data_disk_name = self._name + '-data'
            data_disk = await run_async(self._compute.ex_get_volume,
                                        data_disk_name)
            self.log.info('Exists')
        except ResourceNotFoundError:
            data_disk = await run_async(
                self._compute.create_volume,
                self.config['data_disk_size'],
                data_disk_name,
                ex_disk_type=('pd-ssd' if self.config['data_disk_ssd'] else
                              'pd-standard'))
            self.log.info('Created')

        self.log.info('Data disk: %sGB %s', data_disk.size,
                      data_disk.extra['type'])

        try:
            self.log.info('Creating host')
            host = await run_async(self._compute.ex_get_node, self._name)
            self.log.info('Exists')
        except ResourceNotFoundError:
            host = await run_async(
                self._compute.create_node,
                self._name,
                location=self.config['gcp_compute_zone'],
                size=self.config['machine_type'],
                image=self.config['boot_image'],
                external_ip=addr,
                ex_network=self.config['gcp_compute_net'],
                ex_subnetwork=self.config['gcp_compute_subnet'],
            )
            self.log.info('Created')

        self.log.info('Host: %s %s', host.extra['image'], host.size)

        try:
            self.log.info('Attaching data disk')
            await run_async(self._compute.attach_volume,
                            host,
                            data_disk,
                            ex_auto_delete=False)
            self.log.info('Attached')
        except ResourceInUseError:
            self.log.info('Already attached')

        self.log.info('Setting host tags')
        await run_async(self._compute.ex_set_node_tags, host,
                        self.config['gcp_compute_tags'])

        self.log.info('Setting host metadata')
        await run_async(self._compute.ex_set_node_metadata, host,
                        self.config['host_metadata'])

        self.log.info('Starting host')
        await run_async(self._compute.ex_start_node, host)
        self.log.info('Started')

    async def stop(self, clean=HostClean.STOP) -> None:
        self.log.info('stop(clean=%s) begins', clean.name)
        try:
            await self._stop(clean)
        except:
            self.log.exception('stop() failed')
            raise
        self.log.info('stop() finished')

    async def _stop(self, clean) -> None:
        try:
            self.log.info('Stopping host')
            host = await run_async(self._compute.ex_get_node, self._name,
                                   self.config['gcp_compute_zone'])
            await run_async(self._compute.ex_stop_node, host)
            self.log.info('Stopped')

            if clean <= HostClean.STOP:
                return

            self.log.info('Removing host')
            await run_async(self._compute.destroy_node,
                            host,
                            destroy_boot_disk=True)
            self.log.info('Removed')

        except ResourceNotFoundError:
            self.log.info('Not present')

        if clean <= HostClean.HOST:
            return

        try:
            self.log.info('Removing data disk')
            data_disk_name = self._name + '-data'
            data_disk = await run_async(self._compute.ex_get_volume,
                                        data_disk_name)
            await run_async(self._compute.destroy_volume, data_disk)
            self.log.info('Removed')
        except ResourceNotFoundError:
            self.log.info('Not present')

        if clean <= HostClean.DATA:
            return

        try:
            self.log.info('Removing DNS record')
            record = await run_async(self._dns.get_record, self._dns_zone.id,
                                     'A:' + self.config['hostname'])
            await run_async(self._dns.delete_record, record)
            self.log.info('Removed')
        except RecordDoesNotExistError:
            self.log.info('Not present')

        try:
            self.log.info('Removing external static IP address')
            await run_async(self._compute.ex_destroy_address, self._name)
            self.log.info('Removed')
        except ResourceNotFoundError:
            self.log.info('Not present')