diff --git a/netbox/netbox/urls.py b/netbox/netbox/urls.py
index 260d389cf..1c8051c06 100644
--- a/netbox/netbox/urls.py
+++ b/netbox/netbox/urls.py
@@ -42,6 +42,7 @@ _patterns = [
url(r'^api/ipam/', include('ipam.api.urls')),
url(r'^api/secrets/', include('secrets.api.urls')),
url(r'^api/tenancy/', include('tenancy.api.urls')),
+ url(r'^api/virtualization/', include('virtualization.api.urls')),
url(r'^api/docs/', swagger_view, name='api_docs'),
# Serving static media in Django to pipe it through LoginRequiredMiddleware
diff --git a/netbox/virtualization/api/__init__.py b/netbox/virtualization/api/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/netbox/virtualization/api/serializers.py b/netbox/virtualization/api/serializers.py
new file mode 100644
index 000000000..5b73d9bea
--- /dev/null
+++ b/netbox/virtualization/api/serializers.py
@@ -0,0 +1,139 @@
+from __future__ import unicode_literals
+
+from rest_framework import serializers
+
+from dcim.api.serializers import NestedPlatformSerializer
+from extras.api.customfields import CustomFieldModelSerializer
+from tenancy.api.serializers import NestedTenantSerializer
+from utilities.api import ModelValidationMixin
+from virtualization.models import Cluster, ClusterGroup, ClusterType, VirtualMachine, VMInterface
+
+
+#
+# Cluster types
+#
+
+class ClusterTypeSerializer(ModelValidationMixin, serializers.ModelSerializer):
+
+ class Meta:
+ model = ClusterType
+ fields = ['id', 'name', 'slug']
+
+
+class NestedClusterTypeSerializer(serializers.ModelSerializer):
+ url = serializers.HyperlinkedIdentityField(view_name='virtualization-api:clustertype-detail')
+
+ class Meta:
+ model = ClusterType
+ fields = ['id', 'url', 'name', 'slug']
+
+
+#
+# Cluster groups
+#
+
+class ClusterGroupSerializer(ModelValidationMixin, serializers.ModelSerializer):
+
+ class Meta:
+ model = ClusterGroup
+ fields = ['id', 'name', 'slug']
+
+
+class NestedClusterGroupSerializer(serializers.ModelSerializer):
+ url = serializers.HyperlinkedIdentityField(view_name='virtualization-api:clustergroup-detail')
+
+ class Meta:
+ model = ClusterGroup
+ fields = ['id', 'url', 'name', 'slug']
+
+
+#
+# Clusters
+#
+
+class ClusterSerializer(CustomFieldModelSerializer):
+ type = NestedClusterTypeSerializer()
+ group = NestedClusterGroupSerializer()
+
+ class Meta:
+ model = Cluster
+ fields = ['id', 'name', 'type', 'group', 'comments', 'custom_fields']
+
+
+class NestedClusterSerializer(serializers.ModelSerializer):
+ url = serializers.HyperlinkedIdentityField(view_name='virtualization-api:cluster-detail')
+
+ class Meta:
+ model = Cluster
+ fields = ['id', 'url', 'name']
+
+
+class WritableClusterSerializer(ModelValidationMixin, CustomFieldModelSerializer):
+
+ class Meta:
+ model = Cluster
+ fields = ['id', 'name', 'type', 'group', 'comments', 'custom_fields']
+
+
+#
+# Virtual machines
+#
+
+class VirtualMachineSerializer(CustomFieldModelSerializer):
+ cluster = NestedClusterSerializer()
+ tenant = NestedTenantSerializer()
+ platform = NestedPlatformSerializer()
+
+ class Meta:
+ model = VirtualMachine
+ fields = [
+ 'id', 'name', 'cluster', 'tenant', 'platform', 'primary_ip4', 'primary_ip6', 'comments', 'custom_fields',
+ ]
+
+
+class NestedVirtualMachineSerializer(serializers.ModelSerializer):
+ url = serializers.HyperlinkedIdentityField(view_name='virtualization-api:virtualmachine-detail')
+
+ class Meta:
+ model = VirtualMachine
+ fields = ['id', 'url', 'name']
+
+
+class WritableVirtualMachineSerializer(ModelValidationMixin, CustomFieldModelSerializer):
+
+ class Meta:
+ model = Cluster
+ fields = [
+ 'id', 'name', 'cluster', 'tenant', 'platform', 'primary_ip4', 'primary_ip6', 'comments', 'custom_fields',
+ ]
+
+
+#
+# VM interfaces
+#
+
+class VMInterfaceSerializer(serializers.ModelSerializer):
+ virtual_machine = NestedVirtualMachineSerializer()
+
+ class Meta:
+ model = VMInterface
+ fields = [
+ 'id', 'name', 'virtual_machine', 'enabled', 'mac_address', 'mtu', 'description',
+ ]
+
+
+class NestedVMInterfaceSerializer(serializers.ModelSerializer):
+ url = serializers.HyperlinkedIdentityField(view_name='virtualization-api:vminterface-detail')
+
+ class Meta:
+ model = VMInterface
+ fields = ['id', 'url', 'name']
+
+
+class WritableVMInterfaceSerializer(ModelValidationMixin, serializers.ModelSerializer):
+
+ class Meta:
+ model = VMInterface
+ fields = [
+ 'id', 'name', 'virtual_machine', 'enabled', 'mac_address', 'mtu', 'description',
+ ]
diff --git a/netbox/virtualization/api/urls.py b/netbox/virtualization/api/urls.py
new file mode 100644
index 000000000..a53fdbde2
--- /dev/null
+++ b/netbox/virtualization/api/urls.py
@@ -0,0 +1,29 @@
+from __future__ import unicode_literals
+
+from rest_framework import routers
+
+from . import views
+
+
+class VirtualizationRootView(routers.APIRootView):
+ """
+ Virtualization API root view
+ """
+ def get_view_name(self):
+ return 'Virtualization'
+
+
+router = routers.DefaultRouter()
+router.APIRootView = VirtualizationRootView
+
+# Clusters
+router.register(r'cluster-types', views.ClusterTypeViewSet)
+router.register(r'cluster-groups', views.ClusterGroupViewSet)
+router.register(r'clusters', views.ClusterViewSet)
+
+# VirtualMachines
+router.register(r'virtual-machines', views.VirtualMachineViewSet)
+router.register(r'vm-interfaces', views.VMInterfaceViewSet)
+
+app_name = 'virtualization-api'
+urlpatterns = router.urls
diff --git a/netbox/virtualization/api/views.py b/netbox/virtualization/api/views.py
new file mode 100644
index 000000000..79fd73ca8
--- /dev/null
+++ b/netbox/virtualization/api/views.py
@@ -0,0 +1,44 @@
+from __future__ import unicode_literals
+
+from rest_framework.viewsets import ModelViewSet
+
+from extras.api.views import CustomFieldModelViewSet
+from utilities.api import WritableSerializerMixin
+from virtualization.models import Cluster, ClusterGroup, ClusterType, VirtualMachine, VMInterface
+from . import serializers
+
+
+#
+# Clusters
+#
+
+class ClusterTypeViewSet(ModelViewSet):
+ queryset = ClusterType.objects.all()
+ serializer_class = serializers.ClusterTypeSerializer
+
+
+class ClusterGroupViewSet(ModelViewSet):
+ queryset = ClusterGroup.objects.all()
+ serializer_class = serializers.ClusterGroupSerializer
+
+
+class ClusterViewSet(WritableSerializerMixin, CustomFieldModelViewSet):
+ queryset = Cluster.objects.select_related('type', 'group')
+ serializer_class = serializers.ClusterSerializer
+ write_serializer_class = serializers.WritableClusterSerializer
+
+
+#
+# Virtual machines
+#
+
+class VirtualMachineViewSet(WritableSerializerMixin, CustomFieldModelViewSet):
+ queryset = VirtualMachine.objects.all()
+ serializer_class = serializers.VirtualMachineSerializer
+ write_serializer_class = serializers.WritableVirtualMachineSerializer
+
+
+class VMInterfaceViewSet(WritableSerializerMixin, ModelViewSet):
+ queryset = VMInterface.objects.select_related('virtual_machine')
+ serializer_class = serializers.VMInterfaceSerializer
+ write_serializer_class = serializers.WritableVMInterfaceSerializer
diff --git a/netbox/virtualization/tables.py b/netbox/virtualization/tables.py
index 3beb7b53b..a5bbad9a2 100644
--- a/netbox/virtualization/tables.py
+++ b/netbox/virtualization/tables.py
@@ -9,13 +9,13 @@ from .models import Cluster, ClusterGroup, ClusterType, VirtualMachine, VMInterf
CLUSTERTYPE_ACTIONS = """
{% if perms.virtualization.change_clustertype %}
-
+
{% endif %}
"""
CLUSTERGROUP_ACTIONS = """
{% if perms.virtualization.change_clustergroup %}
-
+
{% endif %}
"""