func readAgentBasicInfo(hostname string) (Agent, string, error) { agent := Agent{} token := "" query := fmt.Sprintf(` select hostname, port, token, last_submitted, mysql_port from host_agent where hostname = '%s' `, hostname) db, err := db.OpenOrchestrator() if err != nil { return agent, "", err } err = sqlutils.QueryRowsMap(db, query, func(m sqlutils.RowMap) error { agent.Hostname = m.GetString("hostname") agent.Port = m.GetInt("port") agent.LastSubmitted = m.GetString("last_submitted") agent.MySQLPort = m.GetInt64("mysql_port") token = m.GetString("token") return nil }) if token == "" { return agent, "", log.Errorf("Cannot get agent/token: %s", hostname) } return agent, token, nil }
// ReadClusterInstances reads all instances of a given cluster func ReadClusterInstances(clusterName string) ([](*Instance), error) { instances := [](*Instance){} db, err := db.OpenOrchestrator() if err != nil { return instances, log.Errore(err) } if strings.Index(clusterName, "'") >= 0 { return instances, log.Errorf("Invalid cluster name: %s", clusterName) } query := fmt.Sprintf(` select *, timestampdiff(second, last_checked, now()) as seconds_since_last_checked, (last_checked <= last_seen) is true as is_last_check_valid, timestampdiff(second, last_seen, now()) as seconds_since_last_seen from database_instance where cluster_name = '%s' order by hostname, port`, clusterName) err = sqlutils.QueryRowsMap(db, query, func(m sqlutils.RowMap) error { instance := readInstanceRow(m) instances = append(instances, instance) return nil }) return instances, err }
// Seed func Seed(targetHostname string, sourceHostname string) (int64, error) { if targetHostname == sourceHostname { return 0, log.Errorf("Cannot seed %s onto itself", targetHostname) } seedId, err := SubmitSeedEntry(targetHostname, sourceHostname) if err != nil { return 0, log.Errore(err) } go func() { err := executeSeed(seedId, targetHostname, sourceHostname) updateSeedComplete(seedId, err) }() return seedId, nil }
// SearchInstances reads all instances qualifying for some searchString func SearchInstances(searchString string) ([](*Instance), error) { instances := [](*Instance){} db, err := db.OpenOrchestrator() if err != nil { return instances, log.Errore(err) } if strings.Index(searchString, "'") >= 0 { return instances, log.Errorf("Invalid searchString: %s", searchString) } query := fmt.Sprintf(` select *, timestampdiff(second, last_checked, now()) as seconds_since_last_checked, (last_checked <= last_seen) is true as is_last_check_valid, timestampdiff(second, last_seen, now()) as seconds_since_last_seen from database_instance where hostname like '%%%s%%' or cluster_name like '%%%s%%' or server_id = '%s' or version like '%%%s%%' or port = '%s' or concat(hostname, ':', port) like '%%%s%%' order by cluster_name, hostname, port`, searchString, searchString, searchString, searchString, searchString, searchString) err = sqlutils.QueryRowsMap(db, query, func(m sqlutils.RowMap) error { instance := readInstanceRow(m) instances = append(instances, instance) return nil }) if err != nil { return instances, log.Errore(err) } err = PopulateInstancesAgents(instances) if err != nil { return instances, log.Errore(err) } return instances, err }
// MoveUp will attempt moving instance indicated by instanceKey up the topology hierarchy. // It will perform all safety and sanity checks and will tamper with this instance's replication // as well as its master. func MoveUp(instanceKey *InstanceKey) (*Instance, error) { instance, err := ReadTopologyInstance(instanceKey) if err != nil { return instance, err } if !instance.IsSlave() { return instance, errors.New(fmt.Sprintf("instance is not a slave: %+v", instanceKey)) } rinstance, _, _ := ReadInstance(&instance.Key) if canMove, merr := rinstance.CanMove(); !canMove { return instance, merr } master, err := GetInstanceMaster(instance) if err != nil { return instance, log.Errorf("Cannot GetInstanceMaster() for %+v. error=%+v", instance, err) } if !master.IsSlave() { return instance, errors.New(fmt.Sprintf("master is not a slave itself: %+v", master.Key)) } if canReplicate, err := instance.CanReplicateFrom(master); canReplicate == false { return instance, err } log.Infof("Will move %+v up the topology", *instanceKey) if maintenanceToken, merr := BeginMaintenance(instanceKey, "orchestrator", "move up"); merr != nil { err = errors.New(fmt.Sprintf("Cannot begin maintenance on %+v", *instanceKey)) goto Cleanup } else { defer EndMaintenance(maintenanceToken) } if maintenanceToken, merr := BeginMaintenance(&master.Key, "orchestrator", fmt.Sprintf("child %+v moves up", *instanceKey)); merr != nil { err = errors.New(fmt.Sprintf("Cannot begin maintenance on %+v", master.Key)) goto Cleanup } else { defer EndMaintenance(maintenanceToken) } master, err = StopSlave(&master.Key) if err != nil { goto Cleanup } instance, err = StopSlave(instanceKey) if err != nil { goto Cleanup } instance, err = StartSlaveUntilMasterCoordinates(instanceKey, &master.SelfBinlogCoordinates) if err != nil { goto Cleanup } instance, err = ChangeMasterTo(instanceKey, &master.MasterKey, &master.ExecBinlogCoordinates) if err != nil { goto Cleanup } Cleanup: instance, _ = StartSlave(instanceKey) master, _ = StartSlave(&master.Key) if err != nil { return instance, log.Errore(err) } // and we're done (pending deferred functions) AuditOperation("move-up", instanceKey, fmt.Sprintf("moved up %+v. Previous master: %+v", *instanceKey, master.Key)) return instance, err }
// ReadTopologyInstance connects to a topology MySQL instance and reads its configuration and // replication status. It writes read info into orchestrator's backend. func ReadTopologyInstance(instanceKey *InstanceKey) (*Instance, error) { defer func() { if err := recover(); err != nil { log.Errorf("Unexpected error: %+v", err) } }() instance := NewInstance() instanceFound := false foundBySlaveHosts := false db, err := db.OpenTopology(instanceKey.Hostname, instanceKey.Port) if err != nil { goto Cleanup } instance.Key = *instanceKey err = db.QueryRow("select @@global.server_id, @@global.version, @@global.binlog_format, @@global.log_bin, @@global.log_slave_updates").Scan( &instance.ServerID, &instance.Version, &instance.Binlog_format, &instance.LogBinEnabled, &instance.LogSlaveUpdatesEnabled) if err != nil { goto Cleanup } instanceFound = true err = sqlutils.QueryRowsMap(db, "show slave status", func(m sqlutils.RowMap) error { instance.Slave_IO_Running = (m.GetString("Slave_IO_Running") == "Yes") instance.Slave_SQL_Running = (m.GetString("Slave_SQL_Running") == "Yes") instance.ReadBinlogCoordinates.LogFile = m.GetString("Master_Log_File") instance.ReadBinlogCoordinates.LogPos = m.GetInt64("Read_Master_Log_Pos") instance.ExecBinlogCoordinates.LogFile = m.GetString("Relay_Master_Log_File") instance.ExecBinlogCoordinates.LogPos = m.GetInt64("Exec_Master_Log_Pos") masterKey, err := NewInstanceKeyFromStrings(m.GetString("Master_Host"), m.GetString("Master_Port")) if err != nil { log.Errore(err) } instance.MasterKey = *masterKey instance.SecondsBehindMaster = m.GetNullInt64("Seconds_Behind_Master") if config.Config.SlaveLagQuery == "" { instance.SlaveLagSeconds = instance.SecondsBehindMaster } // Not breaking the flow even on error return nil }) if err != nil { goto Cleanup } if config.Config.SlaveLagQuery != "" { err = db.QueryRow(config.Config.SlaveLagQuery).Scan(&instance.SlaveLagSeconds) if err != nil { goto Cleanup } } err = sqlutils.QueryRowsMap(db, "show master status", func(m sqlutils.RowMap) error { var err error instance.SelfBinlogCoordinates.LogFile = m.GetString("File") instance.SelfBinlogCoordinates.LogPos = m.GetInt64("Position") return err }) if err != nil { goto Cleanup } // Get slaves, either by SHOW SLAVE HOSTS or via PROCESSLIST if config.Config.DiscoverByShowSlaveHosts { err = sqlutils.QueryRowsMap(db, `show slave hosts`, func(m sqlutils.RowMap) error { slaveKey, err := NewInstanceKeyFromStrings(m.GetString("Host"), m.GetString("Port")) if err == nil { instance.AddSlaveKey(slaveKey) foundBySlaveHosts = true } return err }) if err != nil { goto Cleanup } } if !foundBySlaveHosts { // Either not configured to read SHOW SLAVE HOSTS or nothing was there. // Discover by processlist err = sqlutils.QueryRowsMap(db, ` select substring_index(host, ':', 1) as slave_hostname from information_schema.processlist where command='Binlog Dump'`, func(m sqlutils.RowMap) error { cname, err := GetCNAME(m.GetString("slave_hostname")) if err != nil { return err } slaveKey := InstanceKey{Hostname: cname, Port: instance.Key.Port} instance.AddSlaveKey(&slaveKey) return err }) if err != nil { goto Cleanup } } if err != nil { goto Cleanup } instance.ClusterName, err = ReadClusterNameByMaster(&instance.Key, &instance.MasterKey) if err != nil { goto Cleanup } Cleanup: if instanceFound { _ = WriteInstance(instance, err) } else { _ = UpdateInstanceLastChecked(instanceKey) } if err != nil { log.Errore(err) } return instance, err }