Support ip address type for instances

Include address type in getting instance response.

* Deprecate confip option network_label_regex as we don't reply on Nova
  to get addresses, network names don't make any sense.
* Add 'addresses' in instance API response, keep 'ip' as is but mark
  it deprecated in API doc, python-troveclient shouldn't break.

Story: 2007562
Task: 39445

Change-Id: Ia0458b5ddae8959ce29c17e444e1a51a026283cd
This commit is contained in:
Lingxian Kong 2020-04-16 17:50:55 +12:00
parent a4057b10af
commit 429c39890e
22 changed files with 253 additions and 180 deletions

View File

@ -161,39 +161,14 @@ Response Parameters
- datastore: datastore2 - datastore: datastore2
- datastore.type: datastore_type - datastore.type: datastore_type
- datastore.version: datastore_version1 - datastore.version: datastore_version1
- region: region_name2
- tenant_id: tenant_id
- volume: volume - volume: volume
- volume.size: volume_size - volume.size: volume_size
- volume.used: volume_used - volume.used: volume_used
- hostname: instance_hostname
- ip: instance_ip_address
- created: created - created: created
- updated: updated - updated: updated
- service_status_updated: service_status_updated - service_status_updated: service_status_updated
- fault: instance_fault
- fault.message: instance_fault_message
- fault.created: instance_fault_created
- fault.details: instance_fault_details
- replicas: instance_replicas
- replicas.id: instance_replica_id
- replicas.links: instance_replica_links
- replicas.links.href: instance_replica_link_href
- replicas.links.rel: instance_replica_link_rel
- configuration: configuration1
- configuration.id: configuration_id
- configuration.name: configuration_name
- configuration.links: configuration_links
- configuration.links.href: configuration_link_href
- configuration.links.rel: configuration_link_rel
- locality: locality - locality: locality
- local_storage_used: local_storage_used
- password: root_password - password: root_password
- cluster_id: cluster_id
- shard_id: shard_id
- server_id: server_id
- volume_id: volume_id
- encrypted_rpc_messaging: encrypted_rpc_messaging
- instance: instance - instance: instance
@ -231,6 +206,62 @@ Request
- instanceId: instanceId - instanceId: instanceId
Response Parameters
-------------------
.. rest_parameters:: parameters.yaml
- instance: instance
- id: instanceId1
- name: instanceName1
- status: instance_status
- links: instance_links
- links.href: instance_link_href
- links.rel: instance_link_rel
- flavor: flavor
- flavor.id: flavorId1
- flavor.links: flavor_links
- flavor.links.href: flavor_link_href
- flavor.links.rel: flavor_link_rel
- datastore: datastore2
- datastore.type: datastore_type
- datastore.version: datastore_version1
- region: region_name2
- tenant_id: tenant_id
- volume: volume
- volume.size: volume_size
- volume.used: volume_used
- hostname: instance_hostname
- ip: instance_ip_address
- addresses: instance_ip_addresses
- created: created
- updated: updated
- service_status_updated: service_status_updated
- fault: instance_fault
- fault.message: instance_fault_message
- fault.created: instance_fault_created
- fault.details: instance_fault_details
- replicas: instance_replicas
- replicas.id: instance_replica_id
- replicas.links: instance_replica_links
- replicas.links.href: instance_replica_link_href
- replicas.links.rel: instance_replica_link_rel
- configuration: configuration1
- configuration.id: configuration_id
- configuration.name: configuration_name
- configuration.links: configuration_links
- configuration.links.href: configuration_link_href
- configuration.links.rel: configuration_link_rel
- locality: locality
- local_storage_used: local_storage_used
- password: root_password
- cluster_id: cluster_id
- shard_id: shard_id
- server_id: server_id
- volume_id: volume_id
- encrypted_rpc_messaging: encrypted_rpc_messaging
Response Example Response Example
---------------- ----------------

View File

@ -419,10 +419,16 @@ instance_hostname:
type: string type: string
instance_ip_address: instance_ip_address:
description: | description: |
The IP address of an instance. The IP address of an instance(deprecated).
in: body in: body
require: false require: false
type: string type: string
instance_ip_addresses:
description: |
The IP addresses of an instance, including the address type and IP.
in: body
require: false
type: array
instance_link_href: instance_link_href:
description: | description: |
The ``href`` attribute of an instance link. The ``href`` attribute of an instance link.

View File

@ -1,6 +1,16 @@
{ {
"instances": [ "instances": [
{ {
"addresses": [
{
"address": "10.1.0.62",
"type": "private"
},
{
"address": "172.24.5.114",
"type": "public"
}
],
"created": "2019-12-23T20:58:35", "created": "2019-12-23T20:58:35",
"datastore": { "datastore": {
"type": "mysql", "type": "mysql",

View File

@ -1,6 +1,16 @@
{ {
"instances": [ "instances": [
{ {
"addresses": [
{
"address": "10.1.0.62",
"type": "private"
},
{
"address": "172.24.5.114",
"type": "public"
}
],
"created": "2019-12-23T20:58:35", "created": "2019-12-23T20:58:35",
"datastore": { "datastore": {
"type": "mysql", "type": "mysql",

View File

@ -1,5 +1,15 @@
{ {
"instance": { "instance": {
"addresses": [
{
"address": "10.1.0.62",
"type": "private"
},
{
"address": "172.24.5.114",
"type": "public"
}
],
"created": "2019-12-23T20:58:35", "created": "2019-12-23T20:58:35",
"datastore": { "datastore": {
"type": "mysql", "type": "mysql",

View File

@ -1,5 +1,15 @@
{ {
"instance": { "instance": {
"addresses": [
{
"address": "10.1.0.62",
"type": "private"
},
{
"address": "172.24.5.114",
"type": "public"
}
],
"created": "2019-12-23T20:58:35", "created": "2019-12-23T20:58:35",
"datastore": { "datastore": {
"type": "mysql", "type": "mysql",

View File

@ -556,7 +556,6 @@ function config_trove_network {
sudo ip route sudo ip route
# Now make sure the conf settings are right # Now make sure the conf settings are right
iniset $TROVE_CONF DEFAULT network_label_regex ""
iniset $TROVE_CONF DEFAULT ip_regex "" iniset $TROVE_CONF DEFAULT ip_regex ""
iniset $TROVE_CONF DEFAULT black_list_regex "" iniset $TROVE_CONF DEFAULT black_list_regex ""
iniset $TROVE_CONF DEFAULT management_networks ${mgmt_net_id} iniset $TROVE_CONF DEFAULT management_networks ${mgmt_net_id}

View File

@ -2,6 +2,11 @@
Set up database clustering Set up database clustering
========================== ==========================
.. caution::
Database clustering function is still in experimental, should not be used
in production environment.
You can store data across multiple machines by setting up MongoDB You can store data across multiple machines by setting up MongoDB
sharded clusters. sharded clusters.

View File

@ -58,13 +58,6 @@ trove_auth_url = http://0.0.0.0/identity/v2.0
# Service type to use when searching catalog. # Service type to use when searching catalog.
#neutron_service_type = network #neutron_service_type = network
# Config option for showing the IP address that nova doles out
# For nova-network, set this to the appropriate network label defined in nova
# For neutron, set this to .* since users can specify custom network labels
# You can also optionally specify regex'es to match the actual IP addresses
# ip_regex (white-list) is applied before black_list_regex in the filter chain
network_label_regex = ^private$
#network_label_regex = .* #with neutron enabled
#ip_regex = ^(15.|123.) #ip_regex = ^(15.|123.)
#black_list_regex = ^10.0.0. #black_list_regex = ^10.0.0.

View File

@ -50,7 +50,6 @@ nova_compute_service_type = compute
nova_service_name = Compute Service nova_service_name = Compute Service
# Config option for showing the IP address that nova doles out # Config option for showing the IP address that nova doles out
network_label_regex = ^private$
ip_regex = ^(15.|123.) ip_regex = ^(15.|123.)
black_list_regex = ^(10.0.0.) black_list_regex = ^(10.0.0.)

View File

@ -0,0 +1,8 @@
---
features:
- Added a new field named ``addresses`` in the instance API
response which including the IP address and type, either 'private' or
'public'.
deprecations:
- The 'ip' field of getting instance response is deprecated and will be
removed in W release.

View File

@ -93,7 +93,6 @@ class ClusterView(object):
ip_list.extend(instance_ips) ip_list.extend(instance_ips)
if instance.type in instance_dict_to_be_published_for: if instance.type in instance_dict_to_be_published_for:
instances.append(instance_dict) instances.append(instance_dict)
ip_list.sort()
return instances, ip_list return instances, ip_list
def build_instances(self): def build_instances(self):

View File

@ -373,7 +373,8 @@ common_opts = [
'mariadb': '7a4f82cc-10d2-4bc6-aadc-d9aacc2a3cb5'}, 'mariadb': '7a4f82cc-10d2-4bc6-aadc-d9aacc2a3cb5'},
help='Unique ID to tag notification events.'), help='Unique ID to tag notification events.'),
cfg.StrOpt('network_label_regex', default='^private$', cfg.StrOpt('network_label_regex', default='^private$',
help='Regular expression to match Trove network labels.'), help='Regular expression to match Trove network labels.',
deprecated_for_removal=True),
cfg.StrOpt('ip_regex', default=None, cfg.StrOpt('ip_regex', default=None,
help='List IP addresses that match this regular expression.'), help='List IP addresses that match this regular expression.'),
cfg.StrOpt('black_list_regex', default=None, cfg.StrOpt('black_list_regex', default=None,

View File

@ -25,16 +25,12 @@ from novaclient import exceptions as nova_exceptions
from oslo_config.cfg import NoSuchOptError from oslo_config.cfg import NoSuchOptError
from oslo_log import log as logging from oslo_log import log as logging
from oslo_utils import encodeutils from oslo_utils import encodeutils
from oslo_utils import netutils
from sqlalchemy import func from sqlalchemy import func
from trove.backup.models import Backup from trove.backup.models import Backup
from trove.common import cfg from trove.common import cfg
from trove.common.clients import create_cinder_client from trove.common import clients
from trove.common.clients import create_dns_client
from trove.common.clients import create_glance_client
from trove.common.clients import create_guest_client
from trove.common.clients import create_neutron_client
from trove.common.clients import create_nova_client
from trove.common import crypto_utils as cu from trove.common import crypto_utils as cu
from trove.common import exception from trove.common import exception
from trove.common.i18n import _ from trove.common.i18n import _
@ -63,13 +59,16 @@ from trove.taskmanager import api as task_api
CONF = cfg.CONF CONF = cfg.CONF
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
# Invalid states to contact the agent
AGENT_INVALID_STATUSES = ["BUILD", "REBOOT", "RESIZE", "PROMOTE", "EJECT",
"UPGRADE"]
def filter_ips(ips, white_list_regex, black_list_regex):
"""Return IPs matching white_list_regex and def ip_visible(ip, white_list_regex, black_list_regex):
Filter out IPs matching black_list_regex. if re.search(white_list_regex, ip) and not re.search(black_list_regex, ip):
""" return True
return [ip for ip in ips if re.search(white_list_regex, ip)
and not re.search(black_list_regex, ip)] return False
def load_server(context, instance_id, server_id, region_name): def load_server(context, instance_id, server_id, region_name):
@ -84,7 +83,7 @@ def load_server(context, instance_id, server_id, region_name):
:type server_id: unicode :type server_id: unicode
:rtype: novaclient.v2.servers.Server :rtype: novaclient.v2.servers.Server
""" """
client = create_nova_client(context, region_name=region_name) client = clients.create_nova_client(context, region_name=region_name)
try: try:
server = client.servers.get(server_id) server = client.servers.get(server_id)
except nova_exceptions.NotFound: except nova_exceptions.NotFound:
@ -129,21 +128,42 @@ def load_simple_instance_server_status(context, db_info):
"""Loads a server or raises an exception.""" """Loads a server or raises an exception."""
if 'BUILDING' == db_info.task_status.action: if 'BUILDING' == db_info.task_status.action:
db_info.server_status = "BUILD" db_info.server_status = "BUILD"
db_info.addresses = {}
else: else:
client = create_nova_client(context, db_info.region_id) client = clients.create_nova_client(context, db_info.region_id)
try: try:
server = client.servers.get(db_info.compute_instance_id) server = client.servers.get(db_info.compute_instance_id)
db_info.server_status = server.status db_info.server_status = server.status
db_info.addresses = server.addresses
except nova_exceptions.NotFound: except nova_exceptions.NotFound:
db_info.server_status = "SHUTDOWN" db_info.server_status = "SHUTDOWN"
db_info.addresses = {}
# Invalid states to contact the agent def load_simple_instance_addresses(context, db_info):
AGENT_INVALID_STATUSES = ["BUILD", "REBOOT", "RESIZE", "PROMOTE", "EJECT", """Get addresses of the instance from Neutron."""
"UPGRADE"] if 'BUILDING' == db_info.task_status.action:
db_info.addresses = []
return
addresses = []
client = clients.create_neutron_client(context, db_info.region_id)
ports = client.list_ports(device_id=db_info.compute_instance_id)['ports']
for port in ports:
if 'Management port' not in port['description']:
LOG.debug('Found user port %s for instance %s', port['id'],
db_info.id)
for ip in port['fixed_ips']:
# TODO(lxkong): IPv6 is not supported
if netutils.is_valid_ipv4(ip.get('ip_address')):
addresses.append(
{'address': ip['ip_address'], 'type': 'private'})
fips = client.list_floatingips(port_id=port['id'])
if len(fips['floatingips']) == 0:
continue
fip = fips['floatingips'][0]
addresses.append(
{'address': fip['floating_ip_address'], 'type': 'public'})
db_info.addresses = addresses
class SimpleInstance(object): class SimpleInstance(object):
@ -196,13 +216,6 @@ class SimpleInstance(object):
@property @property
def addresses(self): def addresses(self):
# TODO(tim.simpson): This code attaches two parts of the Nova server to
# db_info: "status" and "addresses". The idea
# originally was to listen to events to update this
# data and store it in the Trove database.
# However, it may have been unwise as a year and a
# half later we still have to load the server anyway
# and this makes the code confusing.
if hasattr(self.db_info, 'addresses'): if hasattr(self.db_info, 'addresses'):
return self.db_info.addresses return self.db_info.addresses
else: else:
@ -217,6 +230,7 @@ class SimpleInstance(object):
"""Returns the IP address to be used with DNS.""" """Returns the IP address to be used with DNS."""
ips = self.get_visible_ip_addresses() ips = self.get_visible_ip_addresses()
if ips: if ips:
# FIXME
return ips[0] return ips[0]
@property @property
@ -234,20 +248,14 @@ class SimpleInstance(object):
return None return None
IPs = [] IPs = []
mgmt_networks = neutron.get_management_networks(self.context)
for label in self.addresses: for addr_info in self.addresses:
if label in mgmt_networks: if CONF.ip_regex and CONF.black_list_regex:
continue if not ip_visible(addr_info['address'], CONF.ip_regex,
if (CONF.network_label_regex and CONF.black_list_regex):
not re.search(CONF.network_label_regex, label)): continue
continue
IPs.extend([addr.get('addr') for addr in self.addresses[label]]) IPs.append(addr_info)
# Includes ip addresses that match the regexp pattern
if CONF.ip_regex and CONF.black_list_regex:
IPs = filter_ips(IPs, CONF.ip_regex, CONF.black_list_regex)
return IPs return IPs
@ -550,15 +558,16 @@ def load_instance(cls, context, id, needs_server=False,
# necessary and instead we'll just use the server_status field from # necessary and instead we'll just use the server_status field from
# the instance table. # the instance table.
load_simple_instance_server_status(context, db_info) load_simple_instance_server_status(context, db_info)
load_simple_instance_addresses(context, db_info)
server = None server = None
else: else:
try: try:
server = load_server(context, db_info.id, server = load_server(context, db_info.id,
db_info.compute_instance_id, db_info.compute_instance_id,
region_name=db_info.region_id) region_name=db_info.region_id)
# TODO(tim.simpson): Remove this hack when we have notifications!
db_info.server_status = server.status db_info.server_status = server.status
db_info.addresses = server.addresses
load_simple_instance_addresses(context, db_info)
except exception.ComputeInstanceNotFound: except exception.ComputeInstanceNotFound:
LOG.error("Could not load compute instance %s.", LOG.error("Could not load compute instance %s.",
db_info.compute_instance_id) db_info.compute_instance_id)
@ -568,24 +577,32 @@ def load_instance(cls, context, id, needs_server=False,
service_status = InstanceServiceStatus.find_by(instance_id=id) service_status = InstanceServiceStatus.find_by(instance_id=id)
LOG.debug("Instance %(instance_id)s service status is %(service_status)s.", LOG.debug("Instance %(instance_id)s service status is %(service_status)s.",
{'instance_id': id, 'service_status': service_status.status}) {'instance_id': id, 'service_status': service_status.status})
return cls(context, db_info, server, service_status) return cls(context, db_info, server, service_status)
def load_instance_with_info(cls, context, id, cluster_id=None): def load_instance_with_info(cls, context, id, cluster_id=None):
db_info = get_db_info(context, id, cluster_id) db_info = get_db_info(context, id, cluster_id)
load_simple_instance_server_status(context, db_info) load_simple_instance_server_status(context, db_info)
load_simple_instance_addresses(context, db_info)
service_status = InstanceServiceStatus.find_by(instance_id=id) service_status = InstanceServiceStatus.find_by(instance_id=id)
LOG.debug("Instance %(instance_id)s service status is %(service_status)s.", LOG.debug("Instance %(instance_id)s service status is %(service_status)s.",
{'instance_id': id, 'service_status': service_status.status}) {'instance_id': id, 'service_status': service_status.status})
instance = cls(context, db_info, service_status) instance = cls(context, db_info, service_status)
load_guest_info(instance, context, id) load_guest_info(instance, context, id)
load_server_group_info(instance, context) load_server_group_info(instance, context)
return instance return instance
def load_guest_info(instance, context, id): def load_guest_info(instance, context, id):
if instance.status not in AGENT_INVALID_STATUSES: if instance.status not in AGENT_INVALID_STATUSES:
guest = create_guest_client(context, id) guest = clients.create_guest_client(context, id)
try: try:
volume_info = guest.get_volume_info() volume_info = guest.get_volume_info()
instance.volume_used = volume_info['used'] instance.volume_used = volume_info['used']
@ -646,7 +663,7 @@ class BaseInstance(SimpleInstance):
self._server_group_loaded = False self._server_group_loaded = False
def get_guest(self): def get_guest(self):
return create_guest_client(self.context, self.db_info.id) return clients.create_guest_client(self.context, self.db_info.id)
def delete(self): def delete(self):
def _delete_resources(): def _delete_resources():
@ -754,7 +771,7 @@ class BaseInstance(SimpleInstance):
try: try:
dns_support = CONF.trove_dns_support dns_support = CONF.trove_dns_support
if dns_support: if dns_support:
dns_api = create_dns_client(self.context) dns_api = clients.create_dns_client(self.context)
dns_api.delete_instance_entry(instance_id=self.id) dns_api.delete_instance_entry(instance_id=self.id)
except Exception as e: except Exception as e:
LOG.warning("Failed to delete dns entry of instance %s, error: %s", LOG.warning("Failed to delete dns entry of instance %s, error: %s",
@ -839,7 +856,7 @@ class BaseInstance(SimpleInstance):
@property @property
def nova_client(self): def nova_client(self):
if not self._nova_client: if not self._nova_client:
self._nova_client = create_nova_client( self._nova_client = clients.create_nova_client(
self.context, region_name=self.db_info.region_id) self.context, region_name=self.db_info.region_id)
return self._nova_client return self._nova_client
@ -866,14 +883,14 @@ class BaseInstance(SimpleInstance):
@property @property
def volume_client(self): def volume_client(self):
if not self._volume_client: if not self._volume_client:
self._volume_client = create_cinder_client( self._volume_client = clients.create_cinder_client(
self.context, region_name=self.db_info.region_id) self.context, region_name=self.db_info.region_id)
return self._volume_client return self._volume_client
@property @property
def neutron_client(self): def neutron_client(self):
if not self._neutron_client: if not self._neutron_client:
self._neutron_client = create_neutron_client( self._neutron_client = clients.create_neutron_client(
self.context, region_name=self.db_info.region_id) self.context, region_name=self.db_info.region_id)
return self._neutron_client return self._neutron_client
@ -966,8 +983,8 @@ class Instance(BuiltInstance):
@classmethod @classmethod
def _validate_remote_datastore(cls, context, region_name, flavor, def _validate_remote_datastore(cls, context, region_name, flavor,
datastore, datastore_version): datastore, datastore_version):
remote_nova_client = create_nova_client(context, remote_nova_client = clients.create_nova_client(
region_name=region_name) context, region_name=region_name)
try: try:
remote_flavor = remote_nova_client.flavors.get(flavor.id) remote_flavor = remote_nova_client.flavors.get(flavor.id)
if (flavor.ram != remote_flavor.ram or if (flavor.ram != remote_flavor.ram or
@ -1000,9 +1017,9 @@ class Instance(BuiltInstance):
"Datastore Version %(dsv)s not found in region %(remote)s." "Datastore Version %(dsv)s not found in region %(remote)s."
% {'dsv': datastore_version.name, 'remote': region_name}) % {'dsv': datastore_version.name, 'remote': region_name})
glance_client = create_glance_client(context) glance_client = clients.create_glance_client(context)
local_image = glance_client.images.get(datastore_version.image) local_image = glance_client.images.get(datastore_version.image)
remote_glance_client = create_glance_client( remote_glance_client = clients.create_glance_client(
context, region_name=region_name) context, region_name=region_name)
remote_image = remote_glance_client.images.get( remote_image = remote_glance_client.images.get(
remote_ds_ver.image) remote_ds_ver.image)
@ -1050,7 +1067,7 @@ class Instance(BuiltInstance):
flavor_id=flavor_id) flavor_id=flavor_id)
datastore_cfg = CONF.get(datastore_version.manager) datastore_cfg = CONF.get(datastore_version.manager)
client = create_nova_client(context) client = clients.create_nova_client(context)
try: try:
flavor = client.flavors.get(flavor_id) flavor = client.flavors.get(flavor_id)
except nova_exceptions.NotFound: except nova_exceptions.NotFound:
@ -1242,7 +1259,7 @@ class Instance(BuiltInstance):
status=tr_instance.ServiceStatuses.NEW) status=tr_instance.ServiceStatuses.NEW)
if CONF.trove_dns_support: if CONF.trove_dns_support:
dns_client = create_dns_client(context) dns_client = clients.create_dns_client(context)
hostname = dns_client.determine_hostname(instance_id) hostname = dns_client.determine_hostname(instance_id)
db_info.hostname = hostname db_info.hostname = hostname
db_info.save() db_info.save()
@ -1659,7 +1676,7 @@ class Instances(object):
if context is None: if context is None:
raise TypeError(_("Argument context not defined.")) raise TypeError(_("Argument context not defined."))
client = create_nova_client(context) client = clients.create_nova_client(context)
servers = client.servers.list() servers = client.servers.list()
query_opts = {'tenant_id': context.project_id, query_opts = {'tenant_id': context.project_id,
'deleted': False} 'deleted': False}
@ -1711,26 +1728,25 @@ class Instances(object):
for db in db_items: for db in db_items:
server = None server = None
try: try:
# TODO(tim.simpson): Delete when we get notifications working!
if InstanceTasks.BUILDING == db.task_status: if InstanceTasks.BUILDING == db.task_status:
db.server_status = "BUILD" db.server_status = "BUILD"
db.addresses = {} db.addresses = []
else: else:
try: try:
region = CONF.service_credentials.region_name region = CONF.service_credentials.region_name
if (not db.region_id or db.region_id == region): if (not db.region_id or db.region_id == region):
server = find_server(db.id, db.compute_instance_id) server = find_server(db.id, db.compute_instance_id)
else: else:
nova_client = create_nova_client( nova_client = clients.create_nova_client(
context, region_name=db.region_id) context, region_name=db.region_id)
server = nova_client.servers.get( server = nova_client.servers.get(
db.compute_instance_id) db.compute_instance_id)
db.server_status = server.status db.server_status = server.status
db.addresses = server.addresses
load_simple_instance_addresses(context, db)
except exception.ComputeInstanceNotFound: except exception.ComputeInstanceNotFound:
db.server_status = "SHUTDOWN" # Fake it... db.server_status = "SHUTDOWN" # Fake it...
db.addresses = {} db.addresses = []
# TODO(tim.simpson): End of hack.
# volumes = find_volumes(server.id) # volumes = find_volumes(server.id)
datastore_status = InstanceServiceStatus.find_by( datastore_status = InstanceServiceStatus.find_by(

View File

@ -53,9 +53,12 @@ class InstanceView(object):
if self.instance.hostname: if self.instance.hostname:
instance_dict['hostname'] = self.instance.hostname instance_dict['hostname'] = self.instance.hostname
else: else:
ip = self.instance.get_visible_ip_addresses() addresses = self.instance.get_visible_ip_addresses()
if ip: if addresses:
instance_dict['ip'] = ip # NOTE(lxkong): 'ip' is deprecated in stable/ussuri and should
# be removed in W.
instance_dict['ip'] = [addr['address'] for addr in addresses]
instance_dict['addresses'] = addresses
if self.instance.slave_of_id is not None: if self.instance.slave_of_id is not None:
instance_dict['replica_of'] = self._build_master_info() instance_dict['replica_of'] = self._build_master_info()

View File

@ -344,6 +344,8 @@ class Manager(periodic_task.PeriodicTasks):
snapshot = instance_tasks.get_replication_master_snapshot( snapshot = instance_tasks.get_replication_master_snapshot(
context, slave_of_id, flavor, replica_backup_id, context, slave_of_id, flavor, replica_backup_id,
replica_number=replica_number) replica_number=replica_number)
LOG.info('Snapshot info for creating replica of %s: %s',
slave_of_id, snapshot)
replica_backup_id = snapshot['dataset']['snapshot_id'] replica_backup_id = snapshot['dataset']['snapshot_id']
replica_backup_created = (replica_backup_id is not None) replica_backup_created = (replica_backup_id is not None)

View File

@ -198,7 +198,7 @@ class ClusterTasks(Cluster):
@classmethod @classmethod
def get_ip(cls, instance): def get_ip(cls, instance):
return instance.get_visible_ip_addresses()[0] return instance.get_visible_ip_addresses()[0].get('address')
def _all_instances_ready(self, instance_ids, cluster_id, def _all_instances_ready(self, instance_ids, cluster_id,
shard_id=None): shard_id=None):
@ -1170,19 +1170,22 @@ class BuiltInstanceTasks(BuiltInstance, NotifyMixin, ConfigurationMixin):
return floating_ips return floating_ips
def detach_public_ips(self): def detach_public_ips(self):
LOG.debug("Begin detach_public_ips for instance %s", self.id) LOG.info("Begin detach_public_ips for instance %s", self.id)
removed_ips = [] removed_ips = []
floating_ips = self._get_floating_ips() floating_ips = self._get_floating_ips()
for ip in self.get_visible_ip_addresses():
if ip in floating_ips: for item in self.get_visible_ip_addresses():
fip_id = floating_ips[ip] if item['type'] == 'public':
self.neutron_client.update_floatingip( ip = item['address']
fip_id, {'floatingip': {'port_id': None}}) if ip in floating_ips:
removed_ips.append(fip_id) fip_id = floating_ips[ip]
self.neutron_client.update_floatingip(
fip_id, {'floatingip': {'port_id': None}})
removed_ips.append(fip_id)
return removed_ips return removed_ips
def attach_public_ips(self, ips): def attach_public_ips(self, ips):
LOG.debug("Begin attach_public_ips for instance %s", self.id) LOG.info("Begin attach_public_ips for instance %s", self.id)
server_id = self.db_info.compute_instance_id server_id = self.db_info.compute_instance_id
# NOTE(zhaochao): in Nova's addFloatingIp, the new floating ip will # NOTE(zhaochao): in Nova's addFloatingIp, the new floating ip will

View File

@ -919,7 +919,7 @@ class TestGetInstances(object):
def test_index_list(self): def test_index_list(self):
allowed_attrs = ['id', 'links', 'name', 'status', 'flavor', allowed_attrs = ['id', 'links', 'name', 'status', 'flavor',
'datastore', 'ip', 'hostname', 'replica_of', 'datastore', 'ip', 'hostname', 'replica_of',
'region'] 'region', 'addresses']
if VOLUME_SUPPORT: if VOLUME_SUPPORT:
allowed_attrs.append('volume') allowed_attrs.append('volume')
instances = dbaas.instances.list() instances = dbaas.instances.list()
@ -941,7 +941,7 @@ class TestGetInstances(object):
allowed_attrs = ['created', 'databases', 'flavor', 'hostname', 'id', allowed_attrs = ['created', 'databases', 'flavor', 'hostname', 'id',
'links', 'name', 'status', 'updated', 'ip', 'links', 'name', 'status', 'updated', 'ip',
'datastore', 'fault', 'region', 'datastore', 'fault', 'region',
'service_status_updated'] 'service_status_updated', 'addresses']
if VOLUME_SUPPORT: if VOLUME_SUPPORT:
allowed_attrs.append('volume') allowed_attrs.append('volume')
instances = dbaas.instances.list(detailed=True) instances = dbaas.instances.list(detailed=True)
@ -961,7 +961,7 @@ class TestGetInstances(object):
allowed_attrs = ['created', 'databases', 'flavor', 'hostname', 'id', allowed_attrs = ['created', 'databases', 'flavor', 'hostname', 'id',
'links', 'name', 'status', 'updated', 'ip', 'links', 'name', 'status', 'updated', 'ip',
'datastore', 'fault', 'region', 'datastore', 'fault', 'region',
'service_status_updated'] 'service_status_updated', 'addresses']
if VOLUME_SUPPORT: if VOLUME_SUPPORT:
allowed_attrs.append('volume') allowed_attrs.append('volume')
else: else:

View File

@ -82,13 +82,16 @@ class ClusterViewTest(trove_testtools.TestCase):
cluster.instances.append(Mock()) cluster.instances.append(Mock())
cluster.instances.append(Mock()) cluster.instances.append(Mock())
cluster.instances[0].type = 'configsvr' cluster.instances[0].type = 'configsvr'
cluster.instances[0].get_visible_ip_addresses = lambda: ['1.2.3.4'] cluster.instances[0].get_visible_ip_addresses.return_value = [
{'type': 'private', 'address': '1.2.3.4'}]
cluster.instances[0].datastore_version.manager = 'mongodb' cluster.instances[0].datastore_version.manager = 'mongodb'
cluster.instances[1].type = 'query_router' cluster.instances[1].type = 'query_router'
cluster.instances[1].get_visible_ip_addresses = lambda: ['1.2.3.4'] cluster.instances[1].get_visible_ip_addresses.return_value = [
{'type': 'private', 'address': '1.2.3.4'}]
cluster.instances[1].datastore_version.manager = 'mongodb' cluster.instances[1].datastore_version.manager = 'mongodb'
cluster.instances[2].type = 'member' cluster.instances[2].type = 'member'
cluster.instances[2].get_visible_ip_addresses = lambda: ['1.2.3.4'] cluster.instances[2].get_visible_ip_addresses.return_value = [
{'type': 'private', 'address': '1.2.3.4'}]
cluster.instances[2].datastore_version.manager = 'mongodb' cluster.instances[2].datastore_version.manager = 'mongodb'
def test_case(ip_to_be_published_for, def test_case(ip_to_be_published_for,
@ -124,7 +127,8 @@ class ClusterInstanceDetailViewTest(trove_testtools.TestCase):
self.instance.addresses = {"private": [{"addr": self.ip}]} self.instance.addresses = {"private": [{"addr": self.ip}]}
self.instance.volume_used = '3' self.instance.volume_used = '3'
self.instance.root_password = 'iloveyou' self.instance.root_password = 'iloveyou'
self.instance.get_visible_ip_addresses = lambda: ["1.2.3.4"] self.instance.get_visible_ip_addresses.return_value = [
{'type': 'private', 'address': '1.2.3.4'}]
self.instance.slave_of_id = None self.instance.slave_of_id = None
self.instance.slaves = None self.instance.slaves = None
self.context = trove_testtools.TroveTestContext(self) self.context = trove_testtools.TroveTestContext(self)
@ -162,3 +166,5 @@ class ClusterInstanceDetailViewTest(trove_testtools.TestCase):
result['instance']['datastore']['version']) result['instance']['datastore']['version'])
self.assertNotIn('hostname', result['instance']) self.assertNotIn('hostname', result['instance'])
self.assertEqual([self.ip], result['instance']['ip']) self.assertEqual([self.ip], result['instance']['ip'])
self.assertEqual(self.ip,
result['instance']['addresses'][0]['address'])

View File

@ -17,6 +17,7 @@ from mock import Mock, patch
from trove.backup import models as backup_models from trove.backup import models as backup_models
from trove.common import cfg from trove.common import cfg
from trove.common import clients
from trove.common import exception from trove.common import exception
from trove.common.instance import ServiceStatuses from trove.common.instance import ServiceStatuses
from trove.common import neutron from trove.common import neutron
@ -24,7 +25,6 @@ from trove.datastore import models as datastore_models
from trove.instance import models from trove.instance import models
from trove.instance.models import DBInstance from trove.instance.models import DBInstance
from trove.instance.models import DBInstanceFault from trove.instance.models import DBInstanceFault
from trove.instance.models import filter_ips
from trove.instance.models import Instance from trove.instance.models import Instance
from trove.instance.models import instance_encryption_key_cache from trove.instance.models import instance_encryption_key_cache
from trove.instance.models import InstanceServiceStatus from trove.instance.models import InstanceServiceStatus
@ -50,16 +50,16 @@ class SimpleInstanceTest(trove_testtools.TestCase):
ServiceStatuses.BUILDING), ds_version=Mock(), ds=Mock(), ServiceStatuses.BUILDING), ds_version=Mock(), ds=Mock(),
locality='affinity') locality='affinity')
self.instance.context = self.context self.instance.context = self.context
db_info.addresses = {"private": [{"addr": "123.123.123.123"}], db_info.addresses = [
"internal": [{"addr": "10.123.123.123"}], {'type': 'private', 'address': '123.123.123.123'},
"public": [{"addr": "15.123.123.123"}]} {'type': 'private', 'address': '10.123.123.123'},
self.orig_conf = CONF.network_label_regex {'type': 'public', 'address': '15.123.123.123'},
]
self.orig_ip_regex = CONF.ip_regex self.orig_ip_regex = CONF.ip_regex
self.orig_black_list_regex = CONF.black_list_regex self.orig_black_list_regex = CONF.black_list_regex
def tearDown(self): def tearDown(self):
super(SimpleInstanceTest, self).tearDown() super(SimpleInstanceTest, self).tearDown()
CONF.network_label_regex = self.orig_conf
CONF.ip_start = None CONF.ip_start = None
CONF.management_networks = [] CONF.management_networks = []
CONF.ip_regex = self.orig_ip_regex CONF.ip_regex = self.orig_ip_regex
@ -73,64 +73,22 @@ class SimpleInstanceTest(trove_testtools.TestCase):
self.assertFalse(root_on_create_val) self.assertFalse(root_on_create_val)
def test_filter_ips_white_list(self): def test_filter_ips_white_list(self):
CONF.network_label_regex = '.*'
CONF.ip_regex = '^(15.|123.)' CONF.ip_regex = '^(15.|123.)'
CONF.black_list_regex = '^10.123.123.*' CONF.black_list_regex = '^10.123.123.*'
ip = self.instance.get_visible_ip_addresses() ip = self.instance.get_visible_ip_addresses()
ip = filter_ips( ip = [addr['address'] for addr in ip]
ip, CONF.ip_regex, CONF.black_list_regex)
self.assertEqual(2, len(ip)) self.assertEqual(2, len(ip))
self.assertIn('123.123.123.123', ip) self.assertIn('123.123.123.123', ip)
self.assertIn('15.123.123.123', ip) self.assertIn('15.123.123.123', ip)
def test_filter_ips_black_list(self): def test_filter_ips_black_list(self):
CONF.network_label_regex = '.*'
CONF.ip_regex = '.*' CONF.ip_regex = '.*'
CONF.black_list_regex = '^10.123.123.*' CONF.black_list_regex = '^10.123.123.*'
ip = self.instance.get_visible_ip_addresses() ip = self.instance.get_visible_ip_addresses()
ip = filter_ips( ip = [addr['address'] for addr in ip]
ip, CONF.ip_regex, CONF.black_list_regex)
self.assertEqual(2, len(ip)) self.assertEqual(2, len(ip))
self.assertNotIn('10.123.123.123', ip) self.assertNotIn('10.123.123.123', ip)
def test_one_network_label(self):
CONF.network_label_regex = 'public'
ip = self.instance.get_visible_ip_addresses()
self.assertEqual(['15.123.123.123'], ip)
def test_two_network_labels(self):
CONF.network_label_regex = '^(private|public)$'
ip = self.instance.get_visible_ip_addresses()
self.assertEqual(2, len(ip))
self.assertIn('123.123.123.123', ip)
self.assertIn('15.123.123.123', ip)
def test_all_network_labels(self):
CONF.network_label_regex = '.*'
ip = self.instance.get_visible_ip_addresses()
self.assertEqual(3, len(ip))
self.assertIn('10.123.123.123', ip)
self.assertIn('123.123.123.123', ip)
self.assertIn('15.123.123.123', ip)
@patch('trove.common.clients.create_neutron_client')
def test_filter_management_ip_addresses(self, mock_neutron_client):
CONF.network_label_regex = ''
CONF.management_networks = ['fake-net-id']
neutron_client = Mock()
neutron_client.show_network.return_value = {
'network': {'name': 'public'}
}
mock_neutron_client.return_value = neutron_client
ip = self.instance.get_visible_ip_addresses()
neutron_client.show_network.assert_called_once_with('fake-net-id')
self.assertEqual(2, len(ip))
self.assertIn('123.123.123.123', ip)
self.assertIn('10.123.123.123', ip)
def test_locality(self): def test_locality(self):
self.assertEqual('affinity', self.instance.locality) self.assertEqual('affinity', self.instance.locality)
@ -208,8 +166,10 @@ class CreateInstanceTest(trove_testtools.TestCase):
deleted=False deleted=False
) )
self.backup_id = self.backup.id self.backup_id = self.backup.id
self.orig_client = models.create_nova_client
models.create_nova_client = nova.fake_create_nova_client self.orig_client = clients.create_nova_client
clients.create_nova_client = nova.fake_create_nova_client
self.orig_api = task_api.API(self.context).create_instance self.orig_api = task_api.API(self.context).create_instance
task_api.API(self.context).create_instance = Mock() task_api.API(self.context).create_instance = Mock()
self.run_with_quotas = models.run_with_quotas self.run_with_quotas = models.run_with_quotas
@ -232,7 +192,7 @@ class CreateInstanceTest(trove_testtools.TestCase):
self.backup.delete() self.backup.delete()
self.datastore.delete() self.datastore.delete()
self.datastore_version.delete() self.datastore_version.delete()
models.create_nova_client = self.orig_client clients.create_nova_client = self.orig_client
task_api.API(self.context).create_instance = self.orig_api task_api.API(self.context).create_instance = self.orig_api
models.run_with_quotas = self.run_with_quotas models.run_with_quotas = self.run_with_quotas
backup_models.DBBackup.check_swift_object_exist = self.check backup_models.DBBackup.check_swift_object_exist = self.check
@ -310,21 +270,22 @@ class TestInstanceUpgrade(trove_testtools.TestCase):
manager='test', manager='test',
active=1) active=1)
self.safe_nova_client = models.create_nova_client self.safe_nova_client = clients.create_nova_client
models.create_nova_client = nova.fake_create_nova_client clients.create_nova_client = nova.fake_create_nova_client
super(TestInstanceUpgrade, self).setUp() super(TestInstanceUpgrade, self).setUp()
def tearDown(self): def tearDown(self):
self.datastore.delete() self.datastore.delete()
self.datastore_version1.delete() self.datastore_version1.delete()
self.datastore_version2.delete() self.datastore_version2.delete()
models.create_nova_client = self.safe_nova_client clients.create_nova_client = self.safe_nova_client
super(TestInstanceUpgrade, self).tearDown() super(TestInstanceUpgrade, self).tearDown()
@patch('trove.common.clients.create_neutron_client')
@patch.object(task_api.API, 'get_client', Mock(return_value=Mock())) @patch.object(task_api.API, 'get_client', Mock(return_value=Mock()))
@patch.object(task_api.API, 'upgrade') @patch.object(task_api.API, 'upgrade')
@patch('trove.tests.fakes.nova.LOG') @patch('trove.tests.fakes.nova.LOG')
def test_upgrade(self, mock_logging, task_upgrade): def test_upgrade(self, mock_logging, task_upgrade, mock_neutron_client):
instance_model = DBInstance( instance_model = DBInstance(
InstanceTasks.NONE, InstanceTasks.NONE,
id=str(uuid.uuid4()), id=str(uuid.uuid4()),
@ -391,8 +352,8 @@ class TestReplication(trove_testtools.TestCase):
instance_id=self.master.id) instance_id=self.master.id)
self.master_status.save() self.master_status.save()
self.safe_nova_client = models.create_nova_client self.safe_nova_client = clients.create_nova_client
models.create_nova_client = nova.fake_create_nova_client clients.create_nova_client = nova.fake_create_nova_client
self.swift_verify_patch = patch.object(models.Backup, self.swift_verify_patch = patch.object(models.Backup,
'verify_swift_auth_token') 'verify_swift_auth_token')
@ -406,7 +367,7 @@ class TestReplication(trove_testtools.TestCase):
self.master_status.delete() self.master_status.delete()
self.datastore.delete() self.datastore.delete()
self.datastore_version.delete() self.datastore_version.delete()
models.create_nova_client = self.safe_nova_client clients.create_nova_client = self.safe_nova_client
super(TestReplication, self).tearDown() super(TestReplication, self).tearDown()
@patch('trove.instance.models.LOG') @patch('trove.instance.models.LOG')

View File

@ -29,12 +29,10 @@ class InstanceViewsTest(trove_testtools.TestCase):
self.addresses = {"private": [{"addr": "123.123.123.123"}], self.addresses = {"private": [{"addr": "123.123.123.123"}],
"internal": [{"addr": "10.123.123.123"}], "internal": [{"addr": "10.123.123.123"}],
"public": [{"addr": "15.123.123.123"}]} "public": [{"addr": "15.123.123.123"}]}
self.orig_label_regex = CONF.network_label_regex
self.orig_ip_regex = CONF.ip_regex self.orig_ip_regex = CONF.ip_regex
def tearDown(self): def tearDown(self):
super(InstanceViewsTest, self).tearDown() super(InstanceViewsTest, self).tearDown()
CONF.network_label_regex = self.orig_label_regex
CONF.ip_regex = self.orig_ip_regex CONF.ip_regex = self.orig_ip_regex
@ -59,7 +57,8 @@ class InstanceDetailViewTest(trove_testtools.TestCase):
self.instance.addresses = {"private": [{"addr": self.ip}]} self.instance.addresses = {"private": [{"addr": self.ip}]}
self.instance.volume_used = '3' self.instance.volume_used = '3'
self.instance.root_password = 'iloveyou' self.instance.root_password = 'iloveyou'
self.instance.get_visible_ip_addresses = lambda: ["1.2.3.4"] self.instance.get_visible_ip_addresses.return_value = [
{'type': 'private', 'address': '1.2.3.4'}]
self.instance.slave_of_id = None self.instance.slave_of_id = None
self.instance.slaves = [] self.instance.slaves = []
self.instance.locality = 'affinity' self.instance.locality = 'affinity'
@ -104,6 +103,8 @@ class InstanceDetailViewTest(trove_testtools.TestCase):
result['instance']['datastore']['version']) result['instance']['datastore']['version'])
self.assertNotIn('hostname', result['instance']) self.assertNotIn('hostname', result['instance'])
self.assertEqual([self.ip], result['instance']['ip']) self.assertEqual([self.ip], result['instance']['ip'])
self.assertEqual(self.ip,
result['instance']['addresses'][0]['address'])
def test_locality(self): def test_locality(self):
self.instance.hostname = None self.instance.hostname = None

View File

@ -932,7 +932,7 @@ class BuiltInstanceTasksTest(trove_testtools.TestCase):
floating_ips) floating_ips)
@patch.object(BaseInstance, 'get_visible_ip_addresses', @patch.object(BaseInstance, 'get_visible_ip_addresses',
return_value=['192.168.10.1']) return_value=[{'address': '192.168.10.1', 'type': 'public'}])
def test_detach_public_ips(self, mock_address): def test_detach_public_ips(self, mock_address):
removed_ips = self.instance_task.detach_public_ips() removed_ips = self.instance_task.detach_public_ips()
self.assertEqual(['fake-floatingip-id'], removed_ips) self.assertEqual(['fake-floatingip-id'], removed_ips)