CORD-1813 Document Service/Tenancy migration
Change-Id: I52f2ccdd0133c795a93ee0de6d6cf8152b9858c8
diff --git a/docs/migrate-4.0.md b/docs/migrate-4.0.md
new file mode 100644
index 0000000..ea7e698
--- /dev/null
+++ b/docs/migrate-4.0.md
@@ -0,0 +1,128 @@
+# CORD-4.0 Service Migration Guide
+
+## Service/Tenancy Model Migration
+
+CORD-4.0 makes the following changes:
+
+- Renames `Tenant` to `ServiceInstance`.
+- Replaces CORD-3.0's many-to-one tenancy links with a new many-to-many link object called `ServiceInstanceLink`.
+- Introduces the concept of service interfaces using the `InterfaceType` and `ServiceInterface` models.
+- Makes `ServiceDependency` a separate model not directly related to Tenancy models.
+
+Note that for the purposes of this document, we still refer to some R-CORD models using the suffix "Tenant" rather than "ServiceInstance". As time permits, those R-CORD models will be renamed. New services are recommended to use the suffix ServiceInstance rather than Tenant.
+
+### Migrating existing Tenants
+
+The base class has been changed from `Tenant` to `ServiceInstance`. This may require an `xproto` change, for example:
+
+ - message VTRTenant (Tenant){
+ + message VTRTenant (ServiceInstance){
+
+Note that `TenantWithContainer` has not yet been renamed (at some point in the future it may become `ServiceInstanceWithContainer`), so models inheriting from `TenantWithContainer` are fine for now.
+
+### Differences in ServiceInstance fields
+
+A few fields in ServiceInstance have been changed from what they were in `Tenant`:
+
+- `Tenant.provider_service` --> `ServiceInstance.owner`
+
+### Creating links between Service Instances (`ServiceInstanceLink` objects)
+
+A common pattern in CORD-3.0 model policies is to create a new Tenant and link it to an existing model. For example,
+
+ t = VRouterTenant(provider_service=vrouter_service,
+ subscriber_tenant=some_vsg_tenant)
+ t.save()
+
+In the above example, the relationship between the new VRouterTenant and `some_vsg_tenant` was captured by the field `subscriber_tenant`. This field no longer exists in CORD-4.0 and needs to be replaced with a link:
+
+ t = VRouterTenant(owner=vrouter_service)
+ t.save()
+ l = ServiceInstanceLink(provider_service_instance = t,
+ subscriber_service_interface=some_vsg_tenant)
+ l.save()
+
+### Traversing links between Service Instances
+
+In CORD-3.0, it was possible to determine the subscriber of a Tenant by looking at the Tenant's `subscriber_*` properties. For example,
+
+ subscriber = some_vrouter_tenant.subscriber_tenant
+ vsg_tenant = VSGTenant.objects.get(id = subscriber.id)
+ # now, do something with vsg_tenant
+
+In CORD-4.0 you will need to traverse `ServiceInstanceLink` objects instead:
+
+ for link in some_vrouter_tenant.provided_links.all():
+ if link.subscriber_service_instance:
+ subscriber = link.subscriber_service_instance
+ vsg_tenant = subscriber.leaf_model
+ # now, do something with vsg_tenant
+
+You can also walk in the opposite direction:
+
+ for link in some_vsg_tenant.subscribed_links.all():
+ if link.subscriber_service_instance:
+ provider = link.provider_service_instance
+ vrouter_tenant = provider.leaf_model
+ # now, do something with vrouter_tenant
+
+Note that since the service instance graph now supports true many-to-many relations, it's common to have to use for loops as described above to cover cases where an object may be linked to many providers or many subscribers. If it's a known constraint that only one object may be linked, then it may be reasonable to omit the for loop and use `provided_links.first()` or `subscribed_links.first()` instead of `.all()`.
+
+Also note that `leaf_model` is a property that will automatically cast any base object to its descendant class. For example, if you have a generic `ServiceInstance` object, and that `ServiceInstance` is really a `VSGTenant`, then `leaf_model` will perform that conversion for you automatically.
+
+### Removing links between Service Instances
+
+Links are removed by deleting the `ServiceInstanceLink` object. For example,
+
+ # delete all links between some_vsg_tenant and some_vrouter_tenant
+ for link in ServiceInstanceLink.objects.filter(provider_service_instance_id=some_vsg_tenant.id, subscriber_service_instance_id=some_vrouter_instance.id):
+ link.delete()
+
+### Creating ServiceInterfaces
+
+`ServiceInterfaces` allow you to type the links between `ServiceInstances`. For example, if one `ServiceInstance` provides a `WAN` interface and another `ServiceInstance` uses a `LAN` interface, you can explicitly connect those two interfaces. These are currently created in Tosca. For example,
+
+ in#lanside:
+ type: tosca.nodes.InterfaceType
+ properties:
+ direction: in
+
+ out#lanside:
+ type: tosca.nodes.InterfaceType
+ properties:
+ direction: out
+
+ volt_lanside:
+ type: tosca.nodes.ServiceInterface
+ requirements:
+ - service:
+ node: service#volt
+ relationship: tosca.relationships.MemberOfService
+ - interface:
+ node: out#lanside
+ relationship: tosca.relationships.IsType
+
+ vsg_lanside:
+ type: tosca.nodes.ServiceInterface
+ requirements:
+ - service:
+ node: service#vsg
+ relationship: tosca.relationships.MemberOfService
+ - interface:
+ node: in#lanside
+ relationship: tosca.relationships.IsType
+
+This example creates a `lanside` interface that is present in both the `VOLT` and `VSG` services.
+
+Interfaces are currently optional, but may become mandatory in the next release. Until then, you can optionally associate links with interfaces. For example,
+
+ interface_type = InterfaceType.objects.get(name="lanside", direction="in")
+ interface = VSGService.Interfaces.get(interface_type=interface_type)
+ t = VSGTenant(owner=vsg_service)
+ t.save()
+ l = ServiceInstanceLink(provider_service_instance = t,
+ provider_service_interface = interface,
+ subscriber_service_interface=some_volt_tenant)
+ l.save()
+
+As `ServiceInterface` are not mandatory, it's suggested that you perform the other migration steps, and leave `ServiceInterfaces` until everything else is working.
\ No newline at end of file