compass-core/bin/manage_db.py
xiaodongwang 2f5c551505 fix manage db code style.
Change-Id: I824e6b6275b5bffce970c447020c237779b7932a
2014-01-28 12:58:22 -08:00

766 lines
27 KiB
Python
Executable File

#!/usr/bin/python
"""utility binary to manage database."""
import logging
import os
import os.path
import re
import shutil
from flask.ext.script import Manager
from compass.api import app
from compass.config_management.utils import config_manager
from compass.config_management.utils import config_reference
from compass.db import database
from compass.db.model import Adapter, Role, Switch, SwitchConfig
from compass.db.model import Machine, HostState, ClusterState
from compass.db.model import Cluster, ClusterHost, LogProgressingHistory
from compass.utils import flags
from compass.utils import logsetting
from compass.utils import setting_wrapper as setting
flags.add('table_name',
help='table name',
default='')
flags.add('clusters',
help=(
'clusters to clean, the format is as '
'clusterid:hostname1,hostname2,...;...'),
default='')
flags.add('fake_switches_file',
help=(
'files for switches and machines '
'connected to each switch. each line in the file '
'is <switch ip>,<switch port>,<vlan>,<mac>'),
default='')
flags.add('fake_switches_vendor',
help='switch vendor used to set fake switch and machines.',
default='huawei')
flags.add('search_config_properties',
help='semicomma separated properties to search in config',
default='')
flags.add('print_config_properties',
help='semicomma separated config properties to print',
default='')
app_manager = Manager(app, usage="Perform database operations")
TABLE_MAPPING = {
'role': Role,
'adapter': Adapter,
'switch': Switch,
'switch_config': SwitchConfig,
'machine': Machine,
'hoststate': HostState,
'clusterstate': ClusterState,
'cluster': Cluster,
'clusterhost': ClusterHost,
'logprogressinghistory': LogProgressingHistory,
}
@app_manager.command
def list_config():
"List the configuration"
for key, value in app.config.items():
print key, value
@app_manager.command
def createdb():
"Creates database from sqlalchemy models"
if setting.DATABASE_TYPE == 'file':
if os.path.exists(setting.DATABASE_FILE):
os.remove(setting.DATABASE_FILE)
database.create_db()
if setting.DATABASE_TYPE == 'file':
os.chmod(setting.DATABASE_FILE, 0777)
@app_manager.command
def dropdb():
"Drops database from sqlalchemy models"
database.drop_db()
@app_manager.command
def createtable():
"""Create database table by --table_name"""
table_name = flags.OPTIONS.table_name
if table_name and table_name in TABLE_MAPPING:
database.create_table(TABLE_MAPPING[table_name])
else:
print '--table_name should be in %s' % TABLE_MAPPING.keys()
@app_manager.command
def droptable():
"""Drop database table by --talbe_name"""
table_name = flags.OPTIONS.table_name
if table_name and table_name in TABLE_MAPPING:
database.drop_table(TABLE_MAPPING[table_name])
else:
print '--table_name should be in %s' % TABLE_MAPPING.keys()
@app_manager.command
def sync_from_installers():
"""set adapters in Adapter table from installers."""
# TODO(xiaodong): Move the code to config_manager.
manager = config_manager.ConfigManager()
adapters = manager.get_adapters()
target_systems = set()
roles_per_target_system = {}
for adapter in adapters:
target_systems.add(adapter['target_system'])
for target_system in target_systems:
roles_per_target_system[target_system] = manager.get_roles(
target_system)
with database.session() as session:
session.query(Adapter).delete()
session.query(Role).delete()
for adapter in adapters:
session.add(Adapter(**adapter))
for target_system, roles in roles_per_target_system.items():
for role in roles:
session.add(Role(**role))
def _get_switch_ips(switch):
"""Helper function to get switch ips."""
ips = []
blocks = switch['switch_ips'].split('.')
ip_blocks_list = []
for block in blocks:
ip_blocks_list.append([])
sub_blocks = block.split(',')
for sub_block in sub_blocks:
if not sub_block:
continue
if '-' in sub_block:
start_block, end_block = sub_block.split('-', 1)
start_block = int(start_block)
end_block = int(end_block)
if start_block > end_block:
continue
ip_block = start_block
while ip_block <= end_block:
ip_blocks_list[-1].append(str(ip_block))
ip_block += 1
else:
ip_blocks_list[-1].append(sub_block)
ip_prefixes = [[]]
for ip_blocks in ip_blocks_list:
prefixes = []
for ip_block in ip_blocks:
for prefix in ip_prefixes:
prefixes.append(prefix + [ip_block])
ip_prefixes = prefixes
for prefix in ip_prefixes:
if not prefix:
continue
ips.append('.'.join(prefix))
logging.debug('found switch ips: %s', ips)
return ips
def _get_switch_filter_ports(switch):
"""Helper function to get switch filter ports."""
port_pat = re.compile(r'(\D*)(\d+(?:-\d+)?)')
filter_ports = []
for port_range in switch['filter_ports'].split(','):
if not port_range:
continue
mat = port_pat.match(port_range)
if not mat:
filter_ports.append(port_range)
else:
port_prefix = mat.group(1)
port_range = mat.group(2)
if '-' in port_range:
start_port, end_port = port_range.split('-', 1)
start_port = int(start_port)
end_port = int(end_port)
if start_port > end_port:
continue
port = start_port
while port <= end_port:
filter_ports.append('%s%s' % (port_prefix, port))
port += 1
else:
filter_ports.append('%s%s' % (port_prefix, port_range))
logging.debug('filter ports: %s', filter_ports)
return filter_ports
def _get_switch_config():
"""Helper function to get switch config."""
switch_configs = []
if not hasattr(setting, 'SWITCHES') or not setting.SWITCHES:
logging.info('no switch configs to set')
return switch_configs
for switch in setting.SWITCHES:
ips = _get_switch_ips(switch)
filter_ports = _get_switch_filter_ports(switch)
for ip_addr in ips:
for filter_port in filter_ports:
switch_configs.append(
{'ip': ip_addr, 'filter_port': filter_port})
logging.debug('switch configs: %s', switch_configs)
return switch_configs
@app_manager.command
def sync_switch_configs():
"""Set switch configs in SwitchConfig table from setting.
.. note::
the switch config is stored in SWITCHES list in setting config.
for each entry in the SWITCHES, its type is dict and must contain
fields 'switch_ips' and 'filter_ports'.
The format of switch_ips is
<ip_blocks>.<ip_blocks>.<ip_blocks>.<ip_blocks>.
ip_blocks consists of ip_block separated by comma.
ip_block can be an integer and a range of integer like xx-xx.
The example of switch_ips is like: xxx.xxx.xxx-yyy,xxx-yyy.xxx,yyy
The format of filter_ports consists of list of
<port_prefix><port_range> separated by comma. port_range can be an
integer or a rnage of integer like xx-xx.
The example of filter_ports is like: ae1-5,20-40.
"""
switch_configs = _get_switch_config()
switch_config_tuples = set([])
with database.session() as session:
session.query(SwitchConfig).delete(synchronize_session='fetch')
for switch_config in switch_configs:
switch_config_tuple = tuple(switch_config.values())
if switch_config_tuple in switch_config_tuples:
logging.debug('ignore adding switch config: %s',
switch_config)
continue
else:
logging.debug('add switch config: %s', switch_config)
switch_config_tuples.add(switch_config_tuple)
session.add(SwitchConfig(**switch_config))
def _get_clusters():
"""Helper function to get clusters from flag --clusters."""
clusters = {}
logging.debug('get clusters from flag: %s', flags.OPTIONS.clusters)
for clusterid_and_hostnames in flags.OPTIONS.clusters.split(';'):
if not clusterid_and_hostnames:
continue
if ':' in clusterid_and_hostnames:
clusterid_str, hostnames_str = clusterid_and_hostnames.split(
':', 1)
else:
clusterid_str = clusterid_and_hostnames
hostnames_str = ''
clusterid = int(clusterid_str)
hostnames = [
hostname for hostname in hostnames_str.split(',')
if hostname
]
clusters[clusterid] = hostnames
logging.debug('got clusters from flag: %s', clusters)
with database.session() as session:
clusterids = clusters.keys()
if not clusterids:
cluster_list = session.query(Cluster).all()
clusterids = [cluster.id for cluster in cluster_list]
for clusterid in clusterids:
hostnames = clusters.get(clusterid, [])
if not hostnames:
host_list = session.query(ClusterHost).filter_by(
cluster_id=clusterid).all()
hostids = [host.id for host in host_list]
clusters[clusterid] = hostids
else:
hostids = []
for hostname in hostnames:
host = session.query(ClusterHost).filter_by(
cluster_id=clusterid, hostname=hostname).first()
if host:
hostids.append(host.id)
clusters[clusterid] = hostids
return clusters
def _clean_clusters(clusters):
"""Helper function to clean clusters."""
# TODO(xiaodong): Move the code to config manager.
manager = config_manager.ConfigManager()
logging.info('clean cluster hosts: %s', clusters)
with database.session() as session:
for clusterid, hostids in clusters.items():
cluster = session.query(Cluster).filter_by(id=clusterid).first()
if not cluster:
continue
all_hostids = [host.id for host in cluster.hosts]
logging.debug('all hosts in cluster %s is: %s',
clusterid, all_hostids)
logging.info('clean hosts %s in cluster %s',
hostids, clusterid)
adapter = cluster.adapter
for hostid in hostids:
host = session.query(ClusterHost).filter_by(id=hostid).first()
if not host:
continue
log_dir = os.path.join(
setting.INSTALLATION_LOGDIR,
'%s.%s' % (host.hostname, clusterid))
logging.info('clean log dir %s', log_dir)
shutil.rmtree(log_dir, True)
session.query(LogProgressingHistory).filter(
LogProgressingHistory.pathname.startswith(
'%s/' % log_dir)).delete(
synchronize_session='fetch')
logging.info('clean host %s', hostid)
manager.clean_host_config(
hostid,
os_version=adapter.os,
target_system=adapter.target_system)
session.query(ClusterHost).filter_by(
id=hostid).delete(synchronize_session='fetch')
session.query(HostState).filter_by(
id=hostid).delete(synchronize_session='fetch')
if set(all_hostids) == set(hostids):
logging.info('clean cluster %s', clusterid)
manager.clean_cluster_config(
clusterid,
os_version=adapter.os,
target_system=adapter.target_system)
session.query(Cluster).filter_by(
id=clusterid).delete(synchronize_session='fetch')
session.query(ClusterState).filter_by(
id=clusterid).delete(synchronize_session='fetch')
manager.sync()
@app_manager.command
def clean_clusters():
"""Delete clusters and hosts.
.. note::
The clusters and hosts are defined in --clusters.
the clusters flag is as clusterid:hostname1,hostname2,...;...
"""
clusters = _get_clusters()
_clean_clusters(clusters)
os.system('service rsyslog restart')
def _clean_installation_progress(clusters):
"""Helper function to clean installation progress."""
# TODO(xiaodong): Move the code to config manager.
logging.info('clean installation progress for cluster hosts: %s',
clusters)
with database.session() as session:
for clusterid, hostids in clusters.items():
cluster = session.query(Cluster).filter_by(
id=clusterid).first()
if not cluster:
continue
logging.info(
'clean installation progress for hosts %s in cluster %s',
hostids, clusterid)
all_hostids = [host.id for host in cluster.hosts]
logging.debug('all hosts in cluster %s is: %s',
clusterid, all_hostids)
for hostid in hostids:
host = session.query(ClusterHost).filter_by(id=hostid).first()
if not host:
continue
log_dir = os.path.join(
setting.INSTALLATION_LOGDIR,
'%s.%s' % (host.hostname, clusterid))
logging.info('clean log dir %s', log_dir)
shutil.rmtree(log_dir, True)
session.query(LogProgressingHistory).filter(
LogProgressingHistory.pathname.startswith(
'%s/' % log_dir)).delete(
synchronize_session='fetch')
logging.info('clean host installation progress for %s',
hostid)
if host.state and host.state.state != 'UNINITIALIZED':
session.query(ClusterHost).filter_by(
id=hostid).update({
'mutable': False
}, synchronize_session='fetch')
session.query(HostState).filter_by(id=hostid).update({
'state': 'INSTALLING',
'progress': 0.0,
'message': '',
'severity': 'INFO'
}, synchronize_session='fetch')
if set(all_hostids) == set(hostids):
logging.info('clean cluster installation progress %s',
clusterid)
if cluster.state and cluster.state != 'UNINITIALIZED':
session.query(Cluster).filter_by(
id=clusterid).update({
'mutable': False
}, synchronize_session='fetch')
session.query(ClusterState).filter_by(
id=clusterid).update({
'state': 'INSTALLING',
'progress': 0.0,
'message': '',
'severity': 'INFO'
}, synchronize_session='fetch')
@app_manager.command
def clean_installation_progress():
"""Clean clusters and hosts installation progress.
.. note::
The cluster and hosts is defined in --clusters.
The clusters flags is as clusterid:hostname1,hostname2,...;...
"""
clusters = _get_clusters()
_clean_installation_progress(clusters)
os.system('service rsyslog restart')
def _reinstall_hosts(clusters):
"""Helper function to reinstall hosts."""
# TODO(xiaodong): Move the code to config_manager.
logging.info('reinstall cluster hosts: %s', clusters)
manager = config_manager.ConfigManager()
with database.session() as session:
for clusterid, hostids in clusters.items():
cluster = session.query(Cluster).filter_by(id=clusterid).first()
if not cluster:
continue
all_hostids = [host.id for host in cluster.hosts]
logging.debug('all hosts in cluster %s is: %s',
clusterid, all_hostids)
logging.info('reinstall hosts %s in cluster %s',
hostids, clusterid)
adapter = cluster.adapter
for hostid in hostids:
host = session.query(ClusterHost).filter_by(id=hostid).first()
if not host:
continue
log_dir = os.path.join(
setting.INSTALLATION_LOGDIR,
'%s.%s' % (host.hostname, clusterid))
logging.info('clean log dir %s', log_dir)
shutil.rmtree(log_dir, True)
session.query(LogProgressingHistory).filter(
LogProgressingHistory.pathname.startswith(
'%s/' % log_dir)).delete(
synchronize_session='fetch')
logging.info('reinstall host %s', hostid)
manager.reinstall_host(
hostid,
os_version=adapter.os,
target_system=adapter.target_system)
if host.state and host.state.state != 'UNINITIALIZED':
session.query(ClusterHost).filter_by(
id=hostid).update({
'mutable': False
}, synchronize_session='fetch')
session.query(HostState).filter_by(
id=hostid).update({
'state': 'INSTALLING',
'progress': 0.0,
'message': '',
'severity': 'INFO'
}, synchronize_session='fetch')
if set(all_hostids) == set(hostids):
logging.info('reinstall cluster %s',
clusterid)
if cluster.state and cluster.state != 'UNINITIALIZED':
session.query(Cluster).filter_by(
id=clusterid).update({
'mutable': False
}, synchronize_session='fetch')
session.query(ClusterState).filter_by(
id=clusterid).update({
'state': 'INSTALLING',
'progress': 0.0,
'message': '',
'severity': 'INFO'
}, synchronize_session='fetch')
manager.sync()
@app_manager.command
def reinstall_hosts():
"""Reinstall hosts in clusters.
.. note::
The hosts are defined in --clusters.
The clusters flag is as clusterid:hostname1,hostname2,...;...
"""
clusters = _get_clusters()
_reinstall_hosts(clusters)
os.system('service rsyslog restart')
def _get_fake_switch_machines(switch_ips, switch_machines):
"""Helper function to get fake switch machines."""
missing_flags = False
if not flags.OPTIONS.fake_switches_vendor:
print 'the flag --fake_switches_vendor should be specified'
missing_flags = True
if not flags.OPTIONS.fake_switches_file:
print 'the flag --fake_switches_file should be specified.'
print 'each line in fake_switches_files presents one machine'
print 'the format of each line is <%s>,<%s>,<%s>,<%s>' % (
'switch ip as xxx.xxx.xxx.xxx',
'switch port as xxx12',
'vlan as 1',
'mac as xx:xx:xx:xx:xx:xx')
missing_flags = True
if missing_flags:
return False
try:
with open(flags.OPTIONS.fake_switches_file) as switch_file:
for line in switch_file:
line = line.strip()
switch_ip, switch_port, vlan, mac = line.split(',', 3)
if switch_ip not in switch_ips:
switch_ips.append(switch_ip)
switch_machines.setdefault(switch_ip, []).append({
'mac': mac,
'port': switch_port,
'vlan': int(vlan)
})
except Exception as error:
logging.error('failed to parse file %s',
flags.OPTIONS.fake_switches_file)
logging.exception(error)
return False
return True
@app_manager.command
def set_fake_switch_machine():
"""Set fake switches and machines.
.. note::
--fake_switches_vendor is the vendor name for all fake switches.
the default value is 'huawei'
--fake_switches_file is the filename which stores all fake switches
and fake machines.
each line in fake_switches_files presents one machine.
the format of each line <switch_ip>,<switch_port>,<vlan>,<mac>.
"""
# TODO(xiaodong): Move the main code to config manager.
switch_ips = []
switch_machines = {}
vendor = flags.OPTIONS.fake_switches_vendor
credential = {
'version' : 'v2c',
'community' : 'public',
}
if not _get_fake_switch_machines(switch_ips, switch_machines):
return
with database.session() as session:
session.query(Switch).delete(synchronize_session='fetch')
session.query(Machine).delete(synchronize_session='fetch')
for switch_ip in switch_ips:
logging.info('add switch %s', switch_ip)
switch = Switch(ip=switch_ip, vendor_info=vendor,
credential=credential,
state='under_monitoring')
logging.debug('add switch %s', switch_ip)
session.add(switch)
machines = switch_machines[switch_ip]
for item in machines:
logging.debug('add machine %s', item)
machine = Machine(**item)
machine.switch = switch
session.add(machine)
def _get_config_properties():
"""Helper function to get config properties."""
if not flags.OPTIONS.search_config_properties:
logging.info('the flag --search_config_properties is not specified.')
return {}
search_config_properties = flags.OPTIONS.search_config_properties
config_properties = {}
for config_property in search_config_properties.split(';'):
if not config_property:
continue
if '=' not in config_property:
logging.debug('ignore config property %s '
'since there is no = in it.', config_property)
continue
property_name, property_value = config_property.split('=', 1)
config_properties[property_name] = property_value
logging.debug('get search config properties: %s', config_properties)
return config_properties
def _get_print_properties():
"""Helper function to get what properties to print."""
if not flags.OPTIONS.print_config_properties:
logging.info('the flag --print_config_properties is not specified.')
return []
print_config_properties = flags.OPTIONS.print_config_properties
config_properties = []
for config_property in print_config_properties.split(';'):
if not config_property:
continue
config_properties.append(config_property)
logging.debug('get print config properties: %s', config_properties)
return config_properties
def _match_config_properties(config, config_properties):
"""Helper function to check if config properties are match."""
# TODO(xiaodong): Move the code to config manager.
ref = config_reference.ConfigReference(config)
for property_name, property_value in config_properties.items():
config_value = ref.get(property_name)
if config_value is None:
return False
if isinstance(config_value, list):
found = False
for config_value_item in config_value:
if str(config_value_item) == str(property_value):
found = True
if not found:
return False
else:
if not str(config_value) == str(property_value):
return False
return True
def _print_config_properties(config, config_properties):
"""Helper function to print config properties."""
ref = config_reference.ConfigReference(config)
print_properties = []
for property_name in config_properties:
config_value = ref.get(property_name)
if config_value is None:
logging.error('did not found %s in %s',
property_name, config)
continue
print_properties.append('%s=%s' % (property_name, config_value))
print ';'.join(print_properties)
@app_manager.command
def search_hosts():
"""Search hosts by properties.
.. note::
--search_config_properties defines what properties are used to search.
the format of search_config_properties is as
<property_name>=<property_value>;... If no search properties are set,
It will returns properties of all hosts.
--print_config_properties defines what properties to print.
the format of print_config_properties is as
<property_name>;...
"""
config_properties = _get_config_properties()
print_properties = _get_print_properties()
with database.session() as session:
hosts = session.query(ClusterHost).all()
for host in hosts:
if _match_config_properties(host.config, config_properties):
_print_config_properties(host.config, print_properties)
@app_manager.command
def search_clusters():
"""Search clusters by properties.
.. note::
--search_config_properties defines what properties are used to search.
the format of search_config_properties is as
<property_name>=<property_value>;... If no search properties are set,
It will returns properties of all hosts.
--print_config_properties defines what properties to print.
the format of print_config_properties is as
<property_name>;...
"""
config_properties = _get_config_properties()
print_properties = _get_print_properties()
with database.session() as session:
clusters = session.query(Cluster).all()
for cluster in clusters:
if _match_config_properties(cluster.config, config_properties):
_print_config_properties(cluster.config, print_properties)
if __name__ == "__main__":
flags.init()
logsetting.init()
app_manager.run()