initial code migration from xos repo
Change-Id: I8c848929ec4583a7a18ba9da44095f8f688f96c0
diff --git a/xos/admin.py b/xos/admin.py
new file mode 100644
index 0000000..19f5190
--- /dev/null
+++ b/xos/admin.py
@@ -0,0 +1,229 @@
+from django.contrib import admin
+
+from services.hpc.models import *
+from django import forms
+from django.utils.safestring import mark_safe
+from django.contrib.auth.admin import UserAdmin
+from django.contrib.admin.widgets import FilteredSelectMultiple
+from django.contrib.auth.forms import ReadOnlyPasswordHashField
+from django.contrib.auth.signals import user_logged_in
+from django.utils import timezone
+from suit.widgets import LinkedSelect
+from core.admin import ServiceAppAdmin,SliceInline,ServiceAttrAsTabInline, ReadOnlyAwareAdmin, XOSTabularInline, SliderWidget, ServicePrivilegeInline
+from core.middleware import get_request
+
+from functools import update_wrapper
+from django.contrib.admin.views.main import ChangeList
+from django.core.urlresolvers import reverse, NoReverseMatch
+from django.contrib.admin.utils import quote
+
+from filteredadmin import FilteredChangeList, FilteredAdmin, FilteredInline
+
+class HpcServiceForm(forms.ModelForm):
+ scale = forms.IntegerField(widget = SliderWidget, required=False)
+
+ def __init__(self, *args, **kwargs):
+ super(HpcServiceForm, self).__init__(*args, **kwargs)
+ if ("instance" in kwargs) and (hasattr(kwargs["instance"], "scale")):
+ self.fields['scale'].initial = kwargs["instance"].scale
+
+ def save(self, *args, **kwargs):
+ if self.cleaned_data['scale']:
+ self.instance.scale = self.cleaned_data['scale']
+
+ return super(HpcServiceForm, self).save(*args, **kwargs)
+
+class HpcServiceAdmin(ReadOnlyAwareAdmin):
+ model = HpcService
+ verbose_name = "HPC Service"
+ verbose_name_plural = "HPC Service"
+ list_display = ("backend_status_icon", "name","enabled")
+ list_display_links = ('backend_status_icon', 'name', )
+ fieldsets = [(None, {'fields': ['backend_status_text', 'name','scale','enabled','versionNumber', 'description', "cmi_hostname", "hpc_port80", "watcher_hpc_network", "watcher_dnsredir_network", "watcher_dnsdemux_network"], 'classes':['suit-tab suit-tab-general']})]
+ readonly_fields = ('backend_status_text', )
+ inlines = [SliceInline,ServiceAttrAsTabInline,ServicePrivilegeInline]
+ form = HpcServiceForm
+
+ extracontext_registered_admins = True
+
+ user_readonly_fields = ["name", "enabled", "versionNumber", "description"]
+
+ suit_form_tabs =(('general', 'HPC Service Details'),
+ ('administration', 'Administration'),
+ ('tools', 'Tools'),
+ ('slices','Slices'),
+ ('serviceattrs','Additional Attributes'),
+ ('serviceprivileges','Privileges'),
+ )
+
+ suit_form_includes = (('hpcadmin.html', 'top', 'administration'),
+ ('hpctools.html', 'top', 'tools') )
+
+ def url_for_model_changelist(self, request, model):
+ if not request.resolver_match.args:
+ return reverse('admin:%s_%s_changelist' % (model._meta.app_label, model._meta.model_name), current_app=model._meta.app_label)
+ else:
+ obj_id = request.resolver_match.args[0]
+ changelist_url = reverse('admin:%s_%s_filteredchangelist' % (model._meta.app_label, model._meta.model_name), args=(obj_id,), current_app=model._meta.app_label)
+ return changelist_url
+
+class HPCAdmin(FilteredAdmin):
+ # Change the application breadcrumb to point to an HPC Service if one is
+ # defined
+
+ custom_app_breadcrumb_name = "Hpc"
+ @property
+ def custom_app_breadcrumb_url(self):
+ services = HpcService.objects.all()
+ if len(services)==1:
+ return "/admin/hpc/hpcservice/%s/" % services[0].id
+ else:
+ return "/admin/hpc/hpcservice/"
+
+class CDNPrefixInline(FilteredInline):
+ model = CDNPrefix
+ extra = 0
+ suit_classes = 'suit-tab suit-tab-prefixes'
+ fields = ('backend_status_icon', 'cdn_prefix_id', 'prefix', 'defaultOriginServer', 'enabled')
+ readonly_fields = ('backend_status_icon', 'cdn_prefix_id',)
+
+class OriginServerInline(FilteredInline):
+ model = OriginServer
+ extra = 0
+ suit_classes = 'suit-tab suit-tab-origins'
+ fields = ('backend_status_icon', 'origin_server_id', 'url')
+ readonly_fields = ('backend_status_icon', 'origin_server_id')
+
+class ContentProviderInline(FilteredInline):
+ model = ContentProvider
+ extra = 0
+ suit_classes = 'suit-tab suit-tab-cps'
+ fields = ('backend_status_icon', 'content_provider_id', 'name', 'enabled')
+ readonly_fields = ('backend_status_icon', 'content_provider_id',)
+
+class OriginServerAdmin(HPCAdmin):
+ list_display = ('backend_status_icon', 'url','protocol','redirects','contentProvider','authenticated','enabled' )
+ list_display_links = ('backend_status_icon', 'url', )
+
+ fields = ('backend_status_text', 'url','protocol','redirects','contentProvider','authenticated','enabled','origin_server_id','description' )
+ readonly_fields = ('backend_status_text', 'origin_server_id',)
+ user_readonly_fields = ('url','protocol','redirects','contentProvider','authenticated','enabled','origin_server_id','description')
+
+class ContentProviderForm(forms.ModelForm):
+ users = forms.ModelMultipleChoiceField(
+ queryset=User.objects.all(),
+ required=False,
+ help_text="Select which users can manage this ContentProvider",
+ widget=FilteredSelectMultiple(
+ verbose_name=('Users'), is_stacked=False
+ )
+ )
+
+ class Meta:
+ model = ContentProvider
+ widgets = {
+ 'serviceProvider' : LinkedSelect
+ }
+ fields = '__all__'
+
+ def __init__(self, *args, **kwargs):
+ request = kwargs.pop('request', None)
+ super(ContentProviderForm, self).__init__(*args, **kwargs)
+
+ if self.instance and self.instance.pk:
+ self.fields['users'].initial = self.instance.users.all()
+
+class ContentProviderAdmin(HPCAdmin):
+ form = ContentProviderForm
+ list_display = ('backend_status_icon', 'name','description','enabled' )
+ list_display_links = ('backend_status_icon', 'name', )
+ readonly_fields = ('backend_status_text', )
+ admin_readonly_fields = ('backend_status_text', )
+ cp_readonly_fields = ('backend_status_text', 'name', 'enabled', 'serviceProvider', 'users')
+ fieldsets = [ (None, {'fields': ['backend_status_text', 'name','enabled','description','serviceProvider','users'], 'classes':['suit-tab suit-tab-general']})]
+
+ inlines = [CDNPrefixInline, OriginServerInline]
+
+ user_readonly_fields = ('name','description','enabled','serviceProvider','users')
+
+ suit_form_tabs = (('general','Details'),('prefixes','CDN Prefixes'), ('origins','Origin Servers'))
+
+ def change_view(self,request, *args, **kwargs):
+ if request.user.is_admin:
+ self.readonly_fields = self.admin_readonly_fields
+ else:
+ self.readonly_fields = self.cp_readonly_fields
+
+ return super(ContentProviderAdmin, self).change_view(request, *args, **kwargs)
+
+ def has_add_permission(self, request):
+ return request.user.is_admin
+
+ def has_delete_permission(self, request, obj=None):
+ return request.user.is_admin
+
+class ServiceProviderAdmin(HPCAdmin):
+ list_display = ('backend_status_icon', 'name', 'description', 'enabled')
+ list_display_links = ('backend_status_icon', 'name', )
+ fieldsets = [
+ (None, {'fields': ['backend_status_text', 'name','description','enabled', 'hpcService'], 'classes':['suit-tab suit-tab-general']})]
+#, ('Content Providers', {'fields':['contentProviders'],'classes':['suit-tab suit-tab-cps']})]
+
+ readonly_fields = ('backend_status_text', )
+ user_readonly_fields = ('name', 'description', 'enabled')
+
+ suit_form_tabs = (('general','Details'),('cps','Content Providers'))
+ inlines = [ContentProviderInline]
+
+class CDNPrefixForm(forms.ModelForm):
+ class Meta:
+ widgets = {
+ 'contentProvider' : LinkedSelect
+ }
+ fields = '__all__'
+
+class CDNPrefixAdmin(HPCAdmin):
+ form = CDNPrefixForm
+ list_display = ['backend_status_icon', 'prefix','contentProvider']
+ list_display_links = ('backend_status_icon', 'prefix', )
+ fields = ['backend_status_text', 'prefix', 'contentProvider', 'cdn_prefix_id', 'description', 'defaultOriginServer', 'enabled']
+ readonly_fields = ('backend_status_text', )
+ user_readonly_fields = ['prefix','contentProvider', "cdn_prefix_id", "description", "defaultOriginServer", "enabled"]
+
+class SiteMapAdmin(HPCAdmin):
+ model = SiteMap
+ verbose_name = "Site Map"
+ verbose_name_plural = "Site Map"
+ list_display = ("backend_status_icon", "name", "contentProvider", "serviceProvider")
+ list_display_links = ('backend_status_icon', 'name', )
+ fields = ['backend_status_text', 'name', 'hpcService', 'cdnPrefix', 'contentProvider', 'serviceProvider', 'map', 'map_id']
+ user_readonly_fields = ('backend_status_text', "name", "hpcService", "cdnPrefix", "contentProvider", "serviceProvider", "description", "map")
+ readonly_fields = ('backend_status_text', )
+
+class AccessMapAdmin(HPCAdmin):
+ model = AccessMap
+ verbose_name = "Access Map"
+ verbose_name_plural = "Access Map"
+ list_display = ("backend_status_icon", "name", "contentProvider")
+ list_display_links = ('backend_status_icon', 'name', )
+ user_readonly_fields = ('backend_status_text', "name", "contentProvider", "description", "map")
+ readonly_fields = ('backend_status_text', )
+
+class HpcHealthCheckAdmin(HPCAdmin):
+ model = HpcHealthCheck
+ verbose_name = "Health Check"
+ verbose_name = "Health Checks"
+ list_display = ["backend_status_icon", "resource_name", "kind"]
+ list_display_links = ["backend_status_icon", "resource_name"]
+ fields = ["backend_status_text", "hpcService", "resource_name", "kind", "result_contains", "result_min_size", "result_max_size"]
+ readonly_fields = ["backend_status_text",]
+
+admin.site.register(ServiceProvider, ServiceProviderAdmin)
+admin.site.register(ContentProvider, ContentProviderAdmin)
+admin.site.register(CDNPrefix, CDNPrefixAdmin)
+admin.site.register(OriginServer,OriginServerAdmin)
+admin.site.register(HpcService, HpcServiceAdmin)
+admin.site.register(SiteMap, SiteMapAdmin)
+admin.site.register(AccessMap, AccessMapAdmin)
+admin.site.register(HpcHealthCheck, HpcHealthCheckAdmin)
+
diff --git a/xos/api/service/hpc/hpc_config.py b/xos/api/service/hpc/hpc_config.py
new file mode 100644
index 0000000..a181a1c
--- /dev/null
+++ b/xos/api/service/hpc/hpc_config.py
@@ -0,0 +1,131 @@
+from django.http import HttpResponse, HttpResponseServerError
+from core.models import *
+from services.hpc.models import *
+from services.requestrouter.models import *
+import xos.settings
+import json
+import os
+import time
+
+def get_service_slices(service):
+ try:
+ return service.slices.all()
+ except:
+ # this field used to be improperly named, and makemigrations won't fix it
+ return service.service.all()
+
+def HpcConfig(request):
+ hpcSlice=None
+ cmiSlice=None
+ redirSlice=None
+ demuxSlice=None
+
+ node_slicename = request.GET.get("slicename", None)
+ if not node_slicename:
+ return HttpResponseServerError("Error: no slicename passed in request")
+
+ # search for an HPC Service that owns the slicename that was passed
+ # to us.
+ hpc=None
+ for candidate in HpcService.objects.all():
+ if candidate.cmi_hostname == node_slicename:
+ # A hack for standalone CMIs that aren't managed by XOS. Set
+ # /etc/slicename to cmi_hostname that's configured in the
+ # HPCService object.
+ hpc = candidate
+
+ for slice in get_service_slices(candidate):
+ if slice.name == node_slicename:
+ hpc = candidate
+
+ if (not hpc):
+ return HttpResponseServerError("Error: no HPC service")
+
+ for slice in get_service_slices(hpc):
+ if "cmi" in slice.name:
+ cmiSlice = slice
+ elif ("hpc" in slice.name) or ("vcoblitz" in slice.name):
+ hpcSlice = slice
+ elif "redir" in slice.name:
+ redirSlice = slice
+ elif "demux" in slice.name:
+ demuxSlice = slice
+
+ if (hpc.cmi_hostname):
+ cmi_hostname = hpc.cmi_hostname
+ else:
+ if not cmiSlice:
+ return HttpResponseServerError("Error: no CMI slice")
+
+ if len(cmiSlice.instances.all())==0:
+ return HttpResponseServerError("Error: CMI slice has no instances")
+
+ # for now, assuming using NAT
+ cmi_hostname = cmiSlice.instances.all()[0].node.name
+
+ if not hpcSlice:
+ return HttpResponseServerError("Error: no HPC slice")
+
+ if (redirSlice==None) or (demuxSlice==None):
+ # The HPC Service didn't have a dnsredir or a dnsdemux, so try looking
+ # in the RequestRouterService for one.
+
+ rr = RequestRouterService.objects.all()
+ if not (rr):
+ return HttpResponseServerError("Error: no RR service")
+
+ rr = rr[0]
+ try:
+ slices = rr.slices.all()
+ except:
+ # this field used to be improperly named, and makemigrations won't fix it
+ slices = rr.service.all()
+ for slice in slices:
+ if "redir" in slice.name:
+ redirSlice = slice
+ elif "demux" in slice.name:
+ demuxSlice = slice
+
+ if not redirSlice:
+ return HttpResponseServerError("Error: no dnsredir slice")
+
+ if not demuxSlice:
+ return HttpResponseServerError("Error: no dnsdemux slice")
+
+ d = {}
+ d["hpc_slicename"] = hpcSlice.name
+ d["redir_slicename"] = redirSlice.name
+ d["demux_slicename"] = demuxSlice.name
+ d["cmi_hostname"] = cmi_hostname
+ d["xos_hostname"] = xos.settings.RESTAPI_HOSTNAME
+ d["xos_port"] = str(xos.settings.RESTAPI_PORT)
+
+ if hpc.hpc_port80:
+ d["hpc_port80"] = "True"
+ else:
+ d["hpc_port80"] = "False"
+
+ return HttpResponse("""# auto-generated by HpcConfig
+ENABLE_PLC=False
+ENABLE_PS=True
+BASE_HRN="princeton"
+RELEVANT_SERVICE_NAMES=['vcoblitz', 'coredirect', 'codnsdemux', "syndicate_comon_server"]
+COBLITZ_SLICE_NAME=BASE_HRN+"_vcoblitz"
+COBLITZ_SLICE_ID=70
+COBLITZ_PS_SLICE_NAME="{hpc_slicename}"
+DNSREDIR_SLICE_NAME=BASE_HRN+"_coredirect"
+DNSREDIR_SLICE_ID=71
+DNSREDIR_PS_SLICE_NAME="{redir_slicename}"
+DNSDEMUX_SLICE_NAME=BASE_HRN+"_codnsdemux"
+DNSDEMUX_SLICE_ID=69
+DNSDEMUX_PS_SLICE_NAME="{demux_slicename}"
+CMI_URL="http://{cmi_hostname}/"
+CMI_HTTP_PORT="8004"
+CMI_HTTPS_PORT="8003"
+PUPPET_MASTER_HOSTNAME="{cmi_hostname}"
+PUPPET_MASTER_PORT="8140"
+PS_HOSTNAME="{xos_hostname}"
+PS_PORT="{xos_port}"
+COBLITZ_PORT_80={hpc_port80}
+""".format(**d))
+
diff --git a/xos/api/service/hpc/legacyapi.py b/xos/api/service/hpc/legacyapi.py
new file mode 100644
index 0000000..b5592c0
--- /dev/null
+++ b/xos/api/service/hpc/legacyapi.py
@@ -0,0 +1,353 @@
+import os
+import json
+import socket
+import sys
+import time
+import traceback
+import xmlrpclib
+
+from core.models import Slice, Instance, ServiceClass, Reservation, Tag, Network, User, Node, Image, Deployment, Site, NetworkTemplate, NetworkSlice
+
+from django.http import HttpResponse
+from django.views.decorators.csrf import csrf_exempt
+
+def ps_id_to_pl_id(x):
+ # Since we don't want the XOS object IDs to conflict with existing
+ # PlanetLab object IDs in the CMI, just add 100000 to the XOS object
+ # IDs.
+ return 100000 + x
+
+def pl_id_to_ps_id(x):
+ return x - 100000
+
+# slice_remap is a dict of ps_slice_name -> (pl_slice_name, pl_slice_id)
+
+def pl_slice_id(slice, slice_remap={}):
+ if slice.name in slice_remap:
+ return int(slice_remap[slice.name][1])
+ else:
+ return ps_id_to_pl_id(slice.id)
+
+def pl_slicename(slice, slice_remap={}):
+ if slice.name in slice_remap:
+ return slice_remap[slice.name][0]
+ else:
+ return slice.name
+
+def filter_fields(src, fields):
+ dest = {}
+ for (key,value) in src.items():
+ if (not fields) or (key in fields):
+ dest[key] = value
+ return dest
+
+def GetSlices(filter={}, slice_remap={}):
+ #ps_slices = Slice.objects.filter(**filter)
+ ps_slices = Slice.objects.all()
+ slices = []
+ for ps_slice in ps_slices:
+ if (filter) and ("name" in filter):
+ remapped_name = slice_remap.get(ps_slice.name, (ps_slice.name,))[0]
+ if (remapped_name != filter["name"]):
+ continue
+
+ node_ids=[]
+ for ps_instance in ps_slice.instances.all():
+ node_ids.append(ps_id_to_pl_id(ps_instance.node.id))
+
+ slice = {"instantiation": "plc-instantiated",
+ "description": "XOS slice",
+ "slice_id": pl_slice_id(ps_slice, slice_remap),
+ "node_ids": node_ids,
+ "url": "xos",
+ "max_nodes": 1000,
+ "peer_slice_id": None,
+ "slice_tag_ids": [],
+ "peer_id": None,
+ "site_id": ps_id_to_pl_id(ps_slice.site_id),
+ "name": pl_slicename(ps_slice, slice_remap),
+ "planetstack_name": ps_slice.name} # keeping planetstack_name for now, to match the modified config.py
+
+ # creator_person_id, person_ids, expires, created
+
+ slices.append(slice)
+ return slices
+
+def GetNodes(node_ids=None, fields=None, slice_remap={}):
+ if node_ids:
+ ps_nodes = Node.objects.filter(id__in=[pl_id_to_ps_id(nid) for nid in node_ids])
+ else:
+ ps_nodes = Node.objects.all()
+ nodes = []
+ for ps_node in ps_nodes:
+ slice_ids=[]
+ for ps_instance in ps_node.instances.all():
+ slice_ids.append(pl_slice_id(ps_instance.slice, slice_remap))
+
+ node = {"node_id": ps_id_to_pl_id(ps_node.id),
+ "site_id": ps_id_to_pl_id(ps_node.site_id),
+ "node_type": "regular",
+ "peer_node_id": None,
+ "hostname": ps_node.name.lower(),
+ "conf_file_ids": [],
+ "slice_ids": slice_ids,
+ "model": "xos",
+ "peer_id": None,
+ "node_tag_ids": []}
+
+ # last_updated, key, boot_state, pcu_ids, node_type, session, last_boot,
+ # interface_ids, slice_ids_whitelist, run_level, ssh_rsa_key, last_pcu_reboot,
+ # nodegroup_ids, verified, last_contact, boot_nonce, version,
+ # last_pcu_configuration, last_download, date_created, ports
+
+ nodes.append(node)
+
+ nodes = [filter_fields(node, fields) for node in nodes]
+
+ return nodes
+
+def GetTags(slicename,node_id):
+ return {}
+
+def GetSites(slice_remap={}):
+ ps_sites = Site.objects.all()
+ sites = []
+ for ps_site in ps_sites:
+ slice_ids=[]
+ for ps_slice in ps_site.slices.all():
+ slice_ids.append(pl_slice_id(ps_slice, slice_remap))
+
+ node_ids=[]
+ for ps_node in ps_site.nodes.all():
+ node_ids.append(ps_id_to_pl_id(ps_node.id))
+
+ if ps_site.location:
+ longitude = ps_site.location.longitude
+ latitude = ps_site.location.latitude
+ else:
+ longitude = 0
+ latitude = 0
+
+ site = {"site_id": ps_id_to_pl_id(ps_site.id),
+ "node_ids": node_ids,
+ "pcu_ids": [],
+ "max_slices": 100,
+ "max_instances": 1000,
+ "is_public": False,
+ "peer_site_id": None,
+ "abbrebiated_name": ps_site.abbreviated_name,
+ "address_ids": [],
+ "name": ps_site.name,
+ "url": None,
+ "site_tag_ids": [],
+ "enabled": True,
+ "longitude": float(longitude),
+ "latitude": float(latitude),
+ "slice_ids": slice_ids,
+ "login_base": ps_site.login_base,
+ "peer_id": None}
+
+ # last_updated, ext_consortium_id, person_ids, date_created
+
+ sites.append(site)
+
+ return sites
+
+def GetInterfaces(slicename, node_ids, return_nat=False, return_private=False):
+ interfaces = []
+ ps_slices = Slice.objects.filter(name=slicename)
+ for ps_slice in ps_slices:
+ for ps_instance in ps_slice.instances.all():
+ node_id = ps_id_to_pl_id(ps_instance.node_id)
+ if node_id in node_ids:
+ ps_node = ps_instance.node
+
+ ip = socket.gethostbyname(ps_node.name.strip())
+
+ # If the slice has a network that's labeled for hpc_client, then
+ # return that network.
+ found_labeled_network = False
+ for port in ps_instance.ports.all():
+ if (not port.ip):
+ continue
+ if (port.network.owner != ps_slice):
+ continue
+ if port.network.labels and ("hpc_client" in port.network.labels):
+ ip=port.ip
+ found_labeled_network = True
+
+ if not found_labeled_network:
+ # search for a dedicated public IP address
+ for port in ps_instance.ports.all():
+ if (not port.ip):
+ continue
+ template = port.network.template
+ if (template.visibility=="public") and (template.translation=="none"):
+ ip=port.ip
+
+ if return_nat:
+ ip = None
+ for port in ps_instance.ports.all():
+ if (not port.ip):
+ continue
+ template = port.network.template
+ if (template.visibility=="private") and (template.translation=="NAT"):
+ ip=port.ip
+ if not ip:
+ continue
+
+ if return_private:
+ ip = None
+ for port in ps_instance.ports.all():
+ if (not port.ip):
+ continue
+ template = port.network.template
+ if (template.visibility=="private") and (template.translation=="none"):
+ ip=port.ip
+ if not ip:
+ continue
+
+ interface = {"node_id": node_id,
+ "ip": ip,
+ "broadcast": None,
+ "mac": "11:22:33:44:55:66",
+ "bwlimit": None,
+ "network": None,
+ "is_primary": True,
+ "dns1": None,
+ "hostname": None,
+ "netmask": None,
+ "interface_tag_ids": [],
+ "interface_id": node_id, # assume each node has only one interface
+ "gateway": None,
+ "dns2": None,
+ "type": "ipv4",
+ "method": "dhcp"}
+ interfaces.append(interface)
+ return interfaces
+
+def GetConfiguration(name, slice_remap={}):
+ slicename = name["name"]
+ if "node_id" in name:
+ node_id = name["node_id"]
+ else:
+ node_id = 0
+
+ node_instance_tags = GetTags(slicename, node_id)
+
+ slices = GetSlices({"name": slicename}, slice_remap=slice_remap)
+ perhost = {}
+ allinterfaces = {}
+ hostprivmap = {}
+ hostipmap = {}
+ hostnatmap = {}
+ nodes = []
+ if len(slices)==1:
+ slice = slices[0]
+ node_ids = slice['node_ids']
+ nodes = GetNodes(node_ids, ['hostname', 'node_id', 'site_id'], slice_remap=slice_remap)
+ nodemap = {}
+ for node in nodes:
+ nodemap[node['node_id']]=node['hostname']
+
+ interfaces = GetInterfaces(slice["planetstack_name"], node_ids)
+ hostipmap = {}
+ for interface in interfaces:
+ if nodemap[interface['node_id']] not in allinterfaces:
+ allinterfaces[nodemap[interface['node_id']]] = []
+ interface['interface_tags'] = []
+ allinterfaces[nodemap[interface['node_id']]].append(interface)
+ if interface['is_primary']:
+ hostipmap[nodemap[interface['node_id']]] = interface['ip']
+
+ hostnatmap = {}
+ interfaces = GetInterfaces(slice["planetstack_name"], node_ids, return_nat=True)
+ for interface in interfaces:
+ interface['interface_tags'] = []
+ hostnatmap[nodemap[interface['node_id']]] = interface['ip']
+
+ hostprivmap = {}
+ interfaces = GetInterfaces(slice["planetstack_name"], node_ids, return_private=True)
+ for interface in interfaces:
+ interface['interface_tags'] = []
+ hostprivmap[nodemap[interface['node_id']]] = interface['ip']
+
+ for nid in node_ids:
+ instance_tags = GetTags(slicename,nid)
+ perhost[nodemap[nid]] = instance_tags
+
+ instances = GetSlices(slice_remap=slice_remap)
+ if node_id != 0:
+ instances = [slice for slice in instances if (node_id in slice.node_ids)]
+
+ sites = GetSites(slice_remap=slice_remap)
+ for site in sites:
+ site["site_tags"] = []
+
+ timestamp = int(time.time())
+ return {'version': 3,
+ 'timestamp': timestamp,
+ 'configuration': node_instance_tags,
+ 'allconfigurations':perhost,
+ 'hostipmap':hostipmap,
+ 'hostnatmap':hostnatmap,
+ 'hostprivmap':hostprivmap,
+ 'slivers': instances,
+ 'interfaces': allinterfaces,
+ 'sites': sites,
+ 'nodes': nodes}
+
+DEFAULT_REMAP = {"princeton_vcoblitz2": ["princeton_vcoblitz", 70]}
+
+def HandleGetConfiguration1():
+ configs={}
+ for slicename in ["princeton_vcoblitz"]:
+ configs[slicename] = GetConfiguration({"name": slicename}, DEFAULT_REMAP)
+ return configs
+
+def HandleGetNodes1():
+ return GetNodes(slice_remap=DEFAULT_REMAP)
+
+def HandleGetSlices1():
+ return GetSlices(slice_remap=DEFAULT_REMAP)
+
+def HandleGetConfiguration2(name, slice_remap):
+ return GetConfiguration(name, slice_remap=slice_remap)
+
+def HandleGetNodes2(slice_remap):
+ return GetNodes(slice_remap=slice_remap)
+
+def HandleGetSlices2(slice_remap):
+ return GetSlices(slice_remap=slice_remap)
+
+FUNCS = {"GetConfiguration": HandleGetConfiguration1,
+ "GetNodes": HandleGetNodes1,
+ "GetSlices": HandleGetSlices1,
+ "GetConfiguration2": HandleGetConfiguration2,
+ "GetNodes2": HandleGetNodes2,
+ "GetSlices2": HandleGetSlices2}
+
+@csrf_exempt
+def LegacyXMLRPC(request):
+ if request.method == "POST":
+ try:
+ (args, method) = xmlrpclib.loads(request.body)
+ result = None
+ if method in FUNCS:
+ result = FUNCS[method](*args)
+ return HttpResponse(xmlrpclib.dumps((result,), methodresponse=True, allow_none=1))
+ except:
+ traceback.print_exc()
+ return HttpResponseServerError()
+ else:
+ return HttpResponse("Not Implemented")
+
+if __name__ == '__main__':
+ slices = GetSlices(slice_remap = DEFAULT_REMAP)
+ nodes = GetNodes(slice_remap = DEFAULT_REMAP)
+
+ config = GetConfiguration({"name": "princeton_vcoblitz"}, slice_remap = DEFAULT_REMAP)
+ print config
+ print slices
+ print nodes
+
diff --git a/xos/filteredadmin.py b/xos/filteredadmin.py
new file mode 100644
index 0000000..b677b54
--- /dev/null
+++ b/xos/filteredadmin.py
@@ -0,0 +1,191 @@
+from django.contrib import admin
+
+from django import forms
+from services.hpc.models import HpcService
+from core.admin import ServiceAppAdmin,SliceInline,ServiceAttrAsTabInline, ReadOnlyAwareAdmin, XOSTabularInline, SliderWidget, ServicePrivilegeInline
+from core.middleware import get_request
+
+from functools import update_wrapper
+from django.contrib.admin.views.main import ChangeList
+from django.core.urlresolvers import reverse, NoReverseMatch
+from django.contrib.admin.utils import quote
+
+import threading
+_thread_locals = threading.local()
+
+class FilteredChangeList(ChangeList):
+ """ A special ChangeList with a doctored url_for_result function that
+ points to the filteredchange view instead of the default change
+ view.
+ """
+
+ def __init__(self, request, *args, **kwargs):
+ self.service = getattr(request, "hpcService", None)
+ self.embedded = getattr(request, "embedded", False)
+ super(FilteredChangeList, self).__init__(request, *args, **kwargs)
+
+ def url_for_result(self, result):
+ if (self.service is None):
+ return super(FilteredChangeList, self).url_for_result(result)
+
+ pk = getattr(result, self.pk_attname)
+ if self.embedded:
+ return reverse('admin:%s_%s_embeddedfilteredchange' % (self.opts.app_label,
+ self.opts.model_name),
+ args=(quote(self.service.id), quote(pk),),
+ current_app=self.model_admin.admin_site.name)
+
+ else:
+ return reverse('admin:%s_%s_filteredchange' % (self.opts.app_label,
+ self.opts.model_name),
+ args=(quote(self.service.id), quote(pk),),
+ current_app=self.model_admin.admin_site.name)
+
+class FilteredAdmin(ReadOnlyAwareAdmin):
+ """
+ One approach to filtering the HPC Admin views by HPCService. Encode
+ the HPCService into the URL for the changelist view. Then we could do our
+ custom filtering in self.filtered_changelist_view.
+
+ To make this work, a few changes needed to be made to the change and
+ change_list templates.
+
+ 1) "custom_changelist_breadcrumb_url" is used to replace the breadcrumb
+ in change and add views with one that will point back to the filtered
+ list.
+
+ 2) "custom_add_url" is used to replace the Add button's URL with one
+ that points to the filtered add view.
+
+ TODO: Save & Add Another,
+ the add link when the changelist is empty
+ """
+
+ @property
+ def change_list_template(self):
+ return _thread_locals.change_list_template
+
+ @property
+ def change_form_template(self):
+ return _thread_locals.change_form_template
+
+ def get_urls(self):
+ from django.conf.urls import patterns, url
+
+ def wrap(view):
+ def wrapper(*args, **kwargs):
+ return self.admin_site.admin_view(view)(*args, **kwargs)
+ return update_wrapper(wrapper, view)
+
+ urls = super(FilteredAdmin, self).get_urls()
+ info = self.model._meta.app_label, self.model._meta.model_name
+ my_urls = [
+ url(r'^(.+)/filteredlist/$', wrap(self.filtered_changelist_view), name="%s_%s_filteredchangelist" % info),
+ url(r'^(.+)/embeddedfilteredlist/$', wrap(self.embedded_filtered_changelist_view), name="%s_%s_embeddedfilteredchangelist" % info),
+ url(r'^(.+)/(.+)/filteredchange$', wrap(self.filtered_change_view), name='%s_%s_filteredchange' % info),
+ url(r'^(.+)/(.+)/embeddedfilteredchange$', wrap(self.embedded_filtered_change_view), name='%s_%s_embeddedfilteredchange' % info),
+ url(r'^(.+)/filteredadd/$', wrap(self.filtered_add_view), name='%s_%s_filteredadd' % info),
+ url(r'^(.+)/embeddedfilteredadd/$', wrap(self.embedded_filtered_add_view), name='%s_%s_embeddedfilteredadd' % info),
+ ]
+ return my_urls + urls
+
+ def add_extra_context(self, request, extra_context):
+ super(FilteredAdmin, self).add_extra_context(request, extra_context)
+
+ if getattr(request,"hpcService",None) is not None:
+ extra_context["custom_changelist_breadcrumb_url"] = "/admin/hpc/%s/%s/filteredlist/" % (self.model._meta.model_name, str(request.hpcService.id))
+ if getattr(request,"embedded",False):
+ extra_context["custom_add_url"] = "/admin/hpc/%s/%s/embeddedfilteredadd/" % (self.model._meta.model_name, str(request.hpcService.id))
+ else:
+ extra_context["custom_add_url"] = "/admin/hpc/%s/%s/filteredadd/" % (self.model._meta.model_name, str(request.hpcService.id))
+ if len(request.resolver_match.args)>1:
+ # this is only useful on change views, not changelist views
+ extra_context["custom_delete_url"] = "/admin/hpc/%s/%s/delete/" % (self.model._meta.model_name, request.resolver_match.args[1])
+
+ extra_context["show_save"] = False
+ extra_context["show_save_and_add_another"] = False
+
+ def changelist_view(self, *args, **kwargs):
+ if "template" in kwargs:
+ _thread_locals.change_list_template = kwargs["template"]
+ del kwargs["template"]
+ else:
+ _thread_locals.change_list_template = "admin/change_list_bc.html"
+ return super(FilteredAdmin, self).changelist_view(*args, **kwargs)
+
+ def filtered_changelist_view(self, request, hpcServiceId, extra_context=None):
+ request.hpcService = HpcService.objects.get(id=hpcServiceId)
+ return self.changelist_view(request, extra_context=extra_context)
+
+ def embedded_filtered_changelist_view(self, request, hpcServiceId, extra_context=None):
+ request.hpcService = HpcService.objects.get(id=hpcServiceId)
+ request.embedded = True
+ return self.changelist_view(request, template="admin/change_list_embedded.html", extra_context=extra_context)
+
+ def change_view(self, *args, **kwargs):
+ if "template" in kwargs:
+ _thread_locals.change_form_template = kwargs["template"]
+ del kwargs["template"]
+ else:
+ _thread_locals.change_form_template = "admin/change_form_bc.html"
+ return super(FilteredAdmin, self).change_view(*args, **kwargs)
+
+ def filtered_change_view(self, request, hpcServiceId, object_id, extra_context=None):
+ request.hpcService = HpcService.objects.get(id=hpcServiceId)
+ return self.change_view(request, object_id, extra_context=extra_context)
+
+ def embedded_filtered_change_view(self, request, hpcServiceId, object_id, extra_context=None):
+ request.hpcService = HpcService.objects.get(id=hpcServiceId)
+ request.embedded = True
+ return self.change_view(request, object_id, template="admin/change_form_embedded.html", extra_context=extra_context)
+
+ def add_view(self, *args, **kwargs):
+ if "template" in kwargs:
+ _thread_locals.change_form_template = kwargs["template"]
+ del kwargs["template"]
+ else:
+ _thread_locals.change_form_template = "admin/change_form_bc.html"
+ return super(FilteredAdmin, self).add_view(*args, **kwargs)
+
+ def filtered_add_view(self, request, hpcServiceId, extra_context=None):
+ request.hpcService = HpcService.objects.get(id=hpcServiceId)
+ return self.add_view(request, extra_context=extra_context)
+
+ def embedded_filtered_add_view(self, request, hpcServiceId, extra_context=None):
+ request.hpcService = HpcService.objects.get(id=hpcServiceId)
+ request.embedded = True
+ return self.add_view(request, template="admin/change_form_embedded.html", extra_context=extra_context)
+
+ def get_queryset(self, request):
+ # request.hpcService will be set in filtered_changelist_view so we can
+ # use it to filter what will be displayed in the list.
+ qs = self.model.objects.all()
+ if (getattr(request,"hpcService",None) is not None) and (hasattr(self.model, "filter_by_hpcService")):
+ qs = self.model.filter_by_hpcService(qs, request.hpcService)
+ return qs
+
+ def get_changelist(self, request, **kwargs):
+ # We implement a custom ChangeList, so the URLs point to the
+ # filtered_change_view rather than the default change_view.
+ return FilteredChangeList
+
+class FilteredInline(XOSTabularInline):
+ def get_change_url(self, id):
+ request = get_request()
+ embedded = getattr(request, "embedded", False)
+ service_id = request.resolver_match.args[0]
+
+ if embedded:
+ reverse_path = "admin:%s_embeddedfilteredchange" % (self.selflink_model._meta.db_table)
+ args = (service_id, id)
+ else:
+ reverse_path = "admin:%s_filteredchange" % (self.selflink_model._meta.db_table)
+ args = (service_id, id)
+
+ try:
+ url = reverse(reverse_path, args=args, current_app=self.selflink_model._meta.app_label)
+ except NoReverseMatch:
+ return None
+
+ return url
+
diff --git a/xos/hpc-onboard.yaml b/xos/hpc-onboard.yaml
new file mode 100644
index 0000000..07a1745
--- /dev/null
+++ b/xos/hpc-onboard.yaml
@@ -0,0 +1,25 @@
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+description: Onboard the CDN service
+
+imports:
+ - custom_types/xos.yaml
+
+topology_template:
+ node_templates:
+ servicecontroller#hpc:
+ type: tosca.nodes.ServiceController
+ properties:
+ base_url: file:///opt/xos_services/hypercache/xos/
+ models: models.py
+ admin: admin.py
+ admin_template: templates/hpcadmin.html templates/hpctools.html
+ synchronizer: synchronizer/manifest
+ synchronizer_run: hpc-synchronizer.py
+ #tosca_custom_types: exampleservice.yaml
+ #tosca_resource: tosca/resources/vcpeservice.py
+ #rest_service: subdirectory:vsg api/service/vsg/vsgservice.py
+ #rest_tenant: subdirectory:cord api/tenant/cord/vsg.py
+ #private_key: file:///opt/xos/key_import/vsg_rsa
+ #public_key: file:///opt/xos/key_import/vsg_rsa.pub
+
diff --git a/xos/make_synchronizer_manifest.sh b/xos/make_synchronizer_manifest.sh
new file mode 100644
index 0000000..4058982
--- /dev/null
+++ b/xos/make_synchronizer_manifest.sh
@@ -0,0 +1,2 @@
+#! /bin/bash
+find synchronizer -type f | cut -b 14- > synchronizer/manifest
diff --git a/xos/models.py b/xos/models.py
new file mode 100644
index 0000000..e49bf4e
--- /dev/null
+++ b/xos/models.py
@@ -0,0 +1,229 @@
+from django.db import models
+from core.models import User, Service, SingletonModel, PlCoreBase
+from core.models.plcorebase import StrippedCharField
+import os
+from django.db import models
+from django.forms.models import model_to_dict
+from django.db.models import Q
+
+
+# Create your models here.
+
+class HpcService(Service):
+
+ class Meta:
+ app_label = "hpc"
+ verbose_name = "HPC Service"
+
+ cmi_hostname = StrippedCharField(max_length=254, null=True, blank=True)
+
+ hpc_port80 = models.BooleanField(default=True, help_text="Enable port 80 for HPC")
+ watcher_hpc_network = StrippedCharField(max_length=254, null=True, blank=True, help_text="Network for hpc_watcher to contact hpc instance")
+ watcher_dnsdemux_network = StrippedCharField(max_length=254, null=True, blank=True, help_text="Network for hpc_watcher to contact dnsdemux instance")
+ watcher_dnsredir_network = StrippedCharField(max_length=254, null=True, blank=True, help_text="Network for hpc_watcher to contact dnsredir instance")
+
+ @property
+ def scale(self):
+ hpc_slices = [x for x in self.slices.all() if "hpc" in x.name]
+ if not hpc_slices:
+ return 0
+ return hpc_slices[0].instances.count()
+
+ @scale.setter
+ def scale(self, value):
+ self.set_scale = value
+
+ def save(self, *args, **kwargs):
+ super(HpcService, self).save(*args, **kwargs)
+
+ # scale up/down
+ scale = getattr(self, "set_scale", None)
+ if scale is not None:
+ exclude_slices = [x for x in self.slices.all() if "cmi" in x.name]
+ self.adjust_scale(slice_hint="hpc", scale=scale, exclusive_slices = exclude_slices, max_per_node=1)
+
+class ServiceProvider(PlCoreBase):
+ class Meta:
+ app_label = "hpc"
+
+ hpcService = models.ForeignKey(HpcService)
+ service_provider_id = models.IntegerField(null=True, blank=True)
+ name = models.CharField(max_length=254,help_text="Service Provider Name")
+ description = models.TextField(max_length=254,null=True, blank=True, help_text="Description of Service Provider")
+ enabled = models.BooleanField(default=True)
+
+ def __unicode__(self): return u'%s' % (self.name)
+
+ @classmethod
+ def filter_by_hpcService(cls, qs, hpcService):
+ # This should be overridden by descendant classes that want to perform
+ # filtering of visible objects by user.
+ return qs.filter(hpcService=hpcService)
+
+class ContentProvider(PlCoreBase):
+ class Meta:
+ app_label = "hpc"
+
+ # legacy vicci content providers already have names.
+ CP_TO_ACCOUNT = {"ON.LAB": "onlabcp",
+ "Syndicate": "syndicatecp"}
+
+ content_provider_id = models.IntegerField(null=True, blank=True)
+ name = models.CharField(max_length=254)
+ enabled = models.BooleanField(default=True)
+ description = models.TextField(max_length=254,null=True, blank=True,help_text="Description of Content Provider")
+ serviceProvider = models.ForeignKey(ServiceProvider)
+
+ # Note user relationships are directed not requiring a role.
+ users = models.ManyToManyField(User)
+
+ def __unicode__(self): return u'%s' % (self.name)
+
+ @property
+ def account(self):
+ return self.CP_TO_ACCOUNT.get(self.name, self.name)
+
+ @classmethod
+ def filter_by_hpcService(cls, qs, hpcService):
+ # This should be overridden by descendant classes that want to perform
+ # filtering of visible objects by user.
+ return qs.filter(serviceProvider__hpcService=hpcService)
+
+ def can_update(self, user):
+ if super(ContentProvider, self).can_update(user):
+ return True
+
+ if user in self.users.all():
+ return True
+
+ return False
+
+class OriginServer(PlCoreBase):
+ class Meta:
+ app_label = "hpc"
+
+ origin_server_id = models.IntegerField(null=True, blank=True)
+ url = models.CharField(max_length=1024)
+ contentProvider = models.ForeignKey(ContentProvider)
+
+ authenticated = models.BooleanField(default=False, help_text="Status for this Site")
+ enabled = models.BooleanField(default=True, help_text="Status for this Site")
+ PROTOCOL_CHOICES = (('http', 'HTTP'),('rtmp', 'RTMP'), ('rtp', 'RTP'),('shout', 'SHOUTcast'))
+ protocol = models.CharField(default="HTTP", max_length = 12, choices=PROTOCOL_CHOICES)
+ redirects = models.BooleanField(default=True, help_text="Indicates whether Origin Server redirects should be used for this Origin Server")
+ description = models.TextField(null=True, blank=True, max_length=255)
+
+ def __unicode__(self): return u'%s' % (self.url)
+
+ @classmethod
+ def filter_by_hpcService(cls, qs, hpcService):
+ # This should be overridden by descendant classes that want to perform
+ # filtering of visible objects by user.
+ return qs.filter(contentProvider__serviceProvider__hpcService=hpcService)
+
+ def can_update(self, user):
+ if super(OriginServer, self).can_update(user):
+ return True
+
+ if self.contentProvider and self.contentProvider.can_update(user):
+ return True
+
+ return False
+
+class CDNPrefix(PlCoreBase):
+ class Meta:
+ app_label = "hpc"
+
+ cdn_prefix_id = models.IntegerField(null=True, blank=True)
+ prefix = models.CharField(max_length=200, help_text="Registered Prefix for Domain")
+ contentProvider = models.ForeignKey(ContentProvider)
+ description = models.TextField(max_length=254,null=True, blank=True,help_text="Description of Content Provider")
+
+ defaultOriginServer = models.ForeignKey(OriginServer, blank=True, null=True)
+ enabled = models.BooleanField(default=True)
+
+ def __unicode__(self): return u'%s' % (self.prefix)
+
+ @classmethod
+ def filter_by_hpcService(cls, qs, hpcService):
+ # This should be overridden by descendant classes that want to perform
+ # filtering of visible objects by user.
+ return qs.filter(contentProvider__serviceProvider__hpcService=hpcService)
+
+ def can_update(self, user):
+ if super(CDNPrefix, self).can_update(user):
+ return True
+
+ if self.contentProvider and self.contentProvider.can_update(user):
+ return True
+
+ return False
+
+class AccessMap(PlCoreBase):
+ class Meta:
+ app_label = "hpc"
+
+ contentProvider = models.ForeignKey(ContentProvider)
+ name = models.CharField(max_length=64, help_text="Name of the Access Map")
+ description = models.TextField(null=True, blank=True,max_length=130)
+ map = models.FileField(upload_to="maps/", help_text="specifies which client requests are allowed")
+
+ def __unicode__(self): return self.name
+
+class SiteMap(PlCoreBase):
+ class Meta:
+ app_label = "hpc"
+
+ """ can be bound to a ContentProvider, ServiceProvider, or neither """
+ contentProvider = models.ForeignKey(ContentProvider, blank=True, null=True)
+ serviceProvider = models.ForeignKey(ServiceProvider, blank=True, null=True)
+ cdnPrefix = models.ForeignKey(CDNPrefix, blank = True, null=True)
+ hpcService = models.ForeignKey(HpcService, blank = True, null=True)
+ name = models.CharField(max_length=64, help_text="Name of the Site Map")
+ description = models.TextField(null=True, blank=True,max_length=130)
+ map = models.FileField(upload_to="maps/", help_text="specifies how to map requests to hpc instances")
+ map_id = models.IntegerField(null=True, blank=True)
+
+ def __unicode__(self): return self.name
+
+ def save(self, *args, **kwds):
+ if (self.contentProvider) and (self.serviceProvider or self.cdnPrefix or self.hpcService):
+ raise ValueError("You may only set one of contentProvider, serviceProvider, cdnPrefix, or hpcService")
+ if (self.serviceProvider) and (self.cdnPrefix or self.hpcService):
+ raise ValueError("You may only set one of contentProvider, serviceProvider, cdnPrefix, or hpcService")
+ if (self.cdnPrefix) and (self.hpcService):
+ raise ValueError("You may only set one of contentProvider, serviceProvider, cdnPrefix, or hpcService")
+
+ super(SiteMap, self).save(*args, **kwds)
+
+ @classmethod
+ def filter_by_hpcService(cls, qs, hpcService):
+ # This should be overridden by descendant classes that want to perform
+ # filtering of visible objects by user.
+ return qs.filter(Q(hpcService=hpcService) |
+ Q(serviceProvider__hpcService=hpcService) |
+ Q(contentProvider__serviceProvider__hpcService=hpcService) |
+ Q(cdnPrefix__contentProvider__serviceProvider__hpcService=hpcService))
+
+class HpcHealthCheck(PlCoreBase):
+ class Meta:
+ app_label = "hpc"
+
+ KIND_CHOICES = (('dns', 'DNS'), ('http', 'HTTP'), ('nameserver', 'Name Server'))
+
+ hpcService = models.ForeignKey(HpcService, blank = True, null=True)
+ kind = models.CharField(max_length=30, choices=KIND_CHOICES, default="dns")
+ resource_name = StrippedCharField(max_length=1024, blank=False, null=False)
+ result_contains = StrippedCharField(max_length=1024, blank=True, null=True)
+ result_min_size = models.IntegerField(null=True, blank=True)
+ result_max_size = models.IntegerField(null=True, blank=True)
+
+ def __unicode__(self): return self.resource_name
+
+ @classmethod
+ def filter_by_hpcService(cls, qs, hpcService):
+ # This should be overridden by descendant classes that want to perform
+ # filtering of visible objects by user.
+ return qs.filter(hpcService=hpcService)
+
+
diff --git a/xos/synchronizer/fsck.py b/xos/synchronizer/fsck.py
new file mode 100644
index 0000000..448bfb7
--- /dev/null
+++ b/xos/synchronizer/fsck.py
@@ -0,0 +1,79 @@
+#!/usr/bin/env python
+import argparse
+import imp
+import inspect
+import os
+import sys
+os.environ.setdefault("DJANGO_SETTINGS_MODULE", "xos.settings")
+sys.path.append("/opt/xos")
+from xos.config import Config, DEFAULT_CONFIG_FN, XOS_DIR
+from xos.logger import Logger, logging
+from synchronizers.base.syncstep import SyncStep
+
+try:
+ from django import setup as django_setup # django 1.7
+except:
+ django_setup = False
+
+logger = Logger(level=logging.INFO)
+
+class XOSConsistencyCheck:
+ def __init__(self):
+ self.sync_steps = []
+ self.load_sync_step_modules()
+
+ def load_sync_step_modules(self, step_dir=None):
+ if step_dir is None:
+ if hasattr(Config(), "observer_steps_dir"):
+ step_dir = Config().observer_steps_dir
+ else:
+ step_dir = XOS_DIR+"/observer/steps"
+
+ for fn in os.listdir(step_dir):
+ pathname = os.path.join(step_dir,fn)
+ if os.path.isfile(pathname) and fn.endswith(".py") and (fn!="__init__.py"):
+ module = imp.load_source(fn[:-3],pathname)
+ for classname in dir(module):
+ c = getattr(module, classname, None)
+
+ # make sure 'c' is a descendent of SyncStep and has a
+ # provides field (this eliminates the abstract base classes
+ # since they don't have a provides)
+
+ if inspect.isclass(c) and issubclass(c, SyncStep) and hasattr(c,"provides") and (c not in self.sync_steps):
+ self.sync_steps.append(c)
+ logger.info('loaded sync steps: %s' % ",".join([x.__name__ for x in self.sync_steps]))
+
+ def run(self):
+ updated = True
+ while updated:
+ updated = False
+
+ for step in self.sync_steps:
+ if hasattr(step, "consistency_check"):
+ updated = updated or step(driver=None).consistency_check()
+
+ if updated:
+ logger.info('re-running consistency checks because something changed')
+
+def main():
+ if not "-C" in sys.argv:
+ print >> sys.stderr, "You probably wanted to use -C " + XOS_DIR + "/hpc_observer/hpc_observer_config"
+
+ # Generate command line parser
+ parser = argparse.ArgumentParser(usage='%(prog)s [options]')
+ # smbaker: util/config.py parses sys.argv[] directly to get config file name; include the option here to avoid
+ # throwing unrecognized argument exceptions
+ parser.add_argument('-C', '--config', dest='config_file', action='store', default=DEFAULT_CONFIG_FN,
+ help='Name of config file.')
+ args = parser.parse_args()
+
+ if django_setup: # 1.7
+ django_setup()
+
+ cc = XOSConsistencyCheck()
+ cc.run()
+
+if __name__ == '__main__':
+ main()
+
diff --git a/xos/synchronizer/hpc-synchronizer.py b/xos/synchronizer/hpc-synchronizer.py
new file mode 100755
index 0000000..84bec4f
--- /dev/null
+++ b/xos/synchronizer/hpc-synchronizer.py
@@ -0,0 +1,11 @@
+#!/usr/bin/env python
+
+# This imports and runs ../../xos-observer.py
+
+import importlib
+import os
+import sys
+observer_path = os.path.join(os.path.dirname(os.path.realpath(__file__)),"../../synchronizers/base")
+sys.path.append(observer_path)
+mod = importlib.import_module("xos-synchronizer")
+mod.main()
diff --git a/xos/synchronizer/hpc_synchronizer_config b/xos/synchronizer/hpc_synchronizer_config
new file mode 100644
index 0000000..9d4e70a
--- /dev/null
+++ b/xos/synchronizer/hpc_synchronizer_config
@@ -0,0 +1,36 @@
+
+[plc]
+name=plc
+deployment=VICCI
+
+[db]
+name=xos
+user=postgres
+password=password
+host=localhost
+port=5432
+
+[api]
+host=128.112.171.237
+port=8000
+ssl_key=None
+ssl_cert=None
+ca_ssl_cert=None
+ratelimit_enabled=0
+omf_enabled=0
+mail_support_address=support@localhost
+nova_enabled=True
+
+[observer]
+name=hpc
+dependency_graph=/opt/xos/synchronizers/hpc/model-deps
+steps_dir=/opt/xos/synchronizers/hpc/steps
+deleters_dir=/opt/xos/synchronizers/hpc/deleters
+log_file=console
+#/var/log/hpc.log
+driver=None
+#cmi_hostname=openclouddev0.internet2.edu
+
+[feefie]
+client_id='vicci_dev_central'
+user_id='pl'
diff --git a/xos/synchronizer/hpc_watcher.py b/xos/synchronizer/hpc_watcher.py
new file mode 100644
index 0000000..d2efdcc
--- /dev/null
+++ b/xos/synchronizer/hpc_watcher.py
@@ -0,0 +1,629 @@
+"""
+ hpc_watcher.py
+
+ Daemon to watch the health of HPC and RR instances.
+
+ This deamon uses HpcHealthCheck objects in the Data Model to conduct
+ periodic tests of HPC and RR nodes. Two types of Health Checks are
+ supported:
+
+ kind="dns": checks the request routers to make sure that a DNS
+ name is resolveable and returns the right kind of records.
+
+ resource_name should be set to the domain name to lookup.
+
+ result_contains is option and can be used to hold "A", "CNAME", or
+ a particular address or hostname that should be contained in the
+ query's answer.
+
+ kind="http": checks the hpc nodes to make sure that a URL can be
+ retrieved from the node.
+
+ resource_name should be set to the HostName:Url to fetch. For
+ example, cdn-stream.htm.fiu.edu:/hft2441/intro.mp4
+
+ In addition to the above, HPC heartbeat probes are conducted, similar to
+ the ones that dnsredir conducts.
+
+ The results of health checks are stored in a tag attached to the Instance
+ the healthcheck was conducted against. If all healthchecks of a particular
+ variety were successful for a instance, then "success" will be stored in
+ the tag. Otherwise, the first healthcheck to fail will be stored in the
+ tag.
+
+ Ubuntu prereqs:
+ apt-get install python-pycurl
+ pip install dnslib
+"""
+
+import os
+import socket
+import sys
+sys.path.append("/opt/xos")
+os.environ.setdefault("DJANGO_SETTINGS_MODULE", "xos.settings")
+import django
+from django.contrib.contenttypes.models import ContentType
+from core.models import *
+from services.hpc.models import *
+from services.requestrouter.models import *
+django.setup()
+import time
+import pycurl
+import traceback
+import json
+from StringIO import StringIO
+
+from dnslib.dns import DNSRecord,DNSHeader,DNSQuestion,QTYPE
+from dnslib.digparser import DigParser
+
+from threading import Thread, Condition
+
+"""
+from dnslib import *
+q = DNSRecord(q=DNSQuestion("cdn-stream.htm.fiu.edu"))
+a_pkt = q.send("150.135.65.10", tcp=False, timeout=10)
+a = DNSRecord.parse(a_pkt)
+
+from dnslib import *
+q = DNSRecord(q=DNSQuestion("onlab.vicci.org"))
+a_pkt = q.send("150.135.65.10", tcp=False, timeout=10)
+a = DNSRecord.parse(a_pkt)
+"""
+
+class WorkQueue:
+ def __init__(self):
+ self.job_cv = Condition()
+ self.jobs = []
+ self.result_cv = Condition()
+ self.results = []
+ self.outstanding = 0
+
+ def get_job(self):
+ self.job_cv.acquire()
+ while not self.jobs:
+ self.job_cv.wait()
+ result = self.jobs.pop()
+ self.job_cv.release()
+ return result
+
+ def submit_job(self, job):
+ self.job_cv.acquire()
+ self.jobs.append(job)
+ self.job_cv.notify()
+ self.job_cv.release()
+ self.outstanding = self.outstanding + 1
+
+ def get_result(self):
+ self.result_cv.acquire()
+ while not self.results:
+ self.result_cv.wait()
+ result = self.results.pop()
+ self.result_cv.release()
+ self.outstanding = self.outstanding - 1
+ return result
+
+ def submit_result(self, result):
+ self.result_cv.acquire()
+ self.results.append(result)
+ self.result_cv.notify()
+ self.result_cv.release()
+
+class DnsResolver(Thread):
+ def __init__(self, queue):
+ Thread.__init__(self)
+ self.queue = queue
+ self.daemon = True
+ self.start()
+
+ def run(self):
+ while True:
+ job = self.queue.get_job()
+ self.handle_job(job)
+ self.queue.submit_result(job)
+
+ def handle_job(self, job):
+ domain = job["domain"]
+ server = job["server"]
+ port = job["port"]
+ result_contains = job.get("result_contains", None)
+
+ try:
+ q = DNSRecord(q=DNSQuestion(domain)) #, getattr(QTYPE,"A")))
+
+ a_pkt = q.send(server, port, tcp=False, timeout=10)
+ a = DNSRecord.parse(a_pkt)
+
+ found_record = False
+ for record in a.rr:
+ if (not result_contains):
+ QTYPE_A = getattr(QTYPE,"A")
+ QTYPE_CNAME = getattr(QTYPE, "CNAME")
+ if ((record.rtype==QTYPE_A) or (record.qtype==QTYPE_CNAME)):
+ found_record = True
+ else:
+ tmp = QTYPE.get(record.rtype) + str(record.rdata)
+ if (result_contains in tmp):
+ found_record = True
+
+ if not found_record:
+ if result_contains:
+ job["status"] = "%s,No %s records" % (domain, result_contains)
+ else:
+ job["status"] = "%s,No A or CNAME records" % domain
+
+ return
+
+ except Exception, e:
+ job["status"] = "%s,Exception: %s" % (domain, str(e))
+ return
+
+ job["status"] = "success"
+
+class HpcHeartbeat(Thread):
+ def __init__(self, queue):
+ Thread.__init__(self)
+ self.queue = queue
+ self.daemon = True
+ self.start()
+
+ def run(self):
+ while True:
+ job = self.queue.get_job()
+ self.handle_job(job)
+ self.queue.submit_result(job)
+
+ def curl_error_message(self, e):
+ if e.args[0] == 6:
+ return "couldn't resolve host"
+ if e.args[0] == 7:
+ return "failed to connect"
+ return "curl error %d" % e.args[0]
+
+ def handle_job(self, job):
+ server = job["server"]
+ port = job["port"]
+
+ try:
+ buffer = StringIO()
+ c = pycurl.Curl()
+
+ c.setopt(c.URL, "http://%s:%s/heartbeat" % (server, port))
+ c.setopt(c.WRITEDATA, buffer)
+ c.setopt(c.HTTPHEADER, ['host: hpc-heartbeat', 'X-heartbeat: 1'])
+ c.setopt(c.TIMEOUT, 10)
+ c.setopt(c.CONNECTTIMEOUT, 10)
+ c.setopt(c.NOSIGNAL, 1)
+
+ try:
+ c.perform()
+ response_code = c.getinfo(c.RESPONSE_CODE)
+ except Exception, e:
+ #traceback.print_exc()
+ job["status"] = self.curl_error_message(e)
+ return
+ finally:
+ c.close()
+
+ if response_code != 200:
+ job["status"] = "error response %d" % response_code
+ return
+
+ except Exception, e:
+ job["status"] = "Exception: %s" % str(e)
+ return
+
+ job["status"] = "success"
+
+class HpcFetchUrl(Thread):
+ def __init__(self, queue):
+ Thread.__init__(self)
+ self.queue = queue
+ self.daemon = True
+ self.start()
+
+ def run(self):
+ while True:
+ job = self.queue.get_job()
+ self.handle_job(job)
+ self.queue.submit_result(job)
+
+ def curl_error_message(self, e):
+ if e.args[0] == 6:
+ return "couldn't resolve host"
+ if e.args[0] == 7:
+ return "failed to connect"
+ return "curl error %d" % e.args[0]
+
+ def handle_job(self, job):
+ server = job["server"]
+ port = job["port"]
+ url = job["url"]
+ domain = job["domain"]
+
+ def progress(download_t, download_d, upload_t, upload_d):
+ # limit download size to a megabyte
+ if (download_d > 1024*1024):
+ return 1
+ else:
+ return 0
+
+ try:
+ buffer = StringIO()
+ c = pycurl.Curl()
+
+ c.setopt(c.URL, "http://%s:%s/%s" % (server, port, url))
+ c.setopt(c.WRITEDATA, buffer)
+ c.setopt(c.HTTPHEADER, ['host: ' + domain])
+ c.setopt(c.TIMEOUT, 10)
+ c.setopt(c.CONNECTTIMEOUT, 10)
+ c.setopt(c.NOSIGNAL, 1)
+ c.setopt(c.NOPROGRESS, 0)
+ c.setopt(c.PROGRESSFUNCTION, progress)
+
+ try:
+ try:
+ c.perform()
+ except Exception, e:
+ # prevent callback abort from raising exception
+ if (e.args[0] != pycurl.E_ABORTED_BY_CALLBACK):
+ raise
+ response_code = c.getinfo(c.RESPONSE_CODE)
+ bytes_downloaded = int(c.getinfo(c.SIZE_DOWNLOAD))
+ total_time = float(c.getinfo(c.TOTAL_TIME))
+ except Exception, e:
+ #traceback.print_exc()
+ job["status"] = self.curl_error_message(e)
+ return
+ finally:
+ c.close()
+
+ if response_code != 200:
+ job["status"] = "error response %s" % str(response_code)
+ return
+
+ except Exception, e:
+ #traceback.print_exc()
+ job["status"] = "Exception: %s" % str(e)
+ return
+
+ job["status"] = "success"
+ job["bytes_downloaded"] = bytes_downloaded
+ job["total_time"] = total_time
+
+class WatcherWorker(Thread):
+ def __init__(self, queue):
+ Thread.__init__(self)
+ self.queue = queue
+ self.daemon = True
+ self.start()
+
+ def run(self):
+ while True:
+ job = self.queue.get_job()
+ self.handle_job(job)
+ self.queue.submit_result(job)
+
+ def curl_error_message(self, e):
+ if e.args[0] == 6:
+ return "couldn't resolve host"
+ if e.args[0] == 7:
+ return "failed to connect"
+ return "curl error %d" % e.args[0]
+
+ def handle_job(self, job):
+ server = job["server"]
+ port = job["port"]
+
+ try:
+ buffer = StringIO()
+ c = pycurl.Curl()
+
+ c.setopt(c.URL, "http://%s:%s/" % (server, port))
+ c.setopt(c.WRITEDATA, buffer)
+ c.setopt(c.TIMEOUT, 10)
+ c.setopt(c.CONNECTTIMEOUT, 10)
+ c.setopt(c.NOSIGNAL, 1)
+
+ try:
+ c.perform()
+ response_code = c.getinfo(c.RESPONSE_CODE)
+ except Exception, e:
+ #traceback.print_exc()
+ job["status"] = json.dumps( {"status": self.curl_error_message(e)} )
+ return
+ finally:
+ c.close()
+
+ if response_code != 200:
+ job["status"] = json.dumps( {"status": "error response %d" % response_code} )
+ return
+
+ d = json.loads(buffer.getvalue())
+ d["status"] = "success";
+ job["status"] = json.dumps(d)
+
+ except Exception, e:
+ job["status"] = json.dumps( {"status": "Exception: %s" % str(e)} )
+ return
+
+class BaseWatcher(Thread):
+ def __init__(self):
+ Thread.__init__(self)
+ self.daemon = True
+
+ def get_public_ip(self, service, instance):
+ network_name = None
+ if "hpc" in instance.slice.name:
+ network_name = getattr(service, "watcher_hpc_network", None)
+ elif "demux" in instance.slice.name:
+ network_name = getattr(service, "watcher_dnsdemux_network", None)
+ elif "redir" in instance.slice.name:
+ network_name = getattr(service, "watcher_dnsredir_network", None)
+
+ if network_name and network_name.lower()=="nat":
+ return None
+
+ if (network_name is None) or (network_name=="") or (network_name.lower()=="public"):
+ return instance.get_public_ip()
+
+ for ns in instance.ports.all():
+ if (ns.ip) and (ns.network.name==network_name):
+ return ns.ip
+
+ raise ValueError("Couldn't find network %s" % str(network_name))
+
+ def set_status(self, instance, service, kind, msg, check_error=True):
+ #print instance.node.name, kind, msg
+ if check_error:
+ instance.has_error = (msg!="success")
+
+ instance_type = ContentType.objects.get_for_model(instance)
+
+ t = Tag.objects.filter(service=service, name=kind+".msg", content_type__pk=instance_type.id, object_id=instance.id)
+ if t:
+ t=t[0]
+ if (t.value != msg):
+ t.value = msg
+ t.save()
+ else:
+ Tag(service=service, name=kind+".msg", content_object = instance, value=msg).save()
+
+ t = Tag.objects.filter(service=service, name=kind+".time", content_type__pk=instance_type.id, object_id=instance.id)
+ if t:
+ t=t[0]
+ t.value = str(time.time())
+ t.save()
+ else:
+ Tag(service=service, name=kind+".time", content_object = instance, value=str(time.time())).save()
+
+ def get_service_slices(self, service, kind=None):
+ try:
+ slices = service.slices.all()
+ except:
+ # buggy data model
+ slices = service.service.all()
+
+ if kind:
+ return [x for x in slices if (kind in x.name)]
+ else:
+ return list(slices)
+
+class RRWatcher(BaseWatcher):
+ def __init__(self):
+ BaseWatcher.__init__(self)
+
+ self.resolver_queue = WorkQueue()
+ for i in range(0,10):
+ DnsResolver(queue = self.resolver_queue)
+
+ def check_request_routers(self, service, instances):
+ for instance in instances:
+ instance.has_error = False
+
+ try:
+ ip = self.get_public_ip(service, instance)
+ except Exception, e:
+ self.set_status(instance, service, "watcher.DNS", "exception: %s" % str(e))
+ continue
+ if not ip:
+ try:
+ ip = socket.gethostbyname(instance.node.name)
+ except:
+ self.set_status(instance, service, "watcher.DNS", "dns resolution failure")
+ continue
+
+ if not ip:
+ self.set_status(instance, service, "watcher.DNS", "no IP address")
+ continue
+
+ checks = HpcHealthCheck.objects.filter(kind="dns")
+ if not checks:
+ self.set_status(instance, service, "watcher.DNS", "no DNS HealthCheck tests configured")
+
+ for check in checks:
+ self.resolver_queue.submit_job({"domain": check.resource_name, "server": ip, "port": 53, "instance": instance, "result_contains": check.result_contains})
+
+ while self.resolver_queue.outstanding > 0:
+ result = self.resolver_queue.get_result()
+ instance = result["instance"]
+ if (result["status"]!="success") and (not instance.has_error):
+ self.set_status(instance, service, "watcher.DNS", result["status"])
+
+ for instance in instances:
+ if not instance.has_error:
+ self.set_status(instance, service, "watcher.DNS", "success")
+
+ def run_once(self):
+ for hpcService in HpcService.objects.all():
+ for slice in self.get_service_slices(hpcService, "dnsdemux"):
+ self.check_request_routers(hpcService, slice.instances.all())
+
+ for rrService in RequestRouterService.objects.all():
+ for slice in self.get_service_slices(rrService, "dnsdemux"):
+ self.check_request_routers(rrService, slice.instances.all())
+
+ def run(self):
+ while True:
+ self.run_once()
+ time.sleep(10)
+
+ django.db.reset_queries()
+
+class HpcProber(BaseWatcher):
+ def __init__(self):
+ BaseWatcher.__init__(self)
+
+ self.heartbeat_queue = WorkQueue()
+ for i in range(0, 10):
+ HpcHeartbeat(queue = self.heartbeat_queue)
+
+ def probe_hpc(self, service, instances):
+ for instance in instances:
+ instance.has_error = False
+
+ self.heartbeat_queue.submit_job({"server": instance.node.name, "port": 8009, "instance": instance})
+
+ while self.heartbeat_queue.outstanding > 0:
+ result = self.heartbeat_queue.get_result()
+ instance = result["instance"]
+ if (result["status"]!="success") and (not instance.has_error):
+ self.set_status(instance, service, "watcher.HPC-hb", result["status"])
+
+ for instance in instances:
+ if not instance.has_error:
+ self.set_status(instance, service, "watcher.HPC-hb", "success")
+
+ def run_once(self):
+ for hpcService in HpcService.objects.all():
+ for slice in self.get_service_slices(hpcService, "hpc"):
+ self.probe_hpc(hpcService, slice.instances.all())
+
+ def run(self):
+ while True:
+ self.run_once()
+ time.sleep(10)
+
+ django.db.reset_queries()
+
+class HpcFetcher(BaseWatcher):
+ def __init__(self):
+ BaseWatcher.__init__(self)
+
+ self.fetch_queue = WorkQueue()
+ for i in range(0, 10):
+ HpcFetchUrl(queue = self.fetch_queue)
+
+ def fetch_hpc(self, service, instances):
+ for instance in instances:
+ instance.has_error = False
+ instance.url_status = []
+
+ checks = HpcHealthCheck.objects.filter(kind="http")
+ if not checks:
+ self.set_status(instance, service, "watcher.HPC-fetch", "no HTTP HealthCheck tests configured")
+
+ for check in checks:
+ if (not check.resource_name) or (":" not in check.resource_name):
+ self.set_status(instance, service, "watcher.HPC-fetch", "malformed resource_name: " + str(check.resource_name))
+ break
+
+ (domain, url) = check.resource_name.split(":",1)
+
+ self.fetch_queue.submit_job({"server": instance.node.name, "port": 80, "instance": instance, "domain": domain, "url": url})
+
+ while self.fetch_queue.outstanding > 0:
+ result = self.fetch_queue.get_result()
+ instance = result["instance"]
+ if (result["status"] == "success"):
+ instance.url_status.append( (result["domain"] + result["url"], "success", result["bytes_downloaded"], result["total_time"]) )
+ if (result["status"]!="success") and (not instance.has_error):
+ self.set_status(instance, service, "watcher.HPC-fetch", result["status"])
+
+ for instance in instances:
+ self.set_status(instance, service, "watcher.HPC-fetch-urls", json.dumps(instance.url_status), check_error=False)
+ if not instance.has_error:
+ self.set_status(instance, service, "watcher.HPC-fetch", "success")
+
+ def run_once(self):
+ for hpcService in HpcService.objects.all():
+ for slice in self.get_service_slices(hpcService, "hpc"):
+ try:
+ self.fetch_hpc(hpcService, slice.instances.all())
+ except:
+ traceback.print_exc()
+
+ def run(self):
+ while True:
+ self.run_once()
+ time.sleep(10)
+
+ django.db.reset_queries()
+
+class WatcherFetcher(BaseWatcher):
+ def __init__(self):
+ BaseWatcher.__init__(self)
+
+ self.fetch_queue = WorkQueue()
+ for i in range(0, 10):
+ WatcherWorker(queue = self.fetch_queue)
+
+ def fetch_watcher(self, service, instances):
+ for instance in instances:
+ try:
+ ip = self.get_public_ip(service, instance)
+ except Exception, e:
+ self.set_status(instance, service, "watcher.watcher", json.dumps({"status": "exception: %s" % str(e)}) )
+ continue
+ if not ip:
+ try:
+ ip = socket.gethostbyname(instance.node.name)
+ except:
+ self.set_status(instance, service, "watcher.watcher", json.dumps({"status": "dns resolution failure"}) )
+ continue
+
+ if not ip:
+ self.set_status(instance, service, "watcher.watcher", json.dumps({"status": "no IP address"}) )
+ continue
+
+ port = 8015
+ if ("redir" in instance.slice.name):
+ port = 8016
+ elif ("demux" in instance.slice.name):
+ port = 8017
+
+ self.fetch_queue.submit_job({"server": ip, "port": port, "instance": instance})
+
+ while self.fetch_queue.outstanding > 0:
+ result = self.fetch_queue.get_result()
+ instance = result["instance"]
+ self.set_status(instance, service, "watcher.watcher", result["status"])
+
+ def run_once(self):
+ for hpcService in HpcService.objects.all():
+ for slice in self.get_service_slices(hpcService):
+ self.fetch_watcher(hpcService, slice.instances.all())
+
+ def run(self):
+ while True:
+ self.run_once()
+ time.sleep(10)
+
+ django.db.reset_queries()
+
+
+if __name__ == "__main__":
+ if "--once" in sys.argv:
+ RRWatcher().run_once()
+ HpcProber().run_once()
+ HpcFetcher().run_once()
+ WatcherFetcher().run_once()
+ else:
+ RRWatcher().start()
+ HpcProber().start()
+ HpcFetcher().start()
+ WatcherFetcher().start()
+
+ print "Running forever..."
+ while True:
+ time.sleep(60)
+
diff --git a/xos/synchronizer/hpclib.py b/xos/synchronizer/hpclib.py
new file mode 100644
index 0000000..bb1c263
--- /dev/null
+++ b/xos/synchronizer/hpclib.py
@@ -0,0 +1,126 @@
+import os
+import base64
+import string
+import sys
+import xmlrpclib
+
+if __name__ == '__main__':
+ sys.path.append("/opt/xos")
+ os.environ.setdefault("DJANGO_SETTINGS_MODULE", "xos.settings")
+
+from xos.config import Config
+from core.models import Service
+from services.hpc.models import HpcService
+from services.requestrouter.models import RequestRouterService
+from xos.logger import Logger, logging
+
+logger = Logger(level=logging.INFO)
+
+class APIHelper:
+ def __init__(self, proxy, auth, method=None):
+ self.proxy = proxy
+ self.auth = auth
+ self.method = method
+
+ def __getattr__(self, name):
+ if name.startswith("_"):
+ return getattr(self, name)
+ else:
+ return APIHelper(self.proxy, self.auth, name)
+
+ def __call__(self, *args):
+ method = getattr(self.proxy, self.method)
+ return method(self.auth, *args)
+
+class CmiClient:
+ def __init__(self, hostname, port=8003, username="apiuser", password="apiuser"):
+ self.connect_api(hostname, port, username, password)
+
+ def connect_api(self, hostname, port=8003, username="apiuser", password="apiuser"):
+ #print "https://%s:%d/COAPI/" % (hostname, port)
+ cob = xmlrpclib.ServerProxy("https://%s:%d/COAPI/" % (hostname, port), allow_none=True)
+ cob_auth = {}
+ cob_auth["Username"] = username
+ cob_auth["AuthString"] = password
+ cob_auth["AuthMethod"] = "password"
+
+ onev = xmlrpclib.ServerProxy("https://%s:%d/ONEV_API/" % (hostname, port), allow_none=True)
+ onev_auth = {}
+ onev_auth["Username"] = username
+ onev_auth["AuthString"] = password
+ onev_auth["AuthMethod"] = "password"
+
+ self.cob = APIHelper(cob, cob_auth)
+ self.onev = APIHelper(onev, onev_auth)
+
+class HpcLibrary:
+ def __init__(self):
+ self._client = None
+
+ def make_account_name(self, x):
+ x=x.lower()
+ y = ""
+ for c in x:
+ if (c in (string.lowercase + string.digits)):
+ y = y + c
+ return y[:20]
+
+ def get_hpc_service(self):
+ hpc_service_name = getattr(Config(), "observer_hpc_service", None)
+ if hpc_service_name:
+ hpc_service = HpcService.objects.filter(name = hpc_service_name)
+ else:
+ hpc_service = HpcService.objects.all()
+
+ if not hpc_service:
+ if hpc_service_name:
+ raise Exception("No HPC Service with name %s" % hpc_service_name)
+ else:
+ raise Exception("No HPC Services")
+ hpc_service = hpc_service[0]
+
+ return hpc_service
+
+ def get_cmi_hostname(self, hpc_service=None):
+ if getattr(Config(),"observer_cmi_hostname",None):
+ return getattr(Config(),"observer_cmi_hostname")
+
+ if (hpc_service is None):
+ hpc_service = self.get_hpc_service()
+
+ if hpc_service.cmi_hostname:
+ return hpc_service.cmi_hostname
+
+ try:
+ slices = hpc_service.slices.all()
+ except:
+ # deal with buggy data model
+ slices = hpc_service.service.all()
+
+ for slice in slices:
+ if slice.name.endswith("cmi"):
+ for instance in slice.instances.all():
+ if instance.node:
+ return instance.node.name
+
+ raise Exception("Failed to find a CMI instance")
+
+ @property
+ def client(self):
+ if self._client is None:
+ self._client = CmiClient(self.get_cmi_hostname())
+ return self._client
+
+if __name__ == '__main__':
+ import django
+ django.setup()
+
+ lib = HpcLibrary()
+
+ print "testing API connection to", lib.get_cmi_hostname()
+ lib.client.cob.GetNewObjects()
+ lib.client.onev.ListAll("CDN")
+
+
+
+
diff --git a/xos/synchronizer/manifest b/xos/synchronizer/manifest
new file mode 100644
index 0000000..f0686a7
--- /dev/null
+++ b/xos/synchronizer/manifest
@@ -0,0 +1,17 @@
+manifest
+hpc_watcher.py
+steps/sync_hpcservices.py
+steps/sync_serviceprovider.py
+steps/sync_contentprovider.py
+steps/sync_cdnprefix.py
+steps/sync_originserver.py
+steps/garbage_collector.py
+steps/sync_sitemap.py
+hpc_synchronizer_config
+start.sh
+stop.sh
+hpc-synchronizer.py
+model-deps
+run.sh
+fsck.py
+hpclib.py
diff --git a/xos/synchronizer/model-deps b/xos/synchronizer/model-deps
new file mode 100644
index 0000000..63188f0
--- /dev/null
+++ b/xos/synchronizer/model-deps
@@ -0,0 +1,19 @@
+{
+ "OriginServer": [
+ "ContentProvider"
+ ],
+ "ContentProvider": [
+ "ServiceProvider"
+ ],
+ "CDNPrefix": [
+ "ContentProvider"
+ ],
+ "AccessMap": [
+ "ContentProvider"
+ ],
+ "SiteMap": [
+ "ContentProvider",
+ "ServiceProvider",
+ "CDNPrefix"
+ ]
+}
diff --git a/xos/synchronizer/run.sh b/xos/synchronizer/run.sh
new file mode 100755
index 0000000..9d22047
--- /dev/null
+++ b/xos/synchronizer/run.sh
@@ -0,0 +1,2 @@
+export XOS_DIR=/opt/xos
+python hpc-synchronizer.py -C $XOS_DIR/synchronizers/hpc/hpc_synchronizer_config
diff --git a/xos/synchronizer/start.sh b/xos/synchronizer/start.sh
new file mode 100755
index 0000000..3153a7d
--- /dev/null
+++ b/xos/synchronizer/start.sh
@@ -0,0 +1,2 @@
+export XOS_DIR=/opt/xos
+nohup python hpc-synchronizer.py -C $XOS_DIR/synchronizers/hpc/hpc_synchronizer_config > /dev/null 2>&1 &
diff --git a/xos/synchronizer/steps/garbage_collector.py b/xos/synchronizer/steps/garbage_collector.py
new file mode 100644
index 0000000..658f7a1
--- /dev/null
+++ b/xos/synchronizer/steps/garbage_collector.py
@@ -0,0 +1,67 @@
+import os
+import sys
+import base64
+import traceback
+from collections import defaultdict
+from django.db.models import F, Q
+from xos.config import Config
+from xos.logger import Logger, logging
+from synchronizers.base.syncstep import SyncStep
+from services.hpc.models import ServiceProvider, ContentProvider, CDNPrefix, OriginServer
+from core.models import *
+
+# hpclibrary will be in steps/..
+parentdir = os.path.join(os.path.dirname(__file__),"..")
+sys.path.insert(0,parentdir)
+
+from hpclib import HpcLibrary
+
+logger = Logger(level=logging.INFO)
+
+class GarbageCollector(SyncStep, HpcLibrary):
+# requested_interval = 86400
+ requested_interval = 0
+ provides=[]
+
+ def __init__(self, **args):
+ SyncStep.__init__(self, **args)
+ HpcLibrary.__init__(self)
+
+ def call(self, **args):
+ logger.info("running garbage collector")
+ try:
+ self.gc_originservers()
+ self.gc_cdnprefixes()
+ self.gc_contentproviders()
+ self.gc_serviceproviders()
+ except:
+ traceback.print_exc()
+
+ def gc_onev(self, ps_class, ps_idField, onev_className, onev_idField):
+ # get the CMI's objects
+ onev_objs = self.client.onev.ListAll(onev_className)
+
+ # get the data model's objects,
+ ps_objs = ps_class.objects.filter(enacted__isnull=False)
+ ps_ids = [str(getattr(x,ps_idField,None)) for x in ps_objs]
+
+ # for each onev object, if it's id does not exist in a data model
+ # object, then delete it.
+ for onev_obj in onev_objs:
+ onev_id = onev_obj[onev_idField]
+ if str(onev_id) not in ps_ids:
+ logger.info("garbage collecting %s %s" % (onev_className, str(onev_id)))
+ self.client.onev.Delete(onev_className, onev_id)
+
+ def gc_originservers(self):
+ self.gc_onev(OriginServer, "origin_server_id", "OriginServer", "origin_server_id")
+
+ def gc_cdnprefixes(self):
+ self.gc_onev(CDNPrefix, "cdn_prefix_id", "CDNPrefix", "cdn_prefix_id")
+
+ def gc_contentproviders(self):
+ self.gc_onev(ContentProvider, "content_provider_id", "ContentProvider", "content_provider_id")
+
+ def gc_serviceproviders(self):
+ self.gc_onev(ServiceProvider, "service_provider_id", "ServiceProvider", "service_provider_id")
+
diff --git a/xos/synchronizer/steps/sync_cdnprefix.py b/xos/synchronizer/steps/sync_cdnprefix.py
new file mode 100644
index 0000000..eff3b5d
--- /dev/null
+++ b/xos/synchronizer/steps/sync_cdnprefix.py
@@ -0,0 +1,101 @@
+import os
+import sys
+import base64
+from django.db.models import F, Q
+from xos.config import Config
+from synchronizers.base.syncstep import SyncStep
+from core.models import Service
+from services.hpc.models import ServiceProvider, ContentProvider, CDNPrefix
+from xos.logger import Logger, logging
+
+# hpclibrary will be in steps/..
+parentdir = os.path.join(os.path.dirname(__file__),"..")
+sys.path.insert(0,parentdir)
+
+from hpclib import HpcLibrary
+
+logger = Logger(level=logging.INFO)
+
+class SyncCDNPrefix(SyncStep, HpcLibrary):
+ provides=[CDNPrefix]
+ observes=CDNPrefix
+ requested_interval=0
+
+ def __init__(self, **args):
+ SyncStep.__init__(self, **args)
+ HpcLibrary.__init__(self)
+
+ def filter_hpc_service(self, objs):
+ hpcService = self.get_hpc_service()
+
+ return [x for x in objs if x.contentProvider.serviceProvider.hpcService == hpcService]
+
+ def fetch_pending(self, deleted):
+ #self.consistency_check()
+
+ return self.filter_hpc_service(SyncStep.fetch_pending(self, deleted))
+
+ def consistency_check(self):
+ # set to true if something changed
+ result=False
+
+ # sanity check to make sure our PS objects have CMI objects behind them
+ all_p_ids = [x["cdn_prefix_id"] for x in self.client.onev.ListAll("CDNPrefix")]
+
+ all_p_ids = []
+ all_origins = {}
+ for x in self.client.onev.ListAll("CDNPrefix"):
+ id = x["cdn_prefix_id"]
+ all_p_ids.append(id)
+ all_origins[id] = x.get("default_origin_server", None)
+
+ for p in CDNPrefix.objects.all():
+ if (p.cdn_prefix_id is None):
+ continue
+
+ if (p.cdn_prefix_id not in all_p_ids):
+ logger.info("CDN Prefix %s was not found on CMI" % p.cdn_prefix_id)
+ p.cdn_prefix_id=None
+ p.save()
+ result = True
+
+ if (p.defaultOriginServer!=None) and (all_origins.get(p.cdn_prefix_id,None) != p.defaultOriginServer.url):
+ logger.info("CDN Prefix %s does not have default origin server on CMI" % str(p))
+ p.save() # this will set updated>enacted and force observer to re-sync
+ result = True
+
+ return result
+
+ def sync_record(self, cp):
+ logger.info("sync'ing cdn prefix %s" % str(cp),extra=cp.tologdict())
+
+ if (not cp.contentProvider) or (not cp.contentProvider.content_provider_id):
+ raise Exception("CDN Prefix %s is linked to a contentProvider without an id" % str(cp))
+
+ cpid = cp.contentProvider.content_provider_id
+
+ cp_dict = {"service": "HyperCache", "enabled": cp.enabled, "content_provider_id": cpid, "cdn_prefix": cp.prefix}
+
+ if cp.defaultOriginServer and cp.defaultOriginServer.url:
+ if (not cp.defaultOriginServer.origin_server_id):
+ # It's probably a bad idea to try to set defaultOriginServer before
+ # we've crated defaultOriginServer.
+ raise Exception("cdn prefix %s is waiting for it's default origin server to get an id" % str(cp))
+
+ cp_dict["default_origin_server"] = cp.defaultOriginServer.url
+
+ #print cp_dict
+
+ if not cp.cdn_prefix_id:
+ id = self.client.onev.Create("CDNPrefix", cp_dict)
+ cp.cdn_prefix_id = id
+ else:
+ del cp_dict["content_provider_id"] # this can't be updated
+ del cp_dict["cdn_prefix"] # this can't be updated either
+ self.client.onev.Update("CDNPrefix", cp.cdn_prefix_id, cp_dict)
+
+ cp.save()
+
+ def delete_record(self, m):
+ if m.cdn_prefix_id is not None:
+ self.client.onev.Delete("CDNPrefix", m.cdn_prefix_id)
diff --git a/xos/synchronizer/steps/sync_contentprovider.py b/xos/synchronizer/steps/sync_contentprovider.py
new file mode 100644
index 0000000..3e30ed3
--- /dev/null
+++ b/xos/synchronizer/steps/sync_contentprovider.py
@@ -0,0 +1,78 @@
+import os
+import sys
+import base64
+from django.db.models import F, Q
+from xos.config import Config
+from synchronizers.base.syncstep import SyncStep
+from core.models import Service
+from services.hpc.models import ServiceProvider, ContentProvider
+from xos.logger import Logger, logging
+
+# hpclibrary will be in steps/..
+parentdir = os.path.join(os.path.dirname(__file__),"..")
+sys.path.insert(0,parentdir)
+
+from hpclib import HpcLibrary
+
+logger = Logger(level=logging.INFO)
+
+class SyncContentProvider(SyncStep, HpcLibrary):
+ provides=[ContentProvider]
+ observes=ContentProvider
+ requested_interval=0
+
+ def __init__(self, **args):
+ SyncStep.__init__(self, **args)
+ HpcLibrary.__init__(self)
+
+ def filter_hpc_service(self, objs):
+ hpcService = self.get_hpc_service()
+
+ return [x for x in objs if x.serviceProvider.hpcService == hpcService]
+
+ def fetch_pending(self, deleted):
+ #self.consistency_check()
+
+ return self.filter_hpc_service(SyncStep.fetch_pending(self, deleted))
+
+ def consistency_check(self):
+ # set to true if something changed
+ result=False
+
+ # sanity check to make sure our PS objects have CMI objects behind them
+ all_cp_ids = [x["content_provider_id"] for x in self.client.onev.ListAll("ContentProvider")]
+ for cp in ContentProvider.objects.all():
+ if (cp.content_provider_id is not None) and (cp.content_provider_id not in all_cp_ids):
+ logger.info("Content provider %s was not found on CMI" % cp.content_provider_id)
+ cp.content_provider_id=None
+ cp.save()
+ result = True
+
+ return result
+
+ def sync_record(self, cp):
+ logger.info("sync'ing content provider %s" % str(cp), extra=cp.tologdict())
+ account_name = self.make_account_name(cp.name)
+
+ if (not cp.serviceProvider) or (not cp.serviceProvider.service_provider_id):
+ raise Exception("ContentProvider %s is linked to a serviceProvider with no id" % str(cp))
+
+ spid = cp.serviceProvider.service_provider_id
+
+ cp_dict = {"account": account_name, "name": cp.name, "enabled": cp.enabled}
+
+ #print cp_dict
+
+ if not cp.content_provider_id:
+ cp_dict["service_provider_id"] = spid
+ id = self.client.onev.Create("ContentProvider", cp_dict)
+ cp.content_provider_id = id
+ else:
+ self.client.onev.Update("ContentProvider", cp.content_provider_id, cp_dict)
+
+ cp.save()
+
+ def delete_record(self, m):
+ if m.content_provider_id is not None:
+ self.client.onev.Delete("ContentProvider", m.content_provider_id)
+
diff --git a/xos/synchronizer/steps/sync_hpcservices.py b/xos/synchronizer/steps/sync_hpcservices.py
new file mode 100644
index 0000000..63bf19b
--- /dev/null
+++ b/xos/synchronizer/steps/sync_hpcservices.py
@@ -0,0 +1,43 @@
+import os
+import sys
+import base64
+from django.db.models import F, Q
+from xos.config import Config
+from synchronizers.base.syncstep import SyncStep
+from core.models import Service
+from services.hpc.models import HpcService
+from services.requestrouter.models import RequestRouterService
+from xos.logger import Logger, logging
+
+# hpclibrary will be in steps/..
+parentdir = os.path.join(os.path.dirname(__file__),"..")
+sys.path.insert(0,parentdir)
+
+from hpclib import HpcLibrary
+
+logger = Logger(level=logging.INFO)
+
+class SyncHpcService(SyncStep, HpcLibrary):
+ provides=[HpcService]
+ observes=HpcService
+ requested_interval=0
+
+ def __init__(self, **args):
+ SyncStep.__init__(self, **args)
+ HpcLibrary.__init__(self)
+
+ def filter_hpc_service(self, objs):
+ hpcService = self.get_hpc_service()
+
+ return [x for x in objs if x == hpcService]
+
+ def fetch_pending(self, deleted):
+ # Looks like deletion is not supported for this object - Sapan
+ if (deleted):
+ return []
+ else:
+ return self.filter_hpc_service(HpcService.objects.filter(Q(enacted__lt=F('updated')) | Q(enacted=None)))
+
+ def sync_record(self, hpc_service):
+ logger.info("sync'ing hpc_service %s" % str(hpc_service),extra=hpc_service.tologdict())
+ hpc_service.save()
diff --git a/xos/synchronizer/steps/sync_originserver.py b/xos/synchronizer/steps/sync_originserver.py
new file mode 100644
index 0000000..bd5b227
--- /dev/null
+++ b/xos/synchronizer/steps/sync_originserver.py
@@ -0,0 +1,92 @@
+import os
+import sys
+import base64
+
+from django.db.models import F, Q
+from xos.config import Config
+from synchronizers.base.syncstep import SyncStep
+from core.models import Service
+from services.hpc.models import ServiceProvider, ContentProvider, CDNPrefix, OriginServer
+from xos.logger import Logger, logging
+
+# hpclibrary will be in steps/..
+parentdir = os.path.join(os.path.dirname(__file__),"..")
+sys.path.insert(0,parentdir)
+
+from hpclib import HpcLibrary
+
+logger = Logger(level=logging.INFO)
+
+class SyncOriginServer(SyncStep, HpcLibrary):
+ provides=[OriginServer]
+ observes=OriginServer
+ requested_interval=0
+
+ def __init__(self, **args):
+ SyncStep.__init__(self, **args)
+ HpcLibrary.__init__(self)
+
+ def filter_hpc_service(self, objs):
+ hpcService = self.get_hpc_service()
+
+ return [x for x in objs if x.contentProvider.serviceProvider.hpcService == hpcService]
+
+ def fetch_pending(self, deleted):
+ #self.consistency_check()
+
+ return self.filter_hpc_service(SyncStep.fetch_pending(self, deleted))
+
+ def consistency_check(self):
+ # set to true if something changed
+ result=False
+
+ # sanity check to make sure our PS objects have CMI objects behind them
+ all_ors_ids = [x["origin_server_id"] for x in self.client.onev.ListAll("OriginServer")]
+ for ors in OriginServer.objects.all():
+ if (ors.origin_server_id is not None) and (ors.origin_server_id not in all_ors_ids):
+ # we have an origin server ID, but it doesn't exist in the CMI
+ # something went wrong
+ # start over
+ logger.info("origin server %s was not found on CMI" % ors.origin_server_id)
+ ors.origin_server_id=None
+ ors.save()
+ result = True
+
+ return result
+
+ def sync_record(self, ors):
+ logger.info("sync'ing origin server %s" % str(ors),extra=ors.tologdict())
+
+ if (not ors.contentProvider) or (not ors.contentProvider.content_provider_id):
+ raise Exception("Origin Server %s is linked to a contentProvider with no id" % str(ors))
+
+ cpid = ors.contentProvider.content_provider_id
+
+ # validation requires URL start with http://
+ url = ors.url
+ if not url.startswith("http://"):
+ url = "http://" + url
+
+ ors_dict = {"authenticated_content": ors.authenticated, "zone_redirects": ors.redirects, "content_provider_id": cpid, "url": url, "service_type": "HyperCache", "caching_type": "Optimistic", "description": ors.description}
+ if not ors_dict["description"]:
+ ors_dict["description"] = "blank"
+
+ #print os_dict
+
+ if not ors.origin_server_id:
+ id = self.client.onev.Create("OriginServer", ors_dict)
+ ors.origin_server_id = id
+ else:
+ self.client.onev.Update("OriginServer", ors.origin_server_id, ors_dict)
+
+ # ... something breaks (analytics) if the URL starts with http://, so we
+ # change it in cob after we added it via onev.
+ url = url[7:]
+ self.client.cob.UpdateContent(ors.origin_server_id, {"url": url})
+
+ ors.silent = True
+ ors.save()
+
+ def delete_record(self, m):
+ if m.origin_server_id is not None:
+ self.client.onev.Delete("OriginServer", m.origin_server_id)
diff --git a/xos/synchronizer/steps/sync_serviceprovider.py b/xos/synchronizer/steps/sync_serviceprovider.py
new file mode 100644
index 0000000..af6d685
--- /dev/null
+++ b/xos/synchronizer/steps/sync_serviceprovider.py
@@ -0,0 +1,67 @@
+import os
+import sys
+import base64
+from django.db.models import F, Q
+from xos.config import Config
+from synchronizers.base.syncstep import SyncStep
+from core.models import Service
+from services.hpc.models import ServiceProvider
+from xos.logger import Logger, logging
+
+# hpclibrary will be in steps/..
+parentdir = os.path.join(os.path.dirname(__file__),"..")
+sys.path.insert(0,parentdir)
+
+from hpclib import HpcLibrary
+
+logger = Logger(level=logging.INFO)
+
+class SyncServiceProvider(SyncStep, HpcLibrary):
+ provides=[ServiceProvider]
+ observes=ServiceProvider
+ requested_interval=0
+
+ def __init__(self, **args):
+ SyncStep.__init__(self, **args)
+ HpcLibrary.__init__(self)
+
+ def filter_hpc_service(self, objs):
+ hpcService = self.get_hpc_service()
+
+ return [x for x in objs if x.hpcService == hpcService]
+
+ def fetch_pending(self, deleted):
+ #self.consistency_check()
+
+ return self.filter_hpc_service(SyncStep.fetch_pending(self, deleted))
+
+ def consistency_check(self):
+ # set to true if something changed
+ result=False
+
+ # sanity check to make sure our PS objects have CMI objects behind them
+ all_sp_ids = [x["service_provider_id"] for x in self.client.onev.ListAll("ServiceProvider")]
+ for sp in ServiceProvider.objects.all():
+ if (sp.service_provider_id is not None) and (sp.service_provider_id not in all_sp_ids):
+ logger.info("Service provider %s was not found on CMI" % sp.service_provider_id)
+ sp.service_provider_id=None
+ sp.save()
+ result = True
+
+ return result
+
+ def sync_record(self, sp):
+ logger.info("sync'ing service provider %s" % str(sp),extra=sp.tologdict())
+ account_name = self.make_account_name(sp.name)
+ sp_dict = {"account": account_name, "name": sp.name, "enabled": sp.enabled}
+ if not sp.service_provider_id:
+ id = self.client.onev.Create("ServiceProvider", sp_dict)
+ sp.service_provider_id = id
+ else:
+ self.client.onev.Update("ServiceProvider", sp.service_provider_id, sp_dict)
+
+ sp.save()
+
+ def delete_record(self, m):
+ if m.service_provider_id is not None:
+ self.client.onev.Delete("ServiceProvider", m.service_provider_id)
diff --git a/xos/synchronizer/steps/sync_sitemap.py b/xos/synchronizer/steps/sync_sitemap.py
new file mode 100644
index 0000000..a1d177b
--- /dev/null
+++ b/xos/synchronizer/steps/sync_sitemap.py
@@ -0,0 +1,118 @@
+import os
+import sys
+import base64
+from django.db.models import F, Q
+from xos.config import Config
+from synchronizers.base.syncstep import SyncStep
+from core.models import Service
+from services.hpc.models import ServiceProvider, ContentProvider, CDNPrefix, SiteMap
+from xos.logger import Logger, logging
+
+# hpclibrary will be in steps/..
+parentdir = os.path.join(os.path.dirname(__file__),"..")
+sys.path.insert(0,parentdir)
+
+from hpclib import HpcLibrary
+
+logger = Logger(level=logging.INFO)
+
+class SyncSiteMap(SyncStep, HpcLibrary):
+ provides=[SiteMap]
+ observes=SiteMap
+ requested_interval=0
+
+ def __init__(self, **args):
+ SyncStep.__init__(self, **args)
+ HpcLibrary.__init__(self)
+
+ def filter_hpc_service(self, objs):
+ hpcService = self.get_hpc_service()
+
+ filtered_objs = []
+ for x in objs:
+ if ((x.hpcService == hpcService) or
+ ((x.serviceProvider != None) and (x.serviceProvider.hpcService == hpcService)) or
+ ((x.contentProvider != None) and (x.contentProvider.serviceProvider.hpcService == hpcService)) or
+ ((x.cdnPrefix!= None) and (x.cdnPrefix.contentProvider.serviceProvider.hpcService == hpcService))):
+ filtered_objs.append(x)
+
+ return filtered_objs
+
+ def fetch_pending(self, deleted):
+ return self.filter_hpc_service(SyncStep.fetch_pending(self, deleted))
+
+ def consistency_check(self):
+ # set to true if something changed
+ result=False
+
+ # sanity check to make sure our PS objects have CMI objects behind them
+ all_map_ids = [x["map_id"] for x in self.client.onev.ListAll("Map")]
+ for map in SiteMap.objects.all():
+ if (map.map_id is not None) and (map.map_id not in all_map_ids):
+ logger.info("Map %s was not found on CMI" % map.map_id,extra=map.tologdict())
+ map.map_id=None
+ map.save()
+ result = True
+
+ return result
+
+ def update_bind(self, map, map_dict, field_name, to_name, ids):
+ for id in ids:
+ if (not id in map_dict.get(field_name, [])):
+ print "Bind Map", map.map_id, "to", to_name, id
+ self.client.onev.Bind("Map", map.map_id, to_name, id)
+
+ for id in map_dict.get(field_name, []):
+ if (not id in ids):
+ print "Unbind Map", map.map_id, "from", to_name, id
+ self.client.onev.UnBind("map", map.map_id, to_name, id)
+
+ def sync_record(self, map):
+ logger.info("sync'ing SiteMap %s" % str(map),extra=map.tologdict())
+
+ if not map.map:
+ # no contents
+ return
+
+ content = map.map.read()
+
+ map_dict = {"name": map.name, "type": "site", "content": content}
+
+ cdn_prefix_ids=[]
+ service_provider_ids=[]
+ content_provider_ids=[]
+
+ if (map.contentProvider):
+ if not map.contentProvider.content_provider_id:
+ raise Exception("Map %s links to a contentProvider with no id" % map.name)
+ conent_provider_ids = [map.contentProvider.content_provider_id]
+
+ if (map.serviceProvider):
+ if not map.serviceProvider.service_provider_id:
+ raise Exception("Map %s links to a serviceProvider with no id" % map.name)
+ service_provider_ids = [map.serviceProvider.service_provider_id]
+
+ if (map.cdnPrefix):
+ if not map.cdnPrefix.cdn_prefix_id:
+ raise Exception("Map %s links to a cdnPrefix with no id" % map.name)
+ cdn_prefix_ids = [map.cdnPrefix.cdn_prefix_id]
+
+ if not map.map_id:
+ print "Create Map", map_dict
+ id = self.client.onev.Create("Map", map_dict)
+ map.map_id = id
+ else:
+ print "Update Map", map_dict
+ # these things we probably cannot update
+ del map_dict["name"]
+ self.client.onev.Update("Map", map.map_id, map_dict)
+
+ cmi_map_dict = self.client.onev.Read("Map", map.map_id)
+
+ self.update_bind(map, cmi_map_dict, "cdn_prefix_ids", "CDNPrefix", cdn_prefix_ids)
+
+ map.save()
+
+ def delete_record(self, m):
+ if m.map_id is not None:
+ self.client.onev.Delete("Map", m.map_id)
diff --git a/xos/synchronizer/stop.sh b/xos/synchronizer/stop.sh
new file mode 100755
index 0000000..780e25c
--- /dev/null
+++ b/xos/synchronizer/stop.sh
@@ -0,0 +1 @@
+pkill -9 -f hpc-synchronizer.py
diff --git a/xos/synchronizer/supervisor/hpc-observer.conf b/xos/synchronizer/supervisor/hpc-observer.conf
new file mode 100644
index 0000000..f2c79d4
--- /dev/null
+++ b/xos/synchronizer/supervisor/hpc-observer.conf
@@ -0,0 +1,2 @@
+[program:hpc-observer]
+command=python /opt/xos/observers/hpc/hpc-observer.py -C /opt/xos/observers/hpc/hpc_observer_config
diff --git a/xos/synchronizer/supervisor/hpc-watcher.conf b/xos/synchronizer/supervisor/hpc-watcher.conf
new file mode 100644
index 0000000..e0f4eb1
--- /dev/null
+++ b/xos/synchronizer/supervisor/hpc-watcher.conf
@@ -0,0 +1,2 @@
+[program:hpc-watcher]
+command=python /opt/xos/observers/hpc/hpc_watcher.py
diff --git a/xos/templates/hpcadmin.html b/xos/templates/hpcadmin.html
new file mode 100644
index 0000000..5c05020
--- /dev/null
+++ b/xos/templates/hpcadmin.html
@@ -0,0 +1,18 @@
+<div class = "left-nav">
+<ul>
+{% for admin in registered_admins %}
+ <li><a href="{{ admin.url }}">{{ admin.name }}</a></li>
+{% endfor %}
+</ul>
+</div>
+
+<!--
+<ul>
+<li><a href="/admin/hpc/originserver/">Origin Servers</a></li>
+<li><a href="/admin/hpc/contentprovider/">Content Providers</a></li>
+<li><a href="/admin/hpc/serviceprovider/">Server Providers</a></li>
+<li><a href="/admin/hpc/cdnprefix/">CDN Prefixes</a></li>
+<li><a href="/admin/hpc/sitemap/">Site Maps</a></li>
+<li><a href="/admin/hpc/accessmap/">Access Maps</a></li>
+</ul>
+-->
diff --git a/xos/templates/hpctools.html b/xos/templates/hpctools.html
new file mode 100644
index 0000000..28ab5c0
--- /dev/null
+++ b/xos/templates/hpctools.html
@@ -0,0 +1,9 @@
+<div class = "left-nav">
+
+<ul>
+<li><a href="/dashboard/xosHpc">Service Monitor</a></li>
+<li><a href="/dashboard/xosHpcUrls">HPC Node Drilldown</a></li>
+<li><a href="/dashboard/xosHpcNodes">HPC Url Drilldown</a></li>
+</ul>
+
+</div>