def start_cassandra(db_ips, db_master, keyname): """ Creates a monit configuration file and prompts Monit to start Cassandra. Args: db_ips: A list of database node IPs to start Cassandra on. db_master: The IP address of the DB master. keyname: A string containing the deployment's keyname. Raises: AppScaleDBError if unable to start Cassandra. """ logging.info("Starting Cassandra...") for ip in db_ips: init_config = '{script} --local-ip {ip} --master-ip {db_master}'.format( script=SETUP_CASSANDRA_SCRIPT, ip=ip, db_master=db_master) try: utils.ssh(ip, keyname, init_config) except subprocess.CalledProcessError: message = 'Unable to configure Cassandra on {}'.format(ip) logging.exception(message) raise dbconstants.AppScaleDBError(message) try: start_service_cmd = START_SERVICE_SCRIPT + CASSANDRA_WATCH_NAME utils.ssh(ip, keyname, start_service_cmd) except subprocess.CalledProcessError: message = 'Unable to start Cassandra on {}'.format(ip) logging.exception(message) raise dbconstants.AppScaleDBError(message) logging.info('Waiting for Cassandra to be ready') status_cmd = '{} status'.format(cassandra_interface.NODE_TOOL) while (utils.ssh(db_master, keyname, status_cmd, method=subprocess.call) != 0): time.sleep(5) logging.info("Successfully started Cassandra.")
def wait_for_quorum(keyname, db_nodes, replication): """ Waits until enough Cassandra nodes are up for a quorum. Args: keyname: A string containing the deployment's keyname. db_nodes: An integer specifying the total number of DB nodes. replication: An integer specifying the keyspace replication factor. """ command = cassandra_interface.NODE_TOOL + " " + 'status' key_file = '{}/{}.key'.format(utils.KEY_DIRECTORY, keyname) ssh_cmd = ['ssh', '-i', key_file, appscale_info.get_db_master_ip(), command] # Determine the number of nodes needed for a quorum. if db_nodes < 1 or replication < 1: raise dbconstants.AppScaleDBError('At least 1 database machine is needed.') if replication > db_nodes: raise dbconstants.AppScaleDBError( 'The replication factor cannot exceed the number of database machines.') can_fail = math.ceil(replication/2.0 - 1) needed = int(db_nodes - can_fail) while True: output = subprocess.check_output(ssh_cmd) nodes_ready = len( [line for line in output.splitlines() if line.startswith('UN')]) logging.info('{} nodes are up. {} are needed.'.format(nodes_ready, needed)) if nodes_ready >= needed: break time.sleep(1)
def estimate_total_entities(session, db_master, keyname): """ Estimate the total number of entities. Args: session: A cassandra-driver session. db_master: A string containing the IP address of the primary DB node. keyname: A string containing the deployment keyname. Returns: A string containing an entity count. Raises: AppScaleDBError if unable to get a count. """ query = SimpleStatement( 'SELECT COUNT(*) FROM "{}"'.format(dbconstants.APP_ENTITY_TABLE), consistency_level=ConsistencyLevel.ONE ) try: rows = session.execute(query)[0].count return str(rows / len(dbconstants.APP_ENTITY_SCHEMA)) except dbconstants.TRANSIENT_CASSANDRA_ERRORS: stats_cmd = '{nodetool} cfstats {keyspace}.{table}'.format( nodetool=cassandra_interface.NODE_TOOL, keyspace=cassandra_interface.KEYSPACE, table=dbconstants.APP_ENTITY_TABLE) stats = utils.ssh(db_master, keyname, stats_cmd, method=subprocess.check_output) for line in stats.splitlines(): if 'Number of keys (estimate)' in line: return '{} (estimate)'.format(line.split()[-1]) raise dbconstants.AppScaleDBError('Unable to estimate total entities.')