Scott Baker | c3ce3e7 | 2016-06-20 17:35:19 -0700 | [diff] [blame] | 1 | from rest_framework.decorators import api_view |
| 2 | from rest_framework.response import Response |
| 3 | from rest_framework.reverse import reverse |
| 4 | from rest_framework import serializers |
| 5 | from rest_framework import generics |
| 6 | from rest_framework import viewsets |
| 7 | from rest_framework.decorators import detail_route, list_route |
| 8 | from rest_framework.views import APIView |
Scott Baker | c3800fa | 2016-08-25 15:30:43 -0700 | [diff] [blame] | 9 | from rest_framework import status |
Scott Baker | c3ce3e7 | 2016-06-20 17:35:19 -0700 | [diff] [blame] | 10 | from core.models import * |
| 11 | from services.vtn.models import VTNService |
| 12 | from django.forms import widgets |
| 13 | from django.conf.urls import patterns, url |
| 14 | from api.xosapi_helpers import PlusModelSerializer, XOSViewSet, ReadOnlyField |
| 15 | from django.shortcuts import get_object_or_404 |
| 16 | from xos.apibase import XOSListCreateAPIView, XOSRetrieveUpdateDestroyAPIView, XOSPermissionDenied |
| 17 | from xos.exceptions import * |
| 18 | import json |
| 19 | import subprocess |
| 20 | |
Scott Baker | c3800fa | 2016-08-25 15:30:43 -0700 | [diff] [blame] | 21 | VTN_SERVCOMP_KINDS=["PRIVATE","VSG"] |
| 22 | |
Scott Baker | c3ce3e7 | 2016-06-20 17:35:19 -0700 | [diff] [blame] | 23 | class VTNServiceSerializer(PlusModelSerializer): |
| 24 | id = ReadOnlyField() |
| 25 | |
| 26 | privateGatewayMac = serializers.CharField(required=False) |
| 27 | localManagementIp = serializers.CharField(required=False) |
| 28 | ovsdbPort = serializers.IntegerField(required=False) |
| 29 | sshPort = serializers.IntegerField(required=False) |
| 30 | sshUser = serializers.CharField(required=False) |
| 31 | sshKeyFile = serializers.CharField(required=False) |
| 32 | mgmtSubnetBits = serializers.IntegerField(required=False) |
| 33 | xosEndpoint = serializers.CharField(required=False) |
| 34 | xosUser = serializers.CharField(required=False) |
| 35 | xosPassword = serializers.CharField(required=False) |
| 36 | |
Scott Baker | c3ce3e7 | 2016-06-20 17:35:19 -0700 | [diff] [blame] | 37 | humanReadableName = serializers.SerializerMethodField("getHumanReadableName") |
| 38 | class Meta: |
| 39 | model = VTNService |
| 40 | fields = ('humanReadableName', 'id', 'privateGatewayMac', 'localManagementIp', 'ovsdbPort', 'sshPort', 'sshUser', 'sshKeyFile', |
| 41 | 'mgmtSubnetBits', 'xosEndpoint', 'xosUser', 'xosPassword') |
| 42 | |
| 43 | def getHumanReadableName(self, obj): |
| 44 | return obj.__unicode__() |
| 45 | |
Scott Baker | c3800fa | 2016-08-25 15:30:43 -0700 | [diff] [blame] | 46 | class VTNNetwork(object): |
| 47 | def __init__(self, xos_network=None): |
| 48 | self.xos_network = xos_network |
| 49 | |
| 50 | def get_controller_network(self): |
| 51 | for cn in self.xos_network.controllernetworks.all(): |
| 52 | # TODO: find the right one |
| 53 | return cn |
| 54 | return None |
| 55 | |
| 56 | def get_cn_field(self, fieldname): |
| 57 | cn=self.get_controller_network() |
| 58 | if not cn: |
| 59 | return None |
| 60 | return getattr(cn, fieldname) |
| 61 | |
| 62 | @property |
| 63 | def id(self): |
| 64 | return self.get_cn_field("net_id") |
| 65 | |
| 66 | @property |
| 67 | def name(self): |
| 68 | return self.xos_network.name |
| 69 | |
| 70 | @property |
| 71 | def subnet(self): |
| 72 | return self.get_cn_field("subnet") |
| 73 | |
| 74 | @property |
| 75 | def gateway(self): |
| 76 | return self.get_cn_field("gateway") |
| 77 | |
| 78 | @property |
| 79 | def segmentation_id(self): |
| 80 | return self.get_cn_field("segmentation_id") |
| 81 | |
| 82 | @property |
| 83 | def type(self): |
| 84 | return self.xos_network.template.vtn_kind |
| 85 | |
| 86 | @property |
| 87 | def providerNetworks(self): |
| 88 | slice = self.xos_network.owner |
| 89 | service = slice.service |
| 90 | if not service: |
| 91 | return [] |
| 92 | |
| 93 | nets=[] |
| 94 | for tenant in service.subscribed_tenants.all(): |
| 95 | if tenant.provider_service: |
| 96 | bidirectional = tenant.connect_method!="private-unidirectional" |
| 97 | for slice in tenant.provider_service.slices.all(): |
| 98 | for net in slice.networks.all(): |
| 99 | if net.template.vtn_kind not in VTN_SERVCOMP_KINDS: |
| 100 | continue |
| 101 | |
| 102 | if not net.controllernetworks.exists(): |
| 103 | continue |
| 104 | |
| 105 | cn = net.controllernetworks.all()[0] |
| 106 | nets.append({"id": cn.net_id, |
| 107 | "name": net.name, |
| 108 | "bidirectional": bidirectional}) |
| 109 | return nets |
| 110 | |
| 111 | @property |
| 112 | def subscriberNetworks(self): |
| 113 | slice = self.xos_network.owner |
| 114 | service = slice.service |
| 115 | if not service: |
| 116 | return [] |
| 117 | |
| 118 | nets=[] |
| 119 | for tenant in service.provided_tenants.all(): |
| 120 | if tenant.subscriber_service: |
| 121 | bidirectional = tenant.connect_method!="private-unidirectional" |
| 122 | for slice in tenant.subscriber_service.slices.all(): |
| 123 | for net in slice.networks.all(): |
| 124 | if net.template.vtn_kind not in VTN_SERVCOMP_KINDS: |
| 125 | continue |
| 126 | |
| 127 | if not net.controllernetworks.exists(): |
| 128 | continue |
| 129 | |
| 130 | cn = net.controllernetworks.all()[0] |
| 131 | nets.append({"id": cn.net_id, |
| 132 | "name": net.name, |
| 133 | "bidirectional": bidirectional}) |
| 134 | return nets |
| 135 | |
| 136 | @property |
| 137 | def ownerSliceName(self): |
| 138 | if self.xos_network.owner: |
| 139 | return self.xos_network.owner.name |
| 140 | return None |
| 141 | |
| 142 | @property |
| 143 | def ownerServiceName(self): |
| 144 | if self.xos_network.owner and self.xos_network.owner.service: |
| 145 | return self.xos_network.owner.service.name |
| 146 | return None |
| 147 | |
| 148 | class VTNNetworkSerializer(serializers.Serializer): |
| 149 | id = ReadOnlyField() |
| 150 | name = serializers.CharField(required=False) |
| 151 | subnet = serializers.CharField(required=False) |
| 152 | gateway = serializers.CharField(required=False) |
| 153 | segmentation_id = serializers.CharField(required=False) |
| 154 | |
| 155 | class Meta: |
| 156 | fields = ('id', 'name', 'subnet', 'gateway', 'segmentation_id') |
| 157 | |
| 158 | class VTNServiceNetworkSerializer(serializers.Serializer): |
| 159 | id = ReadOnlyField() |
| 160 | type = serializers.CharField(required=False) |
| 161 | subscriberNetworks = serializers.SerializerMethodField("getSubscriberNetworks") |
| 162 | providerNetworks = serializers.SerializerMethodField("getProviderNetworks") |
| 163 | ownerSliceName = ReadOnlyField() |
| 164 | ownerServiceName = ReadOnlyField() |
| 165 | |
| 166 | class Meta: |
| 167 | fields = ('id', 'type', "subscriberNetworks", "providerNetworks", "ownerSliceName", "ownerServiceName") |
| 168 | |
| 169 | def getSubscriberNetworks(self, obj): |
| 170 | return obj.subscriberNetworks |
| 171 | |
| 172 | def getProviderNetworks(self, obj): |
| 173 | return obj.providerNetworks |
| 174 | |
| 175 | class VTNPort(object): |
| 176 | def __init__(self, xos_port=None):
|
| 177 | self.xos_port = xos_port
|
| 178 |
|
| 179 | def get_controller_network(self):
|
| 180 | for cn in self.xos_port.network.controllernetworks.all():
|
| 181 | # TODO: find the right one
|
| 182 | return cn
|
| 183 | return None
|
| 184 |
|
| 185 | def get_vsg_tenant(self):
|
| 186 | from services.vsg.models import VSGTenant
|
| 187 | for tenant in VSGTenant.get_tenant_objects().all():
|
| 188 | if tenant.instance == self.xos_port.instance:
|
| 189 | return tenant
|
| 190 | return None
|
| 191 |
|
| 192 | @property
|
| 193 | def vlan_id(self):
|
| 194 | if not self.xos_port.instance:
|
| 195 | return None
|
| 196 | tags = Tag.select_by_content_object(self.xos_port.instance).filter(name="s_tag")
|
| 197 | if not tags:
|
| 198 | return None
|
| 199 | return tags[0].value
|
| 200 |
|
| 201 | @property
|
| 202 | def floating_address_pairs(self):
|
| 203 | address_pairs = []
|
| 204 | vsg = self.get_vsg_tenant()
|
| 205 | if vsg:
|
| 206 | if vsg.wan_container_ip and vsg.wan_container_mac:
|
| 207 | address_pairs.append({"ip_address": vsg.wan_container_ip,
|
| 208 | "mac_address": vsg.wan_container_mac})
|
| 209 |
|
| 210 | if vsg.wan_vm_ip and vsg.wan_vm_mac:
|
| 211 | address_pairs.append({"ip_address": vsg.wan_vm_ip,
|
| 212 | "mac_address": vsg.wan_vm_mac})
|
| 213 |
|
| 214 | return address_pairs
|
| 215 |
|
| 216 | @property
|
| 217 | def id(self):
|
| 218 | return self.xos_port.port_id
|
| 219 |
|
| 220 | @property
|
| 221 | def name(self):
|
| 222 | return "port-%s" % self.xos_port.id
|
| 223 |
|
| 224 | @property
|
| 225 | def network_id(self):
|
| 226 | cn = self.get_controller_network()
|
| 227 | if not cn:
|
| 228 | return None
|
| 229 | return cn.net_id
|
| 230 |
|
| 231 | @property
|
| 232 | def mac_address(self):
|
| 233 | return self.xos_port.mac
|
| 234 |
|
| 235 | @property
|
| 236 | def ip_address(self):
|
| 237 | return self.xos_port.ip |
| 238 | |
| 239 | class VTNPortSerializer(serializers.Serializer): |
| 240 | id = ReadOnlyField() |
| 241 | name = serializers.CharField(required=False) |
| 242 | network_id = serializers.CharField(required=False) |
| 243 | mac_address = serializers.CharField(required=False) |
| 244 | ip_address = serializers.CharField(required=False) |
| 245 | |
| 246 | class Meta: |
| 247 | fields = ('id', 'name', 'network_id', 'mac_address', 'ip_address') |
| 248 | |
| 249 | class FloatingAddressPairSerializer(serializers.Serializer): |
| 250 | ip_address = ReadOnlyField() |
| 251 | mac_address = ReadOnlyField() |
| 252 | |
| 253 | class VTNServicePortSerializer(serializers.Serializer): |
| 254 | id = ReadOnlyField() |
| 255 | vlan_id = serializers.IntegerField(required=False) |
| 256 | |
| 257 | # TODO: structure this better |
| 258 | floating_address_pairs = serializers.SerializerMethodField("getFloatingAddressPairs") |
| 259 | |
| 260 | class Meta: |
| 261 | fields = ('id', 'vlan_id') |
| 262 | |
| 263 | def getFloatingAddressPairs(self, obj): |
| 264 | return obj.floating_address_pairs |
| 265 | |
Scott Baker | c3ce3e7 | 2016-06-20 17:35:19 -0700 | [diff] [blame] | 266 | class VTNViewSet(XOSViewSet): |
| 267 | base_name = "vtn" |
| 268 | method_name = "vtn" |
| 269 | method_kind = "viewset" |
| 270 | |
| 271 | # these are just because ViewSet needs some queryset and model, even if we don't use the |
| 272 | # default endpoints |
| 273 | queryset = VTNService.get_service_objects().all() |
| 274 | model = VTNService |
| 275 | serializer_class = VTNServiceSerializer |
| 276 | |
Scott Baker | c3800fa | 2016-08-25 15:30:43 -0700 | [diff] [blame] | 277 | custom_serializers = {"get_port": VTNPortSerializer, |
| 278 | "get_ports": VTNPortSerializer, |
| 279 | "get_network": VTNNetworkSerializer, |
| 280 | "get_networks": VTNNetworkSerializer} |
| 281 | |
Scott Baker | c3ce3e7 | 2016-06-20 17:35:19 -0700 | [diff] [blame] | 282 | @classmethod |
| 283 | def get_urlpatterns(self, api_path="^"): |
| 284 | patterns = [] |
| 285 | |
| 286 | patterns.append( self.detail_url("services/$", {"get": "get_services"}, "services") ) |
| 287 | patterns.append( self.detail_url("services_names/$", {"get": "get_services_names"}, "services") ) |
| 288 | patterns.append( self.detail_url("services/(?P<service>[a-zA-Z0-9\-_]+)/$", {"get": "get_service"}, "get_service") ) |
| 289 | |
| 290 | # Not as RESTful as it could be, but maintain these endpoints for compability |
| 291 | patterns.append( self.list_url("services/$", {"get": "get_services"}, "rootvtn_services") ) |
| 292 | patterns.append( self.list_url("services_names/$", {"get": "get_services_names"}, "rootvtn_services") ) |
| 293 | patterns.append( self.list_url("services/(?P<service>[a-zA-Z0-9\-_]+)/$", {"get": "get_service"}, "rootvtn_get_service") ) |
| 294 | |
Scott Baker | c3800fa | 2016-08-25 15:30:43 -0700 | [diff] [blame] | 295 | # Neutron-replacement APIs |
| 296 | patterns.append( self.list_url("ports/$", {"get": "get_ports"}, "vtn_ports") ) |
| 297 | patterns.append( self.list_url("ports/(?P<port_id>[a-zA-Z0-9\-_]+)/$", {"get": "get_port"}, "vtn_port") ) |
| 298 | patterns.append( self.list_url("networks/$", {"get": "get_networks"}, "vtn_networks") ) |
| 299 | patterns.append( self.list_url("networks/(?P<network_id>[a-zA-Z0-9\-_]+)/$", {"get": "get_network"}, "vtn_network") ) |
| 300 | |
| 301 | patterns.append( self.list_url("servicePorts/$", {"get": "get_service_ports"}, "vtn_service_ports") ) |
| 302 | patterns.append( self.list_url("servicePorts/(?P<port_id>[a-zA-Z0-9\-_]+)/$", {"get": "get_service_port"}, "vtn_service_port") ) |
| 303 | patterns.append( self.list_url("serviceNetworks/$", {"get": "get_service_networks"}, "vtn_service_networks") ) |
| 304 | patterns.append( self.list_url("serviceNetworks/(?P<network_id>[a-zA-Z0-9\-_]+)/$", {"get": "get_service_network"}, "vtn_service_network") ) |
| 305 | |
Scott Baker | c3ce3e7 | 2016-06-20 17:35:19 -0700 | [diff] [blame] | 306 | patterns = patterns + super(VTNViewSet,self).get_urlpatterns(api_path) |
| 307 | |
| 308 | return patterns |
| 309 | |
| 310 | def get_services_names(self, request, pk=None): |
| 311 | result = {} |
| 312 | for service in Service.objects.all(): |
| 313 | for id in service.get_vtn_src_names(): |
| 314 | dependencies = service.get_vtn_dependencies_names() |
| 315 | if dependencies: |
| 316 | result[id] = dependencies |
| 317 | return Response(result) |
| 318 | |
| 319 | def get_services(self, request, pk=None): |
| 320 | result = {} |
| 321 | for service in Service.objects.all(): |
| 322 | for id in service.get_vtn_src_ids(): |
| 323 | dependencies = service.get_vtn_dependencies_ids() |
| 324 | if dependencies: |
| 325 | result[id] = dependencies |
| 326 | return Response(result) |
| 327 | |
| 328 | def get_service(self, request, pk=None, service=None): |
| 329 | for xos_service in Service.objects.all(): |
| 330 | if service in xos_service.get_vtn_src_ids(): |
| 331 | return Response(xos_service.get_vtn_dependencies_ids()) |
| 332 | return Response([]) |
| 333 | |
Scott Baker | c3800fa | 2016-08-25 15:30:43 -0700 | [diff] [blame] | 334 | def get_ports(self, request): |
| 335 | result=[] |
| 336 | for port in Port.objects.all(): |
| 337 | result.append(VTNPortSerializer(VTNPort(port)).data) |
| 338 | return Response(result) |
| 339 | |
| 340 | def get_port(self, request, port_id=None): |
| 341 | port = Port.objects.filter(port_id = port_id) |
| 342 | if port: |
| 343 | port=port[0] |
| 344 | else: |
| 345 | return Response("Failed to find port %s" % port_id, status=status.HTTP_404_NOT_FOUND) |
| 346 | return Response(VTNPortSerializer(VTNPort(port)).data) |
| 347 | |
| 348 | def get_service_ports(self, request): |
| 349 | result=[] |
| 350 | for port in Port.objects.all(): |
| 351 | result.append(VTNServicePortSerializer(VTNPort(port)).data) |
| 352 | return Response(result) |
| 353 | |
| 354 | def get_service_port(self, request, port_id=None): |
| 355 | port = Port.objects.filter(port_id = port_id) |
| 356 | if port: |
| 357 | port=port[0] |
| 358 | else: |
| 359 | return Response("Failed to find port %s" % port_id, status=status.HTTP_404_NOT_FOUND) |
| 360 | return Response(VTNServicePortSerializer(VTNPort(port)).data) |
| 361 | |
| 362 | def get_networks(self, request): |
| 363 | result=[] |
| 364 | for network in Network.objects.all(): |
| 365 | result.append(VTNNetworkSerializer(VTNNetwork(network)).data) |
| 366 | return Response(result) |
| 367 | |
| 368 | def get_network(self, request, network_id=None): |
| 369 | network = Network.objects.filter(network_id = network_id) |
| 370 | if network: |
| 371 | network=network[0] |
| 372 | else: |
| 373 | return Response("Failed to find network %s" % network_id, status=status.HTTP_404_NOT_FOUND) |
| 374 | return Response(VTNNetworkSerializer(VTNNetwork(network)).data) |
| 375 | |
| 376 | def get_service_networks(self, request): |
| 377 | result=[] |
| 378 | for network in Network.objects.all(): |
| 379 | result.append(VTNServiceNetworkSerializer(VTNNetwork(network)).data) |
| 380 | return Response(result) |
| 381 | |
| 382 | def get_service_network(self, request, network_id=None): |
| 383 | network = Network.objects.filter(network_id = network_id) |
| 384 | if network: |
| 385 | network=network[0] |
| 386 | else: |
| 387 | return Response("Failed to find network %s" % network_id, status=status.HTTP_404_NOT_FOUND) |
| 388 | return Response(VTNServiceNetworkSerializer(VTNNetwork(network)).data) |
| 389 | |
| 390 | |
Scott Baker | c3ce3e7 | 2016-06-20 17:35:19 -0700 | [diff] [blame] | 391 | |