blob: 4bfef745c136acacedd54754b8e25efc99d6ac96 [file] [log] [blame]
import hashlib
import os
import socket
import sys
import base64
import time
from django.db.models import F, Q
from xos.config import Config
from observer.syncstep import SyncStep
from observer.ansible import run_template_ssh
from core.models import Service, Slice
from cord.models import VCPEService, VCPETenant, VOLTTenant
from hpc.models import HpcService, CDNPrefix
from util.logger import Logger, logging
# hpclibrary will be in steps/..
parentdir = os.path.join(os.path.dirname(__file__),"..")
sys.path.insert(0,parentdir)
from broadbandshield import BBS
logger = Logger(level=logging.INFO)
class SyncVCPETenant(SyncStep):
provides=[VCPETenant]
observes=VCPETenant
requested_interval=0
template_name = "sync_vcpetenant.yaml"
service_key_name = "/opt/xos/observers/vcpe/vcpe_private_key"
def __init__(self, **args):
SyncStep.__init__(self, **args)
def defer_sync(self, o, reason):
logger.info("defer object %s due to %s" % (str(o), reason))
raise Exception("defer object %s due to %s" % (str(o), reason))
def fetch_pending(self, deleted):
if (not deleted):
objs = VCPETenant.get_tenant_objects().filter(Q(enacted__lt=F('updated')) | Q(enacted=None),Q(lazy_blocked=False))
else:
objs = VCPETenant.get_deleted_tenant_objects()
return objs
def get_extra_attributes(self, o):
# This is a place to include extra attributes that aren't part of the
# object itself. In our case, it's handy to know the VLAN IDs when
# configuring the VCPE.
dnsdemux_ip = "none"
for service in HpcService.objects.all():
for slice in service.slices.all():
if "dnsdemux" in slice.name:
for sliver in slice.slivers.all():
# Connect to a dnsdemux that's on the hpc_client network
# if one is available.
for ns in sliver.networkslivers.all():
if ns.ip and ns.network.labels and ("hpc_client" in ns.network.labels):
dnsdemux_ip = ns.ip
if dnsdemux_ip=="none":
try:
dnsdemux_ip = socket.gethostbyname(sliver.node.name)
except:
pass
cdn_prefixes = []
for prefix in CDNPrefix.objects.all():
cdn_prefixes.append(prefix.prefix)
vlan_ids = []
if o.volt:
vlan_ids.append(o.volt.vlan_id)
bbs_addrs = []
bbs_slices = Slice.objects.filter(name="mysite_bbs")
if bbs_slices:
bbs_slice = bbs_slices[0]
for bbs_sliver in bbs_slice.slivers.all():
for ns in bbs_sliver.networkslivers.all():
if ns.ip and ns.network.labels and ("hpc_client" in ns.network.labels):
bbs_addrs.append(ns.ip)
if not bbs_addrs:
bbs_addrs = ["198.105.255.10",
"198.105.255.11",
"198.105.255.12",
"198.105.255.13"]
return {"vlan_ids": vlan_ids,
"dnsdemux_ip": dnsdemux_ip,
"cdn_prefixes": cdn_prefixes,
"bbs_addrs": bbs_addrs}
def get_sliver(self, o):
# We need to know what slivers is associated with the object.
# For vCPE this is easy, as VCPETenant has a sliver field.
return o.sliver
def sync_record(self, o):
logger.info("sync'ing VCPETenant %s" % str(o))
sliver = self.get_sliver(o)
if not sliver:
self.defer_sync(o, "waiting on sliver")
return
service = o.sliver.slice.service
if not service:
# Ansible uses the service's keypair in order to SSH into the
# instance. It would be bad if the slice had no service.
raise Exception("Slice %s is not associated with a service" % sliver.slice.name)
if not os.path.exists(self.service_key_name):
raise Exception("Service key %s does not exist" % self.service_key_name)
service_key = file(self.service_key_name).read()
fields = { "sliver_name": sliver.name,
"hostname": sliver.node.name,
"instance_id": sliver.instance_id,
"private_key": service_key,
"ansible_tag": "vcpe_tenant_" + str(o.id)
}
if hasattr(o, "sync_attributes"):
for attribute_name in o.sync_attributes:
fields[attribute_name] = getattr(o, attribute_name)
fields.update(self.get_extra_attributes(o))
ansible_hash = hashlib.md5(repr(sorted(fields.items()))).hexdigest()
quick_update = (o.last_ansible_hash == ansible_hash)
if quick_update:
logger.info("quick_update triggered; skipping ansible recipe")
else:
tStart = time.time()
run_template_ssh(self.template_name, fields)
logger.info("playbook execution time %d" % int(time.time()-tStart))
if o.url_filter_enable:
if False: # (str(o.service_specific_id) != "SYNCME"):
# XXX FIXME
# Also fix the spot in cord/models.py
logger.info("skipping sync of URL filter for SSID %s" % str(o.service_specific_id))
else:
tStart = time.time()
bbs = BBS(o.bbs_account, "123")
bbs.sync(o.url_filter_level, o.users)
if o.hpc_client_ip:
logger.info("associate account %s with ip %s" % (o.bbs_account, o.hpc_client_ip))
bbs.associate(o.hpc_client_ip)
else:
logger.info("no hpc_client_ip to associate")
logger.info("bbs update tiem %d" % int(time.time()-tStart))
o.last_ansible_hash = ansible_hash
o.save()
def delete_record(self, m):
pass