Make PATEntries and SRV Recordset creation idempotent.

This commit is contained in:
Pino de Candia 2018-01-16 23:52:23 +00:00
parent 4a9f96d253
commit be028d5cf3
8 changed files with 40 additions and 25 deletions

View File

@ -7,7 +7,7 @@ use_barbican_key_manager = True
#num_pat_bastions_per_server = 2 #num_pat_bastions_per_server = 2
#pat_dns_zone_name = tatuPAT.com. #pat_dns_zone_name = tatuPAT.com.
#pat_dns_zone_email = tatu@nono.nono #pat_dns_zone_email = tatu@nono.nono
#sqlalchemy_engine = mysql+pymysql://root:pinot@127.0.0.1/neutron?charset=utf8 sqlalchemy_engine = mysql+pymysql://root:pinot@127.0.0.1
auth_url = http://localhost/identity/v3 auth_url = http://localhost/identity/v3
user_id = fab01a1f2a7749b78a53dffe441a1879 user_id = fab01a1f2a7749b78a53dffe441a1879
password = pinot password = pinot

View File

@ -57,7 +57,7 @@ response = requests.post(
data=json.dumps(user) data=json.dumps(user)
) )
if response.status_code != 201: if response.status_code != 201:
print 'Failed: ' + response print 'Failed: ' + str(response)
exit() exit()
assert 'location' in response.headers assert 'location' in response.headers

View File

@ -213,7 +213,7 @@ class HostCerts(object):
'key-cert.pub': host.cert, 'key-cert.pub': host.cert,
'hostname': host.hostname, 'hostname': host.hostname,
} }
if CONF.tatu.use_pat_bastion: if CONF.tatu.use_pat_bastions:
item['pat_bastions'] = ','.join( item['pat_bastions'] = ','.join(
'{}:{}'.format(t[1], t[0]) for t in '{}:{}'.format(t[1], t[0]) for t in
get_port_ip_tuples(host.host_id, 22)) get_port_ip_tuples(host.host_id, 22))
@ -290,7 +290,7 @@ class NovaVendorData(object):
# TODO(pino): make the whole workflow fault-tolerant # TODO(pino): make the whole workflow fault-tolerant
# TODO(pino): make this configurable per project or subnet # TODO(pino): make this configurable per project or subnet
if CONF.tatu.use_pat_bastion: if CONF.tatu.use_pat_bastions:
port_ip_tuples = create_pat_entries(self.session, port_ip_tuples = create_pat_entries(self.session,
req.body['instance-id'], 22) req.body['instance-id'], 22)
add_srv_records(req.body['hostname'], req.body['project-id'], add_srv_records(req.body['hostname'], req.body['project-id'],

View File

@ -41,7 +41,7 @@ opts = [
default='tatu@nono.nono', default='tatu@nono.nono',
help='Email of admin for DNS zone for PAT bastions'), help='Email of admin for DNS zone for PAT bastions'),
cfg.StrOpt('sqlalchemy_engine', cfg.StrOpt('sqlalchemy_engine',
default='mysql+pymysql://root:pinot@127.0.0.1/neutron?charset=utf8', default='mysql+pymysql://root:pinot@127.0.0.1',
help='SQLAlchemy database URL'), help='SQLAlchemy database URL'),
cfg.StrOpt('auth_url', cfg.StrOpt('auth_url',
default='http://localhost/identity/v3', default='http://localhost/identity/v3',
@ -64,12 +64,12 @@ logging.register_options(CONF)
log_levels = logging.get_default_log_levels() + \ log_levels = logging.get_default_log_levels() + \
['tatu=DEBUG', '__main__=DEBUG'] ['tatu=DEBUG', '__main__=DEBUG']
logging.set_defaults(default_log_levels=log_levels) logging.set_defaults(default_log_levels=log_levels)
try: try:
CONF(args=[], CONF(args=[], default_config_files=['/etc/tatu/tatu.conf',
default_config_files=['/etc/tatu/tatu.conf', 'files/tatu.conf',
'files/tatu.conf' '/etc/neutron/dragonflow.ini'])
]
)
except Exception as e: except Exception as e:
LOG.error("Failed to load configuration file: {}".format(e)) LOG.error("Failed to load configuration file: {}".format(e))
@ -91,7 +91,6 @@ NOVA = nova_client.Client('2', session=session)
NEUTRON = neutron_client.Client(session=session) NEUTRON = neutron_client.Client(session=session)
DESIGNATE = designate_client.Client(session=session) DESIGNATE = designate_client.Client(session=session)
dragonflow_cfg.CONF(args=[], default_config_files=['/etc/neutron/dragonflow.ini'])
dragonflow_cfg.CONF.set_override('enable_df_pub_sub', False, group='df') dragonflow_cfg.CONF.set_override('enable_df_pub_sub', False, group='df')
DRAGONFLOW = api_nb.NbApi.get_instance(False) DRAGONFLOW = api_nb.NbApi.get_instance(False)

View File

@ -68,7 +68,7 @@ class UserCert(Base):
__tablename__ = 'user_certs' __tablename__ = 'user_certs'
user_id = sa.Column(sa.String(36), primary_key=True) user_id = sa.Column(sa.String(36), primary_key=True)
fingerprint = sa.Column(sa.String(36), primary_key=True) fingerprint = sa.Column(sa.String(60), primary_key=True)
auth_id = sa.Column(sa.String(36), sa.ForeignKey('authorities.auth_id')) auth_id = sa.Column(sa.String(36), sa.ForeignKey('authorities.auth_id'))
cert = sa.Column(sa.Text) cert = sa.Column(sa.Text)

View File

@ -11,16 +11,22 @@
# under the License. # under the License.
import os import os
from oslo_log import log as logging
from sqlalchemy import create_engine from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker, scoped_session from sqlalchemy.orm import sessionmaker, scoped_session
from tatu import config from tatu import config
from tatu.db.models import Base from tatu.db.models import Base
LOG = logging.getLogger(__name__)
class SQLAlchemySessionManager(object): class SQLAlchemySessionManager(object):
def __init__(self): def __init__(self):
LOG.info('Creating sqlalchemy engine {}'.format(config.CONF.tatu.sqlalchemy_engine))
self.engine = create_engine(config.CONF.tatu.sqlalchemy_engine) self.engine = create_engine(config.CONF.tatu.sqlalchemy_engine)
self.engine.execute("CREATE DATABASE IF NOT EXISTS tatu;")
self.engine.execute("USE tatu;")
Base.metadata.create_all(self.engine) Base.metadata.create_all(self.engine)
self.Session = scoped_session(sessionmaker(self.engine)) self.Session = scoped_session(sessionmaker(self.engine))

View File

@ -57,11 +57,15 @@ def add_srv_records(hostname, project_id, port_ip_tuples):
for port, ip in port_ip_tuples: for port, ip in port_ip_tuples:
bastion = bastion_name_from_ip(ip) bastion = bastion_name_from_ip(ip)
# SRV record format is: priority weight port A-name # SRV record format is: priority weight port A-name
records.add( records.append(
'10 50 {} {}'.format(port, bastion)) '10 50 {} {}'.format(port, bastion))
DESIGNATE.recordsets.create(ZONE['id'], get_srv_url(hostname, project_id), try:
'SRV', records) DESIGNATE.recordsets.create(ZONE['id'],
get_srv_url(hostname, project_id),
'SRV', records)
except Conflict:
pass
_setup_zone() _setup_zone()

View File

@ -86,26 +86,32 @@ def _df_find_lrouter_by_lport(lport):
def get_port_ip_tuples(instance_id, fixed_lport): def get_port_ip_tuples(instance_id, fixed_lport):
port_ip_tuples = [] port_ip_tuples = []
all_entries = DRAGONFLOW.get_all(PATEntry)
LOG.debug('Found {} PATEntries: {}'.format(len(all_entries), all_entries))
server = NOVA.servers.get(instance_id) server = NOVA.servers.get(instance_id)
ifaces = server.interface_list() ifaces = server.interface_list()
for iface in ifaces: for iface in ifaces:
lport = DRAGONFLOW.get(LogicalPort(id=iface['port_id'])) lport = DRAGONFLOW.get(LogicalPort(id=iface.port_id))
lrouter = _df_find_lrouter_by_lport(lport) lrouter = _df_find_lrouter_by_lport(lport)
if lrouter is None: continue if lrouter is None: continue
pat_entries = DRAGONFLOW.get(PATEntry(lport=lport)) for entry in all_entries:
for entry in pat_entries: if entry.lport.id == lport.id and entry.fixed_l4_port == fixed_lport:
if entry.fixed_l4_port == fixed_lport: pat = DRAGONFLOW.get(PAT(id=entry.pat.id))
port_ip_tuples.append((entry.pat_l4_port, str(entry.pat.ip))) port_ip_tuples.append((entry.pat_l4_port, str(pat.ip_address)))
if port_ip_tuples: break
return port_ip_tuples return port_ip_tuples
def create_pat_entries(sql_session, instance_id, fixed_l4_port, def create_pat_entries(sql_session, instance_id, fixed_l4_port,
num=CONF.tatu.num_pat_bastions_per_server): num=CONF.tatu.num_pat_bastions_per_server):
port_ip_tuples = [] port_ip_tuples = get_port_ip_tuples(instance_id, fixed_l4_port)
LOG.debug('Found {} tuples: {}'.format(len(port_ip_tuples), port_ip_tuples))
if port_ip_tuples: return port_ip_tuples
LOG.debug('Creating new tuples.')
server = NOVA.servers.get(instance_id) server = NOVA.servers.get(instance_id)
ifaces = server.interface_list() ifaces = server.interface_list()
for iface in ifaces: for iface in ifaces:
lport = DRAGONFLOW.get(LogicalPort(id=iface['port_id'])) lport = DRAGONFLOW.get(LogicalPort(id=iface.port_id))
# TODO(pino): no router? consider SNAT of source IP to 169.254.169.254 # TODO(pino): no router? consider SNAT of source IP to 169.254.169.254
lrouter = _df_find_lrouter_by_lport(lport) lrouter = _df_find_lrouter_by_lport(lport)
if lrouter is None: continue if lrouter is None: continue
@ -114,7 +120,7 @@ def create_pat_entries(sql_session, instance_id, fixed_l4_port,
if (num < len(PATS)): if (num < len(PATS)):
pats = random.sample(pats, num) pats = random.sample(pats, num)
for pat in pats: for pat in pats:
pat_l4_port = tatu_db.reserve_l4_port(sql_session, str(pat.ip)) pat_l4_port = tatu_db.reserve_l4_port(sql_session, str(pat.ip_address))
pat_entry = PATEntry( pat_entry = PATEntry(
id = '{}:{}'.format(pat.id, pat_l4_port), id = '{}:{}'.format(pat.id, pat_l4_port),
topic = 'tatu', topic = 'tatu',
@ -123,10 +129,10 @@ def create_pat_entries(sql_session, instance_id, fixed_l4_port,
fixed_ip_address = _get_ip4_from_lport(lport), fixed_ip_address = _get_ip4_from_lport(lport),
fixed_l4_port = fixed_l4_port, fixed_l4_port = fixed_l4_port,
lport = lport, lport = lport,
lrouter = df_fields.ReferenceField(LogicalRouter), lrouter = lrouter,
) )
DRAGONFLOW.create(pat_entry) DRAGONFLOW.create(pat_entry)
port_ip_tuples.append((pat_l4_port, str(pat.ip))) port_ip_tuples.append((pat_l4_port, str(pat.ip_address)))
# if we got here, we now have the required pat_entries # if we got here, we now have the required pat_entries
break break
return port_ip_tuples return port_ip_tuples