interfaces/builtin: add kvm interface #3609

Merged
merged 7 commits into from Aug 3, 2017
@@ -0,0 +1,88 @@
+// -*- Mode: Go; indent-tabs-mode: t -*-
+
+/*
+ * Copyright (C) 2017 Canonical Ltd
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+package builtin
+
+import (
+ "fmt"
+
+ "github.com/snapcore/snapd/interfaces"
+ "github.com/snapcore/snapd/interfaces/apparmor"
+ "github.com/snapcore/snapd/interfaces/udev"
+)
+
+const kvmSummary = `allows access to the kvm device`
+
+const kvmBaseDeclarationSlots = `
+ kvm:
+ allow-installation:
+ slot-snap-type:
+ - core
+ deny-auto-connection: true
+`
+
+const kvmConnectedPlugAppArmor = `
+# Description: Allow write access to kvm.
+# See 'man kvm' for details.
+
+/dev/kvm rw,
+`
+
+// The type for kvm interface
+type kvmInterface struct{}
+
+func (iface *kvmInterface) Name() string {
+ return "kvm"
+}
+
+func (iface *kvmInterface) StaticInfo() interfaces.StaticInfo {
+ return interfaces.StaticInfo{
+ Summary: kvmSummary,
+ ImplicitOnClassic: true,
+ ImplicitOnCore: true,
+ BaseDeclarationSlots: kvmBaseDeclarationSlots,
+ }
+}
+
+func (iface *kvmInterface) SanitizeSlot(slot *interfaces.Slot) error {
+ return sanitizeSlotReservedForOS(iface, slot)
+}
+
+func (iface *kvmInterface) AppArmorConnectedPlug(spec *apparmor.Specification, plug *interfaces.Plug, plugAttrs map[string]interface{}, slot *interfaces.Slot, slotAttrs map[string]interface{}) error {
+ spec.AddSnippet(kvmConnectedPlugAppArmor)
+ return nil
+}
+
+func (iface *kvmInterface) UDevConnectedPlug(spec *udev.Specification, plug *interfaces.Plug, plugAttrs map[string]interface{}, slot *interfaces.Slot, slotAttrs map[string]interface{}) error {
+ const udevRule = `KERNEL=="kvm", TAG+="%s"`
+ for appName := range plug.Apps {
+ tag := udevSnapSecurityName(plug.Snap.Name(), appName)
+ spec.AddSnippet(fmt.Sprintf(udevRule, tag))
+ }
+ return nil
+}
+
+func (iface *kvmInterface) AutoConnect(*interfaces.Plug, *interfaces.Slot) bool {
+ // Allow what is allowed in the declarations
+ return true
+}
+
+func init() {
+ registerIface(&kvmInterface{})
+}
@@ -0,0 +1,98 @@
+// -*- Mode: Go; indent-tabs-mode: t -*-
+
+/*
+ * Copyright (C) 2017 Canonical Ltd
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+package builtin_test
+
+import (
+ . "gopkg.in/check.v1"
+
+ "github.com/snapcore/snapd/interfaces"
+ "github.com/snapcore/snapd/interfaces/apparmor"
+ "github.com/snapcore/snapd/interfaces/builtin"
+ "github.com/snapcore/snapd/interfaces/udev"
+ "github.com/snapcore/snapd/snap"
+ "github.com/snapcore/snapd/snap/snaptest"
+ "github.com/snapcore/snapd/testutil"
+)
+
+type kvmInterfaceSuite struct {
+ iface interfaces.Interface
+ slot *interfaces.Slot
+ plug *interfaces.Plug
+}
+
+var _ = Suite(&kvmInterfaceSuite{
+ iface: builtin.MustInterface("kvm"),
+})
+
+func (s *kvmInterfaceSuite) SetUpTest(c *C) {
+ info := snaptest.MockInfo(c, `
+name: core
+type: os
+slots:
+ kvm:
+ interface: kvm
+`, nil)
+ s.slot = &interfaces.Slot{SlotInfo: info.Slots["kvm"]}
+
+ info = snaptest.MockInfo(c, `
+name: consumer
+apps:
+ app:
+ plugs: [kvm]
+`, nil)
+ s.plug = &interfaces.Plug{PlugInfo: info.Plugs["kvm"]}
+}
+
+func (s *kvmInterfaceSuite) TestName(c *C) {
+ c.Assert(s.iface.Name(), Equals, "kvm")
+}
+
+func (s *kvmInterfaceSuite) TestSanitizeSlot(c *C) {
+ c.Assert(s.slot.Sanitize(s.iface), IsNil)
+ slot := &interfaces.Slot{SlotInfo: &snap.SlotInfo{
+ Snap: &snap.Info{SuggestedName: "some-snap"},
+ Name: "kvm",
+ Interface: "kvm",
+ }}
+ c.Assert(slot.Sanitize(s.iface), ErrorMatches,
+ "kvm slots are reserved for the core snap")
+}
+
+func (s *kvmInterfaceSuite) TestSanitizePlug(c *C) {
+ c.Assert(s.plug.Sanitize(s.iface), IsNil)
+}
+
+func (s *kvmInterfaceSuite) TestUDevSpec(c *C) {
+ spec := &udev.Specification{}
+ c.Assert(spec.AddConnectedPlug(s.iface, s.plug, nil, s.slot, nil), IsNil)
+ c.Assert(spec.Snippets(), HasLen, 1)
+ c.Assert(spec.Snippets()[0], Equals, `KERNEL=="kvm", TAG+="snap_consumer_app"`)
+}
+
+func (s *kvmInterfaceSuite) TestAppArmorSpec(c *C) {
+ spec := &apparmor.Specification{}
+ c.Assert(spec.AddConnectedPlug(s.iface, s.plug, nil, s.slot, nil), IsNil)
+ c.Assert(spec.SecurityTags(), DeepEquals, []string{"snap.consumer.app"})
+ c.Assert(spec.SnippetForTag("snap.consumer.app"), testutil.Contains, "/dev/kvm rw,")
+}
+
+func (s *kvmInterfaceSuite) TestInterfaces(c *C) {
+ c.Check(builtin.Interfaces(), testutil.DeepContains, s.iface)
+}