2011-05-17 01:18:34 +00:00
|
|
|
{- git-annex command
|
|
|
|
-
|
|
|
|
- Copyright 2011 Joey Hess <joey@kitenet.net>
|
|
|
|
-
|
|
|
|
- Licensed under the GNU GPL version 3 or higher.
|
|
|
|
-}
|
|
|
|
|
2013-04-13 22:12:02 +00:00
|
|
|
{-# LANGUAGE BangPatterns #-}
|
2012-03-11 21:15:58 +00:00
|
|
|
|
2011-05-17 01:18:34 +00:00
|
|
|
module Command.Status where
|
|
|
|
|
2012-10-24 18:43:32 +00:00
|
|
|
import "mtl" Control.Monad.State.Strict
|
2011-05-17 01:18:34 +00:00
|
|
|
import qualified Data.Map as M
|
2011-11-20 18:12:48 +00:00
|
|
|
import Text.JSON
|
2012-10-03 21:04:52 +00:00
|
|
|
import Data.Tuple
|
2013-05-11 20:03:00 +00:00
|
|
|
import System.PosixCompat.Files
|
2011-05-17 01:18:34 +00:00
|
|
|
|
2011-10-05 20:02:51 +00:00
|
|
|
import Common.Annex
|
2011-06-02 01:56:04 +00:00
|
|
|
import qualified Types.Backend as B
|
|
|
|
import qualified Types.Remote as R
|
2011-05-17 01:18:34 +00:00
|
|
|
import qualified Remote
|
|
|
|
import qualified Command.Unused
|
2011-06-30 17:16:57 +00:00
|
|
|
import qualified Git
|
2011-11-14 23:27:00 +00:00
|
|
|
import qualified Annex
|
2011-05-17 01:18:34 +00:00
|
|
|
import Command
|
2011-07-06 00:36:43 +00:00
|
|
|
import Utility.DataUnits
|
2012-03-22 21:09:54 +00:00
|
|
|
import Utility.DiskFree
|
2011-10-04 04:40:47 +00:00
|
|
|
import Annex.Content
|
2011-06-02 01:56:04 +00:00
|
|
|
import Types.Key
|
2011-07-05 22:31:46 +00:00
|
|
|
import Backend
|
2011-10-15 20:21:08 +00:00
|
|
|
import Logs.UUID
|
2011-11-14 20:14:17 +00:00
|
|
|
import Logs.Trust
|
2011-09-30 07:20:24 +00:00
|
|
|
import Remote
|
2012-03-22 01:55:02 +00:00
|
|
|
import Config
|
2012-04-29 21:48:07 +00:00
|
|
|
import Utility.Percentage
|
2012-07-01 20:59:54 +00:00
|
|
|
import Logs.Transfer
|
2012-10-03 21:04:52 +00:00
|
|
|
import Types.TrustLevel
|
2013-03-11 05:22:56 +00:00
|
|
|
import qualified Limit
|
2011-05-17 01:18:34 +00:00
|
|
|
|
|
|
|
-- a named computation that produces a statistic
|
2011-09-20 22:57:05 +00:00
|
|
|
type Stat = StatState (Maybe (String, StatState String))
|
2011-05-17 01:18:34 +00:00
|
|
|
|
2012-03-11 21:15:58 +00:00
|
|
|
-- data about a set of keys
|
|
|
|
data KeyData = KeyData
|
|
|
|
{ countKeys :: Integer
|
|
|
|
, sizeKeys :: Integer
|
|
|
|
, unknownSizeKeys :: Integer
|
|
|
|
, backendsKeys :: M.Map String Integer
|
|
|
|
}
|
|
|
|
|
|
|
|
-- cached info that multiple Stats use
|
2011-05-17 01:18:34 +00:00
|
|
|
data StatInfo = StatInfo
|
2012-03-11 21:15:58 +00:00
|
|
|
{ presentData :: Maybe KeyData
|
|
|
|
, referencedData :: Maybe KeyData
|
2011-05-17 01:18:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
-- a state monad for running Stats in
|
|
|
|
type StatState = StateT StatInfo Annex
|
|
|
|
|
2011-10-29 19:19:05 +00:00
|
|
|
def :: [Command]
|
2013-04-21 00:23:11 +00:00
|
|
|
def = [command "status" paramPaths seek
|
2013-03-24 22:28:21 +00:00
|
|
|
SectionQuery "shows status information about the annex"]
|
2011-05-17 01:18:34 +00:00
|
|
|
|
|
|
|
seek :: [CommandSeek]
|
2013-03-11 05:22:56 +00:00
|
|
|
seek = [withWords start]
|
|
|
|
|
|
|
|
start :: [FilePath] -> CommandStart
|
|
|
|
start [] = do
|
|
|
|
globalStatus
|
|
|
|
stop
|
|
|
|
start ps = do
|
2013-03-11 05:37:26 +00:00
|
|
|
mapM_ localStatus =<< filterM isdir ps
|
2013-03-11 05:22:56 +00:00
|
|
|
stop
|
2013-03-11 05:37:26 +00:00
|
|
|
where
|
|
|
|
isdir = liftIO . catchBoolIO . (isDirectory <$$> getFileStatus)
|
2013-03-11 05:22:56 +00:00
|
|
|
|
|
|
|
globalStatus :: Annex ()
|
|
|
|
globalStatus = do
|
|
|
|
fast <- Annex.getState Annex.fast
|
|
|
|
let stats = if fast
|
|
|
|
then global_fast_stats
|
|
|
|
else global_fast_stats ++ global_slow_stats
|
|
|
|
showCustom "status" $ do
|
|
|
|
evalStateT (mapM_ showStat stats) (StatInfo Nothing Nothing)
|
|
|
|
return True
|
|
|
|
|
|
|
|
localStatus :: FilePath -> Annex ()
|
|
|
|
localStatus dir = showCustom (unwords ["status", dir]) $ do
|
|
|
|
let stats = map (\s -> s dir) local_stats
|
|
|
|
evalStateT (mapM_ showStat stats) =<< getLocalStatInfo dir
|
|
|
|
return True
|
2011-05-17 01:18:34 +00:00
|
|
|
|
|
|
|
{- Order is significant. Less expensive operations, and operations
|
|
|
|
- that share data go together.
|
|
|
|
-}
|
2013-03-11 05:22:56 +00:00
|
|
|
global_fast_stats :: [Stat]
|
|
|
|
global_fast_stats =
|
2011-05-17 01:18:34 +00:00
|
|
|
[ supported_backends
|
|
|
|
, supported_remote_types
|
2012-12-13 17:48:07 +00:00
|
|
|
, repository_mode
|
2012-10-03 21:04:52 +00:00
|
|
|
, remote_list Trusted
|
|
|
|
, remote_list SemiTrusted
|
|
|
|
, remote_list UnTrusted
|
|
|
|
, remote_list DeadTrusted
|
2012-07-01 20:59:54 +00:00
|
|
|
, transfer_list
|
2012-04-23 14:37:05 +00:00
|
|
|
, disk_size
|
2011-11-14 23:27:00 +00:00
|
|
|
]
|
2013-03-11 05:22:56 +00:00
|
|
|
global_slow_stats :: [Stat]
|
|
|
|
global_slow_stats =
|
2011-11-14 23:27:00 +00:00
|
|
|
[ tmp_size
|
2011-05-17 02:01:50 +00:00
|
|
|
, bad_data_size
|
2011-09-20 22:13:08 +00:00
|
|
|
, local_annex_keys
|
2011-05-17 02:19:15 +00:00
|
|
|
, local_annex_size
|
2012-02-06 18:19:44 +00:00
|
|
|
, known_annex_keys
|
|
|
|
, known_annex_size
|
2012-03-12 20:18:14 +00:00
|
|
|
, bloom_info
|
2011-05-17 01:18:34 +00:00
|
|
|
, backend_usage
|
|
|
|
]
|
2013-03-11 05:22:56 +00:00
|
|
|
local_stats :: [FilePath -> Stat]
|
|
|
|
local_stats =
|
|
|
|
[ local_dir
|
|
|
|
, const local_annex_keys
|
|
|
|
, const local_annex_size
|
|
|
|
, const known_annex_keys
|
|
|
|
, const known_annex_size
|
|
|
|
]
|
2011-05-17 01:18:34 +00:00
|
|
|
|
2011-11-20 18:12:48 +00:00
|
|
|
stat :: String -> (String -> StatState String) -> Stat
|
|
|
|
stat desc a = return $ Just (desc, a desc)
|
2011-05-17 01:18:34 +00:00
|
|
|
|
2011-05-17 02:01:50 +00:00
|
|
|
nostat :: Stat
|
2011-07-15 16:47:14 +00:00
|
|
|
nostat = return Nothing
|
2011-05-17 01:18:34 +00:00
|
|
|
|
2011-11-20 18:12:48 +00:00
|
|
|
json :: JSON j => (j -> String) -> StatState j -> String -> StatState String
|
|
|
|
json serialize a desc = do
|
|
|
|
j <- a
|
|
|
|
lift $ maybeShowJSON [(desc, j)]
|
|
|
|
return $ serialize j
|
|
|
|
|
|
|
|
nojson :: StatState String -> String -> StatState String
|
|
|
|
nojson a _ = a
|
|
|
|
|
2011-05-17 02:01:50 +00:00
|
|
|
showStat :: Stat -> StatState ()
|
2012-04-22 03:32:33 +00:00
|
|
|
showStat s = maybe noop calc =<< s
|
2012-11-12 05:05:04 +00:00
|
|
|
where
|
|
|
|
calc (desc, a) = do
|
|
|
|
(lift . showHeader) desc
|
|
|
|
lift . showRaw =<< a
|
2011-05-17 01:18:34 +00:00
|
|
|
|
|
|
|
supported_backends :: Stat
|
2011-11-20 18:12:48 +00:00
|
|
|
supported_backends = stat "supported backends" $ json unwords $
|
|
|
|
return $ map B.name Backend.list
|
2011-05-17 01:18:34 +00:00
|
|
|
|
|
|
|
supported_remote_types :: Stat
|
2011-11-20 18:12:48 +00:00
|
|
|
supported_remote_types = stat "supported remote types" $ json unwords $
|
|
|
|
return $ map R.typename Remote.remoteTypes
|
2011-05-17 01:18:34 +00:00
|
|
|
|
2012-12-13 17:48:07 +00:00
|
|
|
repository_mode :: Stat
|
|
|
|
repository_mode = stat "repository mode" $ json id $ lift $
|
|
|
|
ifM isDirect
|
|
|
|
( return "direct", return "indirect" )
|
|
|
|
|
2012-10-03 21:04:52 +00:00
|
|
|
remote_list :: TrustLevel -> Stat
|
|
|
|
remote_list level = stat n $ nojson $ lift $ do
|
2012-02-14 07:49:48 +00:00
|
|
|
us <- M.keys <$> (M.union <$> uuidMap <*> remoteMap Remote.name)
|
2011-11-15 04:33:54 +00:00
|
|
|
rs <- fst <$> trustPartition level us
|
|
|
|
s <- prettyPrintUUIDs n rs
|
2011-12-15 22:11:42 +00:00
|
|
|
return $ if null s then "0" else show (length rs) ++ "\n" ++ beginning s
|
2012-11-12 05:05:04 +00:00
|
|
|
where
|
|
|
|
n = showTrustLevel level ++ " repositories"
|
2012-03-12 20:18:14 +00:00
|
|
|
|
2013-03-11 05:22:56 +00:00
|
|
|
local_dir :: FilePath -> Stat
|
|
|
|
local_dir dir = stat "directory" $ json id $ return dir
|
|
|
|
|
2011-05-17 01:18:34 +00:00
|
|
|
local_annex_size :: Stat
|
2011-11-20 18:12:48 +00:00
|
|
|
local_annex_size = stat "local annex size" $ json id $
|
2012-03-11 21:15:58 +00:00
|
|
|
showSizeKeys <$> cachedPresentData
|
2011-05-17 01:18:34 +00:00
|
|
|
|
|
|
|
local_annex_keys :: Stat
|
2011-11-20 18:12:48 +00:00
|
|
|
local_annex_keys = stat "local annex keys" $ json show $
|
2012-03-11 21:15:58 +00:00
|
|
|
countKeys <$> cachedPresentData
|
2011-05-17 01:18:34 +00:00
|
|
|
|
2012-03-22 03:23:23 +00:00
|
|
|
known_annex_size :: Stat
|
|
|
|
known_annex_size = stat "known annex size" $ json id $
|
|
|
|
showSizeKeys <$> cachedReferencedData
|
|
|
|
|
|
|
|
known_annex_keys :: Stat
|
|
|
|
known_annex_keys = stat "known annex keys" $ json show $
|
|
|
|
countKeys <$> cachedReferencedData
|
|
|
|
|
|
|
|
tmp_size :: Stat
|
|
|
|
tmp_size = staleSize "temporary directory size" gitAnnexTmpDir
|
|
|
|
|
|
|
|
bad_data_size :: Stat
|
|
|
|
bad_data_size = staleSize "bad keys size" gitAnnexBadDir
|
|
|
|
|
2012-03-12 20:18:14 +00:00
|
|
|
bloom_info :: Stat
|
|
|
|
bloom_info = stat "bloom filter size" $ json id $ do
|
|
|
|
localkeys <- countKeys <$> cachedPresentData
|
|
|
|
capacity <- fromIntegral <$> lift Command.Unused.bloomCapacity
|
|
|
|
let note = aside $
|
|
|
|
if localkeys >= capacity
|
|
|
|
then "appears too small for this repository; adjust annex.bloomcapacity"
|
2012-04-29 21:48:07 +00:00
|
|
|
else showPercentage 1 (percentage capacity localkeys) ++ " full"
|
2012-03-12 20:18:14 +00:00
|
|
|
|
|
|
|
-- Two bloom filters are used at the same time, so double the size
|
|
|
|
-- of one.
|
2012-04-06 18:54:41 +00:00
|
|
|
size <- roughSize memoryUnits False . (* 2) . fromIntegral . fst <$>
|
2012-03-12 20:18:14 +00:00
|
|
|
lift Command.Unused.bloomBitsHashes
|
|
|
|
|
|
|
|
return $ size ++ note
|
|
|
|
|
2012-07-01 20:59:54 +00:00
|
|
|
transfer_list :: Stat
|
|
|
|
transfer_list = stat "transfers in progress" $ nojson $ lift $ do
|
|
|
|
uuidmap <- Remote.remoteMap id
|
|
|
|
ts <- getTransfers
|
|
|
|
if null ts
|
|
|
|
then return "none"
|
2012-10-02 17:45:30 +00:00
|
|
|
else return $ multiLine $
|
|
|
|
map (\(t, i) -> line uuidmap t i) $ sort ts
|
2012-11-12 05:05:04 +00:00
|
|
|
where
|
|
|
|
line uuidmap t i = unwords
|
|
|
|
[ showLcDirection (transferDirection t) ++ "ing"
|
|
|
|
, fromMaybe (key2file $ transferKey t) (associatedFile i)
|
|
|
|
, if transferDirection t == Upload then "to" else "from"
|
|
|
|
, maybe (fromUUID $ transferUUID t) Remote.name $
|
|
|
|
M.lookup (transferUUID t) uuidmap
|
|
|
|
]
|
2012-07-01 20:59:54 +00:00
|
|
|
|
2012-03-22 01:55:02 +00:00
|
|
|
disk_size :: Stat
|
2012-03-22 03:41:01 +00:00
|
|
|
disk_size = stat "available local disk space" $ json id $ lift $
|
2012-03-22 21:09:54 +00:00
|
|
|
calcfree
|
2013-01-01 17:52:47 +00:00
|
|
|
<$> (annexDiskReserve <$> Annex.getGitConfig)
|
2012-03-22 21:09:54 +00:00
|
|
|
<*> inRepo (getDiskFree . gitAnnexDir)
|
2012-11-12 05:05:04 +00:00
|
|
|
where
|
|
|
|
calcfree reserve (Just have) = unwords
|
|
|
|
[ roughSize storageUnits False $ nonneg $ have - reserve
|
|
|
|
, "(+" ++ roughSize storageUnits False reserve
|
|
|
|
, "reserved)"
|
|
|
|
]
|
|
|
|
calcfree _ _ = "unknown"
|
|
|
|
|
|
|
|
nonneg x
|
|
|
|
| x >= 0 = x
|
|
|
|
| otherwise = 0
|
2012-03-22 01:55:02 +00:00
|
|
|
|
2011-05-17 01:18:34 +00:00
|
|
|
backend_usage :: Stat
|
2011-11-20 18:12:48 +00:00
|
|
|
backend_usage = stat "backend usage" $ nojson $
|
2012-03-11 21:15:58 +00:00
|
|
|
calc
|
|
|
|
<$> (backendsKeys <$> cachedReferencedData)
|
|
|
|
<*> (backendsKeys <$> cachedPresentData)
|
2012-11-12 05:05:04 +00:00
|
|
|
where
|
|
|
|
calc x y = multiLine $
|
|
|
|
map (\(n, b) -> b ++ ": " ++ show n) $
|
|
|
|
reverse $ sort $ map swap $ M.toList $
|
|
|
|
M.unionWith (+) x y
|
2011-05-17 01:18:34 +00:00
|
|
|
|
2012-03-11 21:15:58 +00:00
|
|
|
cachedPresentData :: StatState KeyData
|
|
|
|
cachedPresentData = do
|
2011-05-17 01:18:34 +00:00
|
|
|
s <- get
|
2012-03-11 21:15:58 +00:00
|
|
|
case presentData s of
|
2011-05-17 01:18:34 +00:00
|
|
|
Just v -> return v
|
|
|
|
Nothing -> do
|
2012-03-11 21:15:58 +00:00
|
|
|
v <- foldKeys <$> lift getKeysPresent
|
|
|
|
put s { presentData = Just v }
|
|
|
|
return v
|
2011-05-17 01:18:34 +00:00
|
|
|
|
2012-03-11 21:15:58 +00:00
|
|
|
cachedReferencedData :: StatState KeyData
|
|
|
|
cachedReferencedData = do
|
2011-05-17 01:18:34 +00:00
|
|
|
s <- get
|
2012-03-11 21:15:58 +00:00
|
|
|
case referencedData s of
|
2011-05-17 01:18:34 +00:00
|
|
|
Just v -> return v
|
|
|
|
Nothing -> do
|
2012-03-11 21:15:58 +00:00
|
|
|
!v <- lift $ Command.Unused.withKeysReferenced
|
|
|
|
emptyKeyData addKey
|
|
|
|
put s { referencedData = Just v }
|
|
|
|
return v
|
|
|
|
|
2013-03-11 05:22:56 +00:00
|
|
|
getLocalStatInfo :: FilePath -> Annex StatInfo
|
|
|
|
getLocalStatInfo dir = do
|
|
|
|
matcher <- Limit.getMatcher
|
|
|
|
(presentdata, referenceddata) <-
|
|
|
|
Command.Unused.withKeysFilesReferencedIn dir initial
|
|
|
|
(update matcher)
|
|
|
|
return $ StatInfo (Just presentdata) (Just referenceddata)
|
|
|
|
where
|
|
|
|
initial = (emptyKeyData, emptyKeyData)
|
|
|
|
update matcher key file vs@(presentdata, referenceddata) =
|
|
|
|
ifM (matcher $ Annex.FileInfo file file)
|
|
|
|
( (,)
|
|
|
|
<$> ifM (inAnnex key)
|
|
|
|
( return $ addKey key presentdata
|
|
|
|
, return presentdata
|
|
|
|
)
|
|
|
|
<*> pure (addKey key referenceddata)
|
|
|
|
, return vs
|
|
|
|
)
|
|
|
|
|
2012-03-11 21:15:58 +00:00
|
|
|
emptyKeyData :: KeyData
|
|
|
|
emptyKeyData = KeyData 0 0 0 M.empty
|
2011-09-20 22:57:05 +00:00
|
|
|
|
2012-03-11 21:15:58 +00:00
|
|
|
foldKeys :: [Key] -> KeyData
|
|
|
|
foldKeys = foldl' (flip addKey) emptyKeyData
|
|
|
|
|
|
|
|
addKey :: Key -> KeyData -> KeyData
|
|
|
|
addKey key (KeyData count size unknownsize backends) =
|
|
|
|
KeyData count' size' unknownsize' backends'
|
2012-11-12 05:05:04 +00:00
|
|
|
where
|
|
|
|
{- All calculations strict to avoid thunks when repeatedly
|
|
|
|
- applied to many keys. -}
|
|
|
|
!count' = count + 1
|
|
|
|
!backends' = M.insertWith' (+) (keyBackendName key) 1 backends
|
|
|
|
!size' = maybe size (+ size) ks
|
|
|
|
!unknownsize' = maybe (unknownsize + 1) (const unknownsize) ks
|
|
|
|
ks = keySize key
|
2012-03-11 21:15:58 +00:00
|
|
|
|
|
|
|
showSizeKeys :: KeyData -> String
|
|
|
|
showSizeKeys d = total ++ missingnote
|
2012-11-12 05:05:04 +00:00
|
|
|
where
|
|
|
|
total = roughSize storageUnits False $ sizeKeys d
|
|
|
|
missingnote
|
|
|
|
| unknownSizeKeys d == 0 = ""
|
|
|
|
| otherwise = aside $
|
|
|
|
"+ " ++ show (unknownSizeKeys d) ++
|
|
|
|
" keys of unknown size"
|
2011-05-17 02:01:50 +00:00
|
|
|
|
|
|
|
staleSize :: String -> (Git.Repo -> FilePath) -> Stat
|
2012-03-12 04:41:48 +00:00
|
|
|
staleSize label dirspec = go =<< lift (Command.Unused.staleKeys dirspec)
|
2012-11-12 05:05:04 +00:00
|
|
|
where
|
|
|
|
go [] = nostat
|
|
|
|
go keys = onsize =<< sum <$> keysizes keys
|
|
|
|
onsize 0 = nostat
|
|
|
|
onsize size = stat label $
|
|
|
|
json (++ aside "clean up with git-annex unused") $
|
|
|
|
return $ roughSize storageUnits False size
|
|
|
|
keysizes keys = map (fromIntegral . fileSize) <$> stats keys
|
|
|
|
stats keys = do
|
|
|
|
dir <- lift $ fromRepo dirspec
|
|
|
|
liftIO $ forM keys $ \k -> getFileStatus (dir </> keyFile k)
|
2011-05-17 02:49:41 +00:00
|
|
|
|
|
|
|
aside :: String -> String
|
2011-09-30 07:05:10 +00:00
|
|
|
aside s = " (" ++ s ++ ")"
|
2012-10-02 17:45:30 +00:00
|
|
|
|
|
|
|
multiLine :: [String] -> String
|
|
|
|
multiLine = concatMap (\l -> "\n\t" ++ l)
|