File: Fsck.hs

package info (click to toggle)
git-annex 5.20141125
  • links: PTS
  • area: main
  • in suites: jessie, jessie-kfreebsd
  • size: 37,828 kB
  • ctags: 583
  • sloc: haskell: 42,582; sh: 1,080; ansic: 498; makefile: 316; perl: 125
file content (196 lines) | stat: -rw-r--r-- 7,171 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
{- git-annex assistant fsck configuration
 -
 - Copyright 2013 Joey Hess <joey@kitenet.net>
 -
 - Licensed under the GNU AGPL version 3 or higher.
 -}

{-# LANGUAGE QuasiQuotes, TemplateHaskell, OverloadedStrings #-}

module Assistant.WebApp.Configurators.Fsck where

import qualified Data.Map as M
import qualified Data.Set as S
import qualified Data.Text as T

import Assistant.WebApp.Common
import Types.ScheduledActivity
import Utility.HumanTime
import Utility.Scheduled
import Logs.Schedule
import Annex.UUID
import qualified Remote
import Assistant.DaemonStatus
import qualified Annex.Branch
import Assistant.Fsck
import Config
import Git.Config
import qualified Annex

{- This adds a form to the page. It does not handle posting of the form,
 - because unlike a typical yesod form that posts using the same url
 - that generated it, this form posts using one of two other routes. -}
showFsckForm :: Bool -> ScheduledActivity -> Widget
showFsckForm new activity = do
	u <- liftAnnex getUUID
	let action = if new
		then AddActivityR u
		else ChangeActivityR u activity
	((res, form), enctype) <- liftH $ runFsckForm new activity
	case res of
		FormSuccess _ -> noop
		_ -> $(widgetFile "configurators/fsck/form")

{- This does not display a form, but it does get it from a post, and run
 - some Annex action on it. -}
withFsckForm :: (ScheduledActivity -> Annex ()) -> Handler ()
withFsckForm a = do
	((res, _form), _enctype) <- runFsckForm False $ defaultFsck Nothing
	case res of
		FormSuccess activity -> liftAnnex $ a activity
		_ -> noop

mkFsck :: UUID -> UUID -> Schedule -> Duration -> ScheduledActivity
mkFsck hereu u s d
	| u == hereu = ScheduledSelfFsck s d 
	| otherwise = ScheduledRemoteFsck u s d

runFsckForm :: Bool -> ScheduledActivity -> Handler ((FormResult ScheduledActivity, Widget), Enctype)
runFsckForm new activity = case activity of
	ScheduledSelfFsck s d -> go s d =<< liftAnnex getUUID
	ScheduledRemoteFsck ru s d -> go s d ru
  where
	go (Schedule r t) d ru = do
		u <- liftAnnex getUUID
		repolist <- liftAssistant (getrepolist ru)
		runFormPostNoToken $ \msg -> do
			(reposRes, reposView) <- mreq (selectFieldList repolist) (bfs "") (Just ru)
			(durationRes, durationView) <- mreq intField (bfs "") (Just $ durationSeconds d `quot` 60 )
			(timeRes, timeView) <- mreq (selectFieldList times) (bfs "") (Just t)
			(recurranceRes, recurranceView) <- mreq (selectFieldList recurrances) (bfs "") (Just r)
			let form = do
				webAppFormAuthToken
				$(widgetFile "configurators/fsck/formcontent")
			let formresult = mkFsck
				<$> pure u
				<*> reposRes
				<*> (Schedule <$> recurranceRes <*> timeRes)
				<*> (Duration <$> ((60 *) <$> durationRes))
			return (formresult, form)
	  where
		times :: [(Text, ScheduledTime)]
		times = ensurevalue t (T.pack $ fromScheduledTime t) $
			map (\x -> (T.pack $ fromScheduledTime x, x)) $
				AnyTime : map (\h -> SpecificTime h 0) [0..23]
		recurrances :: [(Text, Recurrance)]
		recurrances = ensurevalue r (T.pack $ fromRecurrance r) $
			[ ("every day", Daily)
			, ("every Sunday", Weekly $ Just 1)
			, ("every Monday", Weekly $ Just 2)
			, ("every Tuesday", Weekly $ Just 3)
			, ("every Wednesday", Weekly $ Just 4)
			, ("every Thursday", Weekly $ Just 5)
			, ("every Friday", Weekly $ Just 6)
			, ("every Saturday", Weekly $ Just 7)
			, ("monthly", Monthly Nothing)
			, ("twice a month", Divisible 2 (Weekly Nothing))
			, ("yearly", Yearly Nothing)
			, ("twice a year", Divisible 6 (Monthly Nothing))
			, ("quarterly", Divisible 4 (Monthly Nothing))
			]
	ensurevalue v desc l = case M.lookup v (M.fromList $ map (\(x,y) -> (y,x)) l) of
		Just _ -> l
		Nothing -> (desc, v) : l
	getrepolist :: UUID -> Assistant [(Text, UUID)]
	getrepolist ensureu = do
		-- It is possible to have fsck jobs for remotes that
		-- do not implement remoteFsck, but it's not too useful,
		-- so omit them from the UI normally.
		remotes <- filter (\r -> Remote.uuid r == ensureu || isJust (Remote.remoteFsck r)) . syncRemotes
			<$> getDaemonStatus
		u <- liftAnnex getUUID
		let us = u : (map Remote.uuid remotes)
		liftAnnex $ 
			zip <$> (map T.pack <$> Remote.prettyListUUIDs us) <*> pure us

defaultFsck :: Maybe Remote -> ScheduledActivity
defaultFsck Nothing = ScheduledSelfFsck (Schedule Daily AnyTime) (Duration $ 60*60)
defaultFsck (Just r) = ScheduledRemoteFsck (Remote.uuid r) (Schedule Daily AnyTime) (Duration $ 60*60)

showFsckStatus :: ScheduledActivity -> Widget
showFsckStatus activity = do
	m <- liftAnnex getLastRunTimes
	let lastrun = M.lookup activity m
	$(widgetFile "configurators/fsck/status")

getConfigFsckR :: Handler Html
getConfigFsckR = postConfigFsckR
postConfigFsckR :: Handler Html
postConfigFsckR = page "Consistency checks" (Just Configuration) $ do
	scheduledchecks <- liftAnnex $
		S.toList <$> (scheduleGet =<< getUUID)
	rs <- liftAssistant $
		filter fsckableRemote . syncRemotes <$> getDaemonStatus
	recommendedchecks <- liftAnnex $ map defaultFsck
		<$> filterM (not <$$> checkFscked) (Nothing : map Just rs)
	$(widgetFile "configurators/fsck")

changeSchedule :: Handler () -> Handler Html
changeSchedule a = do
	a
	liftAnnex $ Annex.Branch.commit "update"
	redirect ConfigFsckR

getRemoveActivityR :: UUID -> ScheduledActivity -> Handler Html
getRemoveActivityR u activity = changeSchedule $
	liftAnnex $ scheduleRemove u activity

getAddActivityR :: UUID -> Handler Html
getAddActivityR = postAddActivityR
postAddActivityR :: UUID -> Handler Html
postAddActivityR u = changeSchedule $
	withFsckForm $ scheduleAdd u

getChangeActivityR :: UUID -> ScheduledActivity -> Handler Html
getChangeActivityR = postChangeActivityR
postChangeActivityR :: UUID -> ScheduledActivity -> Handler Html
postChangeActivityR u oldactivity = changeSchedule $
	withFsckForm $ \newactivity -> scheduleChange u $
			S.insert newactivity . S.delete oldactivity

data FsckPreferences = FsckPreferences
	{ enableFsckNudge :: Bool
	}

getFsckPreferences :: Annex FsckPreferences
getFsckPreferences = FsckPreferences
	<$> (annexFsckNudge <$> Annex.getGitConfig)

fsckPreferencesAForm :: FsckPreferences -> MkAForm FsckPreferences
fsckPreferencesAForm def = FsckPreferences
	<$> areq (checkBoxField `withNote` nudgenote) "Reminders" (Just $ enableFsckNudge def)
  where
	nudgenote = [whamlet|Remind me when using repositories that lack consistency checks.|]

runFsckPreferencesForm :: Handler ((FormResult FsckPreferences, Widget), Enctype)
runFsckPreferencesForm = do
	prefs <- liftAnnex getFsckPreferences
	runFormPostNoToken $ renderBootstrap3 formLayout $ fsckPreferencesAForm prefs
  where formLayout = BootstrapHorizontalForm (ColSm 0) (ColSm 2) (ColSm 0) (ColSm 10)

showFsckPreferencesForm :: Widget
showFsckPreferencesForm = do
	((res, form), enctype) <- liftH $ runFsckPreferencesForm
	case res of
		FormSuccess _ -> noop
		_ -> $(widgetFile "configurators/fsck/preferencesform")

postConfigFsckPreferencesR :: Handler Html
postConfigFsckPreferencesR = do
	((res, _form), _enctype) <- runFsckPreferencesForm
	case res of
		FormSuccess prefs ->
			liftAnnex $ setConfig (annexConfig "fscknudge")
				(boolConfig $ enableFsckNudge prefs)
		_ -> noop
	redirect ConfigFsckR