// changeInterfaces controls the interfaces system. // Plugs can be connected to and disconnected from slots. // When enableInternalInterfaceActions is true plugs and slots can also be // explicitly added and removed. func changeInterfaces(c *Command, r *http.Request) Response { var a interfaceAction decoder := json.NewDecoder(r.Body) if err := decoder.Decode(&a); err != nil { return BadRequest("cannot decode request body into an interface action: %v", err) } if a.Action == "" { return BadRequest("interface action not specified") } if !c.d.enableInternalInterfaceActions && a.Action != "connect" && a.Action != "disconnect" { return BadRequest("internal interface actions are disabled") } if len(a.Plugs) > 1 || len(a.Slots) > 1 { return NotImplemented("many-to-many operations are not implemented") } if a.Action != "connect" && a.Action != "disconnect" { return BadRequest("unsupported interface action: %q", a.Action) } if len(a.Plugs) == 0 || len(a.Slots) == 0 { return BadRequest("at least one plug and slot is required") } var change *state.Change var taskset *state.TaskSet var err error state := c.d.overlord.State() state.Lock() defer state.Unlock() switch a.Action { case "connect": summary := fmt.Sprintf("Connect %s:%s to %s:%s", a.Plugs[0].Snap, a.Plugs[0].Name, a.Slots[0].Snap, a.Slots[0].Name) change = state.NewChange("connect-snap", summary) taskset, err = ifacestate.Connect(state, a.Plugs[0].Snap, a.Plugs[0].Name, a.Slots[0].Snap, a.Slots[0].Name) case "disconnect": summary := fmt.Sprintf("Disconnect %s:%s from %s:%s", a.Plugs[0].Snap, a.Plugs[0].Name, a.Slots[0].Snap, a.Slots[0].Name) change = state.NewChange("disconnect-snap", summary) taskset, err = ifacestate.Disconnect(state, a.Plugs[0].Snap, a.Plugs[0].Name, a.Slots[0].Snap, a.Slots[0].Name) } if err == nil { change.AddAll(taskset) } if err != nil { return BadRequest("%v", err) } state.EnsureBefore(0) return AsyncResponse(nil, &Meta{Change: change.ID()}) }
func change2changeInfo(chg *state.Change) *changeInfo { status := chg.Status() chgInfo := &changeInfo{ ID: chg.ID(), Kind: chg.Kind(), Summary: chg.Summary(), Status: status.String(), Ready: status.Ready(), SpawnTime: chg.SpawnTime(), } readyTime := chg.ReadyTime() if !readyTime.IsZero() { chgInfo.ReadyTime = &readyTime } if err := chg.Err(); err != nil { chgInfo.Err = err.Error() } tasks := chg.Tasks() taskInfos := make([]*taskInfo, len(tasks)) for j, t := range tasks { done, total := t.Progress() taskInfo := &taskInfo{ ID: t.ID(), Kind: t.Kind(), Summary: t.Summary(), Status: t.Status().String(), Log: t.Log(), Progress: taskInfoProgress{ Done: done, Total: total, }, SpawnTime: t.SpawnTime(), } readyTime := t.ReadyTime() if !readyTime.IsZero() { taskInfo.ReadyTime = &readyTime } taskInfos[j] = taskInfo } chgInfo.Tasks = taskInfos return chgInfo }