9
"github.com/gorilla/mux"
10
_ "github.com/mattn/go-sqlite3"
12
"github.com/lxc/lxd/shared"
14
log "gopkg.in/inconshreveable/log15.v2"
17
/* This is used for both profiles post and profile put */
18
type profilesPostReq struct {
19
Name string `json:"name"`
20
Config map[string]string `json:"config"`
21
Description string `json:"description"`
22
Devices shared.Devices `json:"devices"`
25
func profilesGet(d *Daemon, r *http.Request) Response {
26
results, err := dbProfiles(d.db)
28
return SmartError(err)
31
recursion := d.isRecursionRequest(r)
33
resultString := make([]string, len(results))
34
resultMap := make([]*shared.ProfileConfig, len(results))
36
for _, name := range results {
38
url := fmt.Sprintf("/%s/profiles/%s", shared.APIVersion, name)
41
profile, err := doProfileGet(d, name)
43
shared.Log.Error("Failed to get profile", log.Ctx{"profile": name})
46
resultMap[i] = profile
52
return SyncResponse(true, resultString)
55
return SyncResponse(true, resultMap)
58
func profilesPost(d *Daemon, r *http.Request) Response {
59
req := profilesPostReq{}
60
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
61
return BadRequest(err)
66
return BadRequest(fmt.Errorf("No name provided"))
69
err := containerValidConfig(req.Config, true, false)
71
return BadRequest(err)
74
err = containerValidDevices(req.Devices, true, false)
76
return BadRequest(err)
80
_, err = dbProfileCreate(d.db, req.Name, req.Description, req.Config, req.Devices)
83
fmt.Errorf("Error inserting %s into database: %s", req.Name, err))
86
return EmptySyncResponse
89
var profilesCmd = Command{
94
func doProfileGet(d *Daemon, name string) (*shared.ProfileConfig, error) {
95
_, profile, err := dbProfileGet(d.db, name)
99
func profileGet(d *Daemon, r *http.Request) Response {
100
name := mux.Vars(r)["name"]
102
resp, err := doProfileGet(d, name)
104
return SmartError(err)
107
return SyncResponse(true, resp)
110
func getRunningContainersWithProfile(d *Daemon, profile string) []container {
111
results := []container{}
113
output, err := dbProfileContainersGet(d.db, profile)
118
for _, name := range output {
119
c, err := containerLoadByName(d, name)
121
shared.Log.Error("Failed opening container", log.Ctx{"container": name})
124
results = append(results, c)
129
func profilePut(d *Daemon, r *http.Request) Response {
130
name := mux.Vars(r)["name"]
132
req := profilesPostReq{}
133
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
134
return BadRequest(err)
138
err := containerValidConfig(req.Config, true, false)
140
return BadRequest(err)
143
err = containerValidDevices(req.Devices, true, false)
145
return BadRequest(err)
148
// Get the running container list
149
clist := getRunningContainersWithProfile(d, name)
150
var containers []container
151
for _, c := range clist {
156
containers = append(containers, c)
159
// Update the database
160
id, profile, err := dbProfileGet(d.db, name)
162
return InternalError(fmt.Errorf("Failed to retrieve profile='%s'", name))
165
tx, err := dbBegin(d.db)
167
return InternalError(err)
170
if profile.Description != req.Description {
171
err = dbProfileDescriptionUpdate(tx, id, req.Description)
174
return InternalError(err)
178
// Optimize for description-only changes
179
if reflect.DeepEqual(profile.Config, req.Config) && reflect.DeepEqual(profile.Devices, req.Devices) {
182
return InternalError(err)
185
return EmptySyncResponse
188
err = dbProfileConfigClear(tx, id)
191
return InternalError(err)
194
err = dbProfileConfigAdd(tx, id, req.Config)
197
return SmartError(err)
200
err = dbDevicesAdd(tx, "profile", id, req.Devices)
203
return SmartError(err)
208
return InternalError(err)
211
// Update all the containers using the profile. Must be done after txCommit due to DB lock.
212
failures := map[string]error{}
213
for _, c := range containers {
214
err = c.Update(containerArgs{
215
Architecture: c.Architecture(),
216
Ephemeral: c.IsEphemeral(),
217
Config: c.LocalConfig(),
218
Devices: c.LocalDevices(),
219
Profiles: c.Profiles()}, true)
222
failures[c.Name()] = err
226
if len(failures) != 0 {
227
msg := "The following containers failed to update (profile change still saved):\n"
228
for cname, err := range failures {
229
msg += fmt.Sprintf(" - %s: %s\n", cname, err)
231
return InternalError(fmt.Errorf("%s", msg))
234
return EmptySyncResponse
237
// The handler for the post operation.
238
func profilePost(d *Daemon, r *http.Request) Response {
239
name := mux.Vars(r)["name"]
241
req := profilesPostReq{}
242
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
243
return BadRequest(err)
248
return BadRequest(fmt.Errorf("No name provided"))
251
err := dbProfileUpdate(d.db, name, req.Name)
253
return InternalError(err)
256
return EmptySyncResponse
259
// The handler for the delete operation.
260
func profileDelete(d *Daemon, r *http.Request) Response {
261
name := mux.Vars(r)["name"]
262
err := dbProfileDelete(d.db, name)
265
return InternalError(err)
268
return EmptySyncResponse
271
var profileCmd = Command{name: "profiles/{name}", get: profileGet, put: profilePut, delete: profileDelete, post: profilePost}