Esempio n. 1
0
func main() {
	var host = flag.String("H", "", "host (or hosts) to act on")
	var role = flag.String("R", "", "role (or roles) to act on")
	var proxy = flag.String("P", "127.0.0.1", "agent to interact with")
	var all = flag.Bool("A", false, "act on all live nodes")
	var fserial = flag.Bool("s", false, "print output when commands finish")
	var fbinary = flag.Bool("b", false, "force binary mode output")
	var ftext = flag.Bool("l", false, "force line mode output")
	var glock = flag.String("G", "", "global lock to claim")
	var llock = flag.String("L", "", "local lock to claim")
	var dzrhost = flag.String("doozer", "localhost:8046",
		"host:port for doozer")
	var jobs = flag.Int("j", 0, "jobs to run in parallel")
	var tout = flag.Int("t", 20, "timeout (in seconds) for jobs")
	var fnowarn = flag.Bool("w", false, "suppress error text output")

	flag.Usage = clientUsage
	flag.Parse()

	timeout = *tout
	if timeout < 0 {
		log.Error("timeout must be 0 (no timeout) or positive")
		os.Exit(1)
	}
	nowarn = *fnowarn

	// The output selection modes. By default, we assume that if the output
	// is from a single machine, it's binary and we don't touch it. However,
	// if the user is running against multiple targets, we assume that the
	// output is line-based and we prefix the hostname returning each line.
	//
	// Line based defaults to interleaved. The serial flag can be used to
	// ask us to buffer each host's output and then dump it all at once when
	// that host is done. This is more useful for doing a batch job and having
	// easily read output later, but still running commands in parallel.
	//
	// Finally, the user can force binary mode on multi-host outputs, which
	// makes us not touch the output.
	serial = *fserial
	binary = *fbinary && !*ftext

	// Uses the nice golog package to handle logging arguments and flags
	// so we don't have to worry about it.
	log = logging.InitFromFlags()
	safedoozer.SetLogger(log)

	// Connect to our doozer host. We have to do this early because alias
	// validation requires doozer.
	dzr = safedoozer.Dial(*dzrhost)
	defer dzr.Close()

	// Do some simple argument validation.
	args := expandAlias(flag.Args())
	if len(args) == 0 {
		flag.Usage()
		os.Exit(1)
	}
	if !isValidCommand(args[0], args[1:]) {
		os.Exit(1)
	}

	// Figure out localhost.
	var err error // If we use := below, we shadow the global, which is bad.
	hostname, err = os.Hostname()
	if err != nil {
		log.Error("failed getting hostname: %s", err)
		os.Exit(1)
	}
	hostname = strings.Split(hostname, ".")[0]
	if len(hostname) <= 0 {
		log.Error("hostname is empty!")
		os.Exit(1)
	}

	zmq_ctx, err = zmq.NewContext()
	if err != nil {
		log.Error("failed to init zmq: %s", err)
		os.Exit(1)
	}
	defer zmq_ctx.Close()

	// Connect to the proxy agent. This is the agent we will be having do all
	// of the work for us. This is probably localhost.
	psock = socketForIp(*proxy)
	if psock == nil {
		log.Error("unable to connect to proxy agent")
		os.Exit(1)
	}
	defer (*psock).Close()

	// Determine which nodes we will be addressing.
	hosts := make(map[string]bool)
	if *all {
		for _, host := range nodes() {
			hosts[host] = false
		}
	} else {
		if *host != "" {
			lhosts := strings.Split(*host, ",")
			for _, host := range lhosts {
				host = strings.TrimSpace(host)
				if len(host) > 0 {
					hosts[strings.TrimSpace(host)] = false
				}
			}
		}

		if *role != "" {
			roles := strings.Split(*role, ",")
			for _, role := range roles {
				lhosts := convertRoleToHosts(strings.TrimSpace(role))
				for _, host := range lhosts {
					hosts[host] = false
				}
			}
		}

		if *host == "" && *role == "" {
			// Both empty, default to this machine.
			hosts[hostname] = false
		}
	}

	// If no hosts, bail out.
	if len(hosts) <= 0 {
		log.Error("no hosts or roles specified")
		os.Exit(1)
	} else if len(hosts) == 1 {
		// If we're targetting a single machine, we want to use binary mode by
		// default unless the user explicitly set text mode.
		binary = true && !*ftext
	}

	// If we have been told to get a global lock, let's try to get that now.
	if *glock != "" {
		resp := proxyCommand("global_lock", *glock)
		if resp != "locked" {
			log.Error("failed to get global lock %s: %s", *glock, resp)
			os.Exit(1)
		}
		defer func(lock string) {
			resp := proxyCommand("global_unlock", *glock)
			if resp != "unlocked" {
				log.Error("failed to release global lock %s: %s", *glock, resp)
			}
		}(*glock)
	}

	// Same, local.
	if *llock != "" {
		resp := proxyCommand("local_lock", *llock)
		if resp != "locked" {
			log.Error("failed to get local lock %s: %s", *llock, resp)
			os.Exit(1)
		}
		defer func(lock string) {
			resp := proxyCommand("local_unlock", *llock)
			if resp != "unlocked" {
				log.Error("failed to release local lock %s: %s", *llock, resp)
			}
		}(*llock)
	}

	// There are some commands which are client-only and don't run against a
	// given set of hosts. For these, just execute locally and then we're done.
	switch args[0] {
	case "roles":
		cmdRoles()
	case "hosts":
		cmdHosts()
	case "alias":
		cmdAlias(args[1:])
	}

	// Queue up the jobs and then execute them.
	for host, _ := range hosts {
		queueJob(host, args)
	}
	runJobs(*jobs) // Returns when jobs are done.
}
Esempio n. 2
0
func main() {
	var myhost = flag.String("hostname", "", "this machine's hostname")
	var myport = flag.Int("port", 7330, "port number to listen on")
	var dzrhost = flag.String("doozer", "localhost:8046",
		"host:port for doozer")
	flag.Parse()

	// Uses the nice golog package to handle logging arguments and flags
	// so we don't have to worry about it.
	log = logging.InitFromFlags()
	safedoozer.SetLogger(log)

	log.Info("starting up - gid %s", gid)
	rand.Seed(int64(time.Now().Nanosecond())) // Not the best but ok?

	dzr = safedoozer.Dial(*dzrhost)
	defer dzr.Close()
	defer removeGlobalLocks()

	// see config.go for this
	initializeConfig()

	var err error // If we use := below, we shadow the global, which is bad.
	zmq_ctx, err = zmq.NewContext()
	if err != nil {
		log.Fatal("failed to init zmq: %s", err)
	}
	defer zmq_ctx.Close()

	myinfo, err := getSelfInfo()
	if err != nil {
		log.Fatal("Failed getting local information: %s", err)
	}
	if *myhost == "" {
		_, ok := myinfo["hostname"]
		if !ok {
			log.Fatal("getSelfInfo() did not return a hostname")
		}
		hostname = myinfo["hostname"]
	} else {
		log.Warn("user requested hostname override (command line argument)")
		hostname = *myhost
	}

	// Global, easy to access variable since we use it everywhere. This is safe
	// because it's read-only, too -- well, by definition. It isn't really. But
	// if it changes in maintainInfo then we exit.
	log.Info("client starting with hostname %s", hostname)

	// Now we have enough information to see if anybody else is claiming to be
	// this particular node. If so, we want to wait a bit and see if they
	// update again. If they are updating, then it is assumed they are running
	// okay, and we shouldn't start up again.
	lock := "/s/nlock/" + hostname
	rev := dzr.Stat(lock, nil)

	// If the lock is claimed, attempt to wait and see if the remote seems
	// to still be alive.
	if rev > 0 {
		log.Warn("node lock is claimed, waiting to see if it's lively")
		time.Sleep(3 * time.Second)

		nrev := dzr.Stat(lock, nil)
		if nrev > rev {
			log.Fatal("lock is lively, we can't continue!")
		}
	}

	// Safe to claim the node lock. Let's get it.
	log.Info("attempting to get the node lock")
	nrev := dzr.Set(lock, rev, fmt.Sprintf("%d", time.Now().Unix()))
	if nrev <= rev {
		log.Fatal("failed to obtain the lock")
	}
	log.Info("lock successfully obtained! we are lively.")
	go maintainLock(lock, nrev)

	// Now we want to reset the gid map, asserting that we are now the living
	// agent for this host.
	lock = "/s/gid/" + hostname
	rev = dzr.Stat(lock, nil)
	dzr.Set(lock, rev, gid)

	// There are certain miscellaneous tasks that need to get done somewhere,
	// so we use global locks to make sure that somebody is doing them. We
	// don't really care who.
	go manageGlobalFunc(5, "gf.expire-hosts", gfExpireHosts)
	go manageGlobalFunc(5, "gf.expire-glocks", gfExpireGlobalLocks)

	// Personal maintenance here.
	go maintainInfo(&myinfo)
	go maintainStanzas()
	runAgent(*myport) // Returns when dead.
}