Exemple #1
0
func main() {
	addr := flag.String("addr", "", "host:port of slime-proxy to check")
	uuidFlag := flag.String("uuid", "", "uuid of metadata store to verify")
	warnUnconnected := flag.Int("warn", 0,
		"WARN if number of undead unconnected stores exceeds this number")
	critUnconnected := flag.Int("crit", 0,
		"CRIT if number of undead unconnected stores exceeds this number")
	flag.Parse()

	if *addr == "" ||
		*uuidFlag == "" ||
		*warnUnconnected == 0 ||
		*critUnconnected == 0 {

		dieUnknown("Incorrect invocation, all arguments are required.\n" +
			"Run with -h for usage.")
	}

	wantID, err := uuid.Parse(*uuidFlag)
	if err != nil {
		dieUnknown("Couldn't parse uuid given: %v", err)
	}

	checkUUID(*addr, wantID)
	checkConnectivity(*addr, *warnUnconnected, *critUnconnected)
	os.Exit(0)
}
Exemple #2
0
func checkUUID(addr string, wantID [16]byte) {
	resp, err := http.Get(urlJoin(addr, "/data/?mode=uuid"))
	if err != nil {
		dieUnknown("Couldn't get uuid: %v", err)
	}

	if resp.StatusCode < 200 || resp.StatusCode >= 300 {
		dieUnknown("Got response code %v from uuid request",
			resp.StatusCode)
	}

	data, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		dieUnknown("Couldn't read response for uuid: %v", err)
	}

	resp.Body.Close()

	id, err := uuid.Parse(string(data))
	if err != nil {
		dieUnknown("Couldn't parse uuid from server: %v", err)
	}

	if id != wantID {
		fmt.Printf("wanted id %v, got id %v\n",
			uuid.Fmt(wantID), uuid.Fmt(id))
		os.Exit(2)
	}
}
Exemple #3
0
func prefixIDFromLocalKey(key string) ([16]byte, error) {
	parts := strings.SplitN(key, "_", 2)
	if len(parts) != 2 {
		return [16]byte{}, errors.New("not enough key components")
	}
	return uuid.Parse(parts[0])
}
Exemple #4
0
func handleStoreStoreOperation(operation string, target string) error {
	_, err := uuid.Parse(target)
	if err != nil {
		target, err = resolveStoreUUID(target)
		if err != nil {
			return err
		}
	}

	var list []storeResponse
	return jsonPost(conf.Base+"stores", map[string]string{
		"operation": operation,
		"uuid":      target,
	}, &list)
}
Exemple #5
0
func (ds *Directory) Available() bool {
	ds.mu.RLock()
	defer ds.mu.RUnlock()

	data, err := ioutil.ReadFile(filepath.Join(ds.Dir, "uuid"))
	if err != nil {
		return false
	}

	thatUUID, err := uuid.Parse(string(data))
	if err != nil {
		return false
	}

	return thatUUID == ds.uuid
}
Exemple #6
0
func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	switch r.URL.Path {
	case "/uuids":
		h.serveUUIDs(w, r)
	case "/":
		h.serveRoot(w, r)
	default:
		parts := strings.SplitN(r.URL.Path, "/", 3)
		if len(parts) != 3 {
			http.Error(w, "bad url", http.StatusBadRequest)
			return
		}

		uuidStr := parts[1]
		subObject := parts[2]

		uuid, err := uuid.Parse(uuidStr)
		if err != nil {
			http.Error(w, "bad uuid", http.StatusBadRequest)
			return
		}

		var subHandler *storehttp.Server
		for i, st := range h.stores {
			if st.UUID() == uuid {
				subHandler = h.handlers[i]
				break
			}
		}

		if subHandler == nil {
			http.Error(w, "no such uuid", http.StatusNotFound)
			return
		}

		r.URL.Path = "/" + subObject
		subHandler.ServeHTTP(w, r)
	}
}
Exemple #7
0
func openDirectoryImpl(dir string, perFileWait, perByteWait time.Duration, disableBackgroundLoops bool) (*Directory, error) {
	data, err := ioutil.ReadFile(filepath.Join(dir, "uuid"))
	if err != nil {
		return nil, err
	}

	myUUID, err := uuid.Parse(string(data))
	if err != nil {
		return nil, err
	}

	host, _ := os.Hostname()

	ds := &Directory{
		Dir:          dir,
		uuid:         myUUID,
		name:         host + ":" + dir,
		perFileWait:  perFileWait,
		perByteWait:  perByteWait,
		minSplitSize: 500,
		maxSplitSize: 2000,
	}

	err = ds.loadSplitsAndRecover()
	if err != nil {
		return nil, err
	}

	ds.tomb.Go(func() error {
		if !disableBackgroundLoops {
			ds.tomb.Go(ds.hashcheckLoop)
			ds.tomb.Go(ds.resplitLoop)
		}
		return nil
	})

	return ds, nil
}
Exemple #8
0
func (cc *Client) loadUUID() error {
	resp, err := cc.startReq("GET", cc.url+"?mode=uuid", nil, nil, nil)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	if resp.StatusCode < 200 || resp.StatusCode >= 300 {
		return httputil.ReadResponseAsError(resp)
	}

	// 37 is the length of a formatted UUID plus one (to avoid false positives)
	data, err := ioutil.ReadAll(io.LimitReader(resp.Body, 37))
	if err != nil {
		return err
	}

	cc.uuid, err = uuid.Parse(string(data))
	if err != nil {
		return err
	}

	return nil
}
Exemple #9
0
func (h *Handler) serveStores(w http.ResponseWriter, r *http.Request) {
	if r.Method == "POST" {
		var req storesRequest
		err := json.NewDecoder(r.Body).Decode(&req)
		if err != nil {
			httputil.RespondJSONError(w, err.Error(), http.StatusBadRequest)
			return
		}

		switch req.Operation {
		case "rescan":
			err = h.finder.Rescan()
			if err != nil {
				httputil.RespondJSONError(w, fmt.Sprintf("Couldn't rescan: %v", err),
					http.StatusInternalServerError)
				return
			}

		case "scan":
			err = h.finder.Scan(req.URL)
			if err != nil {
				httputil.RespondJSONError(w, fmt.Sprintf("Couldn't scan %v: %v", req.URL, err),
					http.StatusBadRequest)
				return
			}

		case "dead", "undead":
			id, err := uuid.Parse(req.UUID)
			if err != nil {
				httputil.RespondJSONError(w, "Couldn't parse UUID", http.StatusBadRequest)
				return
			}

			err = h.db.RunTx(func(ctx kvl.Ctx) error {
				layer, err := meta.Open(ctx)
				if err != nil {
					return err
				}

				loc, err := layer.GetLocation(id)
				if err != nil {
					return err
				}

				if loc == nil {
					return kvl.ErrNotFound
				}

				loc.Dead = req.Operation == "dead"

				return layer.SetLocation(*loc)
			})
			if err != nil {
				if err == kvl.ErrNotFound {
					httputil.RespondJSONError(w, "No store with that UUID",
						http.StatusBadRequest)
					return
				}
				httputil.RespondJSONError(w, err.Error(), http.StatusInternalServerError)
				return
			}

		case "delete":
			id, err := uuid.Parse(req.UUID)
			if err != nil {
				httputil.RespondJSONError(w, "Couldn't parse UUID", http.StatusBadRequest)
				return
			}

			st := h.finder.StoreFor(id)
			if st != nil {
				httputil.RespondJSONError(w, "UUID currently connected", http.StatusBadRequest)
				return
			}

			err = h.db.RunTx(func(ctx kvl.Ctx) error {
				layer, err := meta.Open(ctx)
				if err != nil {
					return err
				}

				loc, err := layer.GetLocation(id)
				if err != nil {
					return err
				}

				if loc == nil {
					return kvl.ErrNotFound
				}

				return layer.DeleteLocation(*loc)
			})
			if err != nil {
				if err == kvl.ErrNotFound {
					httputil.RespondJSONError(w, "No store with that UUID",
						http.StatusBadRequest)
					return
				}
				httputil.RespondJSONError(w, err.Error(), http.StatusInternalServerError)
				return
			}

		default:
			httputil.RespondJSONError(w, "unsupported operation", http.StatusBadRequest)
			return
		}
	} else if r.Method != "GET" {
		w.Header().Set("Allow", "GET, POST")
		httputil.RespondJSONError(w, "bad method", http.StatusMethodNotAllowed)
		return
	}

	finderEntries := h.finder.Stores()

	ret := make([]storesResponseEntry, 0, 10)
	err := h.db.RunReadTx(func(ctx kvl.Ctx) error {
		ret = ret[:0]

		layer, err := meta.Open(ctx)
		if err != nil {
			return err
		}

		locs, err := layer.AllLocations()
		if err != nil {
			return err
		}

		for _, loc := range locs {
			fe, connected := finderEntries[loc.UUID]

			ret = append(ret, storesResponseEntry{
				UUID:      uuid.Fmt(loc.UUID),
				URL:       loc.URL,
				Name:      loc.Name,
				Dead:      loc.Dead,
				Connected: connected,
				LastSeen:  time.Unix(loc.LastSeen, 0).UTC(),
				Free:      fe.Free,
			})
		}

		return nil
	})
	if err != nil {
		httputil.RespondJSONError(w, err.Error(), http.StatusInternalServerError)
		return
	}

	w.Header().Set("content-type", "application/json; charset=utf-8")
	w.WriteHeader(http.StatusOK)
	json.NewEncoder(w).Encode(ret)
}
func (m *Multi) scrubLocationsStep() (bool, error) {
	uuidCmp := func(a, b [16]byte) int {
		for i := 0; i < 16; i++ {
			if a[i] < b[i] {
				return -1
			} else if a[i] > b[i] {
				return 1
			}
		}
		return 0
	}

	var thisLoc meta.Location
	var haveLoc bool
	var from string
	var wantFiles []string
	err := m.db.RunTx(func(ctx kvl.Ctx) error {
		from = ""
		wantFiles = nil
		haveLoc = false

		layer, err := meta.Open(ctx)
		if err != nil {
			return err
		}

		pos, err := layer.GetConfig("scrublocationpos")
		if err != nil {
			return err
		}

		// parse errors okay, since will be the zero UUID
		since, _ := uuid.Parse(string(pos))

		allLocations, err := layer.AllLocations()
		if err != nil {
			return err
		}

		if len(allLocations) == 0 {
			// no locations exist
			return nil
		}

		for {
			// get the Location with the lowest UUID greater than since
			haveLoc = false
			for _, loc := range allLocations {
				if uuidCmp(loc.UUID, since) >= 0 && (!haveLoc || uuidCmp(thisLoc.UUID, loc.UUID) > 0) {
					thisLoc = loc
					haveLoc = true
				}
			}

			if haveLoc {
				break
			}

			// all locations handled already, try again from the top
			if since == [16]byte{} {
				panic("locations exist but couldn't find one")
			}
			since = [16]byte{}
		}

		// set since to the next highest UUID
		since = thisLoc.UUID
		for i := 15; i >= 0; i-- {
			since[i]++
			if since[i] != 0 {
				break
			}
		}

		err = layer.SetConfig("scrublocationpos", []byte(uuid.Fmt(since)))
		if err != nil {
			return err
		}

		// now get the file list in the location we've chosen, resuming at the
		// saved position (per location)

		fromB, err := layer.GetConfig(
			"scrublocationpos-" + uuid.Fmt(thisLoc.UUID))
		if err != nil {
			return err
		}
		from = string(fromB)

		files, err := layer.GetLocationContents(thisLoc.UUID, from,
			scrubLocationsCount)
		if err != nil {
			return err
		}

		var newLocPos string
		if len(files) > 0 {
			newLocPos = files[len(files)-1]
		} else {
			newLocPos = ""
		}
		err = layer.SetConfig(
			"scrublocationpos-"+uuid.Fmt(thisLoc.UUID), []byte(newLocPos))
		if err != nil {
			return err
		}

		wantFiles = files

		return nil
	})
	if err != nil {
		return false, err
	}

	if !haveLoc {
		// no chunk servers
		return true, nil
	}

	wantFilesMap := make(map[string]struct{}, len(wantFiles))
	for _, f := range wantFiles {
		wantFilesMap[f] = struct{}{}
	}

	st := m.finder.StoreFor(thisLoc.UUID)
	if st == nil {
		log.Printf("Couldn't scrubLocation %v, it is offline",
			uuid.Fmt(thisLoc.UUID))
		return false, nil
	}

	haveFiles, err := st.List(from, scrubLocationsCount, nil)
	if err != nil {
		log.Printf("Couldn't List from %v: %v", uuid.Fmt(thisLoc.UUID), err)
		return false, nil
	}

	haveFilesMap := make(map[string]struct{}, len(haveFiles))
	for _, f := range haveFiles {
		haveFilesMap[f] = struct{}{}
	}

	checkHaveFiles := make([]string, 0, len(haveFiles))
	for _, f := range haveFiles {
		if len(wantFiles) < scrubLocationsCount || f <= wantFiles[len(wantFiles)-1] {
			checkHaveFiles = append(checkHaveFiles, f)
		}
	}

	checkWantFiles := make([]string, 0, len(wantFiles))
	for _, f := range wantFiles {
		if len(haveFiles) < scrubLocationsCount || f <= haveFiles[len(haveFiles)-1] {
			checkWantFiles = append(checkWantFiles, f)
		}
	}

	for _, have := range checkHaveFiles {
		if _, ok := wantFilesMap[have]; !ok {
			pid, err := prefixIDFromLocalKey(have)
			if err != nil {
				log.Printf("Couldn't figure out PrefixID from localKey(%#v): %v",
					have, err)

				err = st.CAS(have, store.AnyV, store.MissingV, nil)
				if err != nil {
					log.Printf("Couldn't delete extraneous chunk %v from %v: %v",
						have, uuid.Fmt(thisLoc.UUID), err)
					continue
				}

				continue
			}

			var inWAL bool
			err = m.db.RunTx(func(ctx kvl.Ctx) error {
				layer, err := meta.Open(ctx)
				if err != nil {
					return err
				}

				inWAL, err = layer.WALCheck(pid)
				if err != nil {
					return err
				}

				if !inWAL {
					// check to see if the write finished
					inWAL, err = layer.LocationShouldHave(thisLoc.UUID, have)
					if err != nil {
						return err
					}
				}

				return nil
			})
			if err != nil {
				log.Printf("Couldn't check WAL for PrefixID %v: %v",
					uuid.Fmt(pid), err)
				continue
			}

			if inWAL {
				log.Printf("extraneous chunk %v on %v, but is in WAL, skipping",
					have, uuid.Fmt(thisLoc.UUID))
				continue
			}

			log.Printf("deleting extraneous chunk %v on %v",
				have, uuid.Fmt(thisLoc.UUID))
			err = st.CAS(have, store.AnyV, store.MissingV, nil)
			if err != nil {
				log.Printf("Couldn't delete extraneous chunk %v from %v: %v",
					have, uuid.Fmt(thisLoc.UUID), err)
				continue
			}
		}
	}

	for _, want := range checkWantFiles {
		if _, ok := haveFilesMap[want]; !ok {
			log.Printf("missing chunk %v on %v", want, uuid.Fmt(thisLoc.UUID))

			pid, err := prefixIDFromLocalKey(want)
			if err != nil {
				log.Printf("Couldn't figure out PrefixID from localKey(%#v): %v",
					want, err)
				continue
			}

			var inWAL bool
			var path string
			err = m.db.RunTx(func(ctx kvl.Ctx) error {
				path = ""

				layer, err := meta.Open(ctx)
				if err != nil {
					return err
				}

				inWAL, err = layer.WALCheck(pid)
				if err != nil {
					return err
				}

				if !inWAL {
					path, err = layer.PathForPrefixID(pid)
					if err != nil {
						return err
					}
				}

				return nil
			})
			if err != nil {
				log.Printf("Couldn't get path for PrefixID %v: %v",
					uuid.Fmt(pid), err)
				continue
			}

			if inWAL {
				log.Printf("skipping rebuild of %v, prefix in WAL", path)
				continue
			}

			err = m.rebuild(path)
			if err != nil {
				log.Printf("Couldn't rebuild %v: %v", path, err)
				continue
			}

			log.Printf("successfully rebuilt %v", path)
		}
	}

	if thisLoc.Dead && len(wantFiles) > 0 {
		// dead stores should be cleared

		for _, want := range wantFiles {
			pid, err := prefixIDFromLocalKey(want)
			if err != nil {
				// already logged in a loop above
				continue
			}

			var inWAL bool
			var path string
			err = m.db.RunTx(func(ctx kvl.Ctx) error {
				path = ""

				layer, err := meta.Open(ctx)
				if err != nil {
					return err
				}

				inWAL, err = layer.WALCheck(pid)
				if err != nil {
					return err
				}

				if !inWAL {
					path, err = layer.PathForPrefixID(pid)
					if err != nil {
						return err
					}
				}

				return nil
			})
			if err != nil {
				log.Printf("Couldn't check wal/path for PrefixID %v: %v",
					uuid.Fmt(pid), err)
				continue
			}

			if inWAL {
				log.Printf("skipping rebuild of PrefixID %v on dead store, prefix in WAL", uuid.Fmt(pid))
				continue
			}

			err = m.rebuild(path)
			if err != nil {
				log.Printf("Couldn't rebuild %v: %v", path, err)
				continue
			}

			log.Printf("successfully rebuilt %v", path)
		}
	}

	return len(wantFiles) == 0, nil
}