blob: 1a45b546346a138114f79e5f6353aaf0ce8f8202 [file] [log] [blame]
Scott Baker4aa660e2015-06-09 12:22:29 -07001import hashlib
Scott Baker64b889b2015-05-05 17:53:12 -07002import os
3import socket
4import sys
5import base64
Scott Baker3145da12015-06-09 12:03:07 -07006import time
Scott Baker64b889b2015-05-05 17:53:12 -07007from django.db.models import F, Q
8from xos.config import Config
9from observer.syncstep import SyncStep
10from observer.ansible import run_template_ssh
Tony Mackd8515472015-08-19 11:58:18 -040011from observers.base.SyncInstanceUsingAnsible import SyncInstanceUsingAnsible
Scott Baker3e6b7052015-06-10 20:37:54 -070012from core.models import Service, Slice
Scott Baker64b889b2015-05-05 17:53:12 -070013from cord.models import VCPEService, VCPETenant, VOLTTenant
14from hpc.models import HpcService, CDNPrefix
15from util.logger import Logger, logging
16
17# hpclibrary will be in steps/..
18parentdir = os.path.join(os.path.dirname(__file__),"..")
19sys.path.insert(0,parentdir)
20
Scott Bakercdf47142015-06-01 16:15:42 -070021from broadbandshield import BBS
22
Scott Baker64b889b2015-05-05 17:53:12 -070023logger = Logger(level=logging.INFO)
24
Tony Mackd8515472015-08-19 11:58:18 -040025class SyncVCPETenant(SyncInstanceUsingAnsible):
Scott Baker64b889b2015-05-05 17:53:12 -070026 provides=[VCPETenant]
27 observes=VCPETenant
28 requested_interval=0
29 template_name = "sync_vcpetenant.yaml"
30 service_key_name = "/opt/xos/observers/vcpe/vcpe_private_key"
31
Scott Baker558c1702015-07-21 12:22:39 -070032 def __init__(self, *args, **kwargs):
33 super(SyncVCPETenant, self).__init__(*args, **kwargs)
Scott Baker64b889b2015-05-05 17:53:12 -070034
35 def fetch_pending(self, deleted):
36 if (not deleted):
37 objs = VCPETenant.get_tenant_objects().filter(Q(enacted__lt=F('updated')) | Q(enacted=None),Q(lazy_blocked=False))
38 else:
39 objs = VCPETenant.get_deleted_tenant_objects()
40
41 return objs
42
Scott Baker7e394ce2015-07-15 18:31:33 -070043 def get_vcpe_service(self, o):
44 if not o.provider_service:
45 return None
46
47 vcpes = VCPEService.get_service_objects().filter(id=o.provider_service.id)
48 if not vcpes:
49 return None
50
51 return vcpes[0]
52
Scott Baker64b889b2015-05-05 17:53:12 -070053 def get_extra_attributes(self, o):
54 # This is a place to include extra attributes that aren't part of the
Scott Baker7e394ce2015-07-15 18:31:33 -070055 # object itself. In the case of vCPE, we need to know:
56 # 1) the addresses of dnsdemux, to setup dnsmasq in the vCPE
57 # 2) CDN prefixes, so we know what URLs to send to dnsdemux
58 # 3) BroadBandShield server addresses, for parental filtering
59 # 4) vlan_ids, for setting up networking in the vCPE VM
Scott Baker64b889b2015-05-05 17:53:12 -070060
Scott Baker7e394ce2015-07-15 18:31:33 -070061 vcpe_service = self.get_vcpe_service(o)
62
63 dnsdemux_ip = None
64 if vcpe_service.backend_network_label:
65 # Connect to dnsdemux using the network specified by
66 # vcpe_service.backend_network_label
67 for service in HpcService.objects.all():
68 for slice in service.slices.all():
69 if "dnsdemux" in slice.name:
Tony Mackd8515472015-08-19 11:58:18 -040070 for instance in slice.instances.all():
Tony Mack26ea0eb2015-09-01 16:06:52 +000071 for ns in instance.ports.all():
Scott Baker1ba43462015-07-15 22:52:10 -070072 if ns.ip and ns.network.labels and (vcpe_service.backend_network_label in ns.network.labels):
Scott Baker7e394ce2015-07-15 18:31:33 -070073 dnsdemux_ip = ns.ip
74 if not dnsdemux_ip:
75 logger.info("failed to find a dnsdemux on network %s" % vcpe_service.backend_network_label)
76 else:
Tony Mackd8515472015-08-19 11:58:18 -040077 # Connect to dnsdemux using the instance's public address
Scott Baker7e394ce2015-07-15 18:31:33 -070078 for service in HpcService.objects.all():
79 for slice in service.slices.all():
80 if "dnsdemux" in slice.name:
Tony Mackd8515472015-08-19 11:58:18 -040081 for instance in slice.instances.all():
Scott Baker7e394ce2015-07-15 18:31:33 -070082 if dnsdemux_ip=="none":
83 try:
Tony Mackd8515472015-08-19 11:58:18 -040084 dnsdemux_ip = socket.gethostbyname(instance.node.name)
Scott Baker7e394ce2015-07-15 18:31:33 -070085 except:
86 pass
87 if not dnsdemux_ip:
88 logger.info("failed to find a dnsdemux with a public address")
89
90 dnsdemux_ip = dnsdemux_ip or "none"
Scott Baker64b889b2015-05-05 17:53:12 -070091
Scott Baker2e64a3a2015-05-06 20:06:21 -070092 cdn_prefixes = []
93 for prefix in CDNPrefix.objects.all():
94 cdn_prefixes.append(prefix.prefix)
95
Scott Baker63bf6e42015-07-20 16:00:42 -070096 # Broadbandshield can either be set up internally, using vcpe_service.bbs_slice,
97 # or it can be setup externally using vcpe_service.bbs_server.
98
Scott Baker74e20522015-06-10 16:16:01 -070099 bbs_addrs = []
Scott Baker1ba43462015-07-15 22:52:10 -0700100 if vcpe_service.bbs_slice:
Scott Bakerd4c1cf32015-07-16 10:39:12 -0700101 if vcpe_service.backend_network_label:
Tony Mackd8515472015-08-19 11:58:18 -0400102 for bbs_instance in vcpe_service.bbs_slice.instances.all():
Tony Mack26ea0eb2015-09-01 16:06:52 +0000103 for ns in bbs_instance.ports.all():
Scott Bakerd4c1cf32015-07-16 10:39:12 -0700104 if ns.ip and ns.network.labels and (vcpe_service.backend_network_label in ns.network.labels):
105 bbs_addrs.append(ns.ip)
106 else:
107 logger.info("unsupported configuration -- bbs_slice is set, but backend_network_label is not")
108 if not bbs_addrs:
109 logger.info("failed to find any usable addresses on bbs_slice")
Scott Bakera4b8bfb2015-08-31 14:54:05 -0700110 elif vcpe_service.bbs_server:
Scott Baker34d4a6a2015-08-31 14:55:31 -0700111 bbs_addrs.append(vcpe_service.bbs_server)
Scott Baker7e394ce2015-07-15 18:31:33 -0700112 else:
113 logger.info("neither bbs_slice nor bbs_server is configured in the vCPE")
Scott Baker74e20522015-06-10 16:16:01 -0700114
Scott Baker7e394ce2015-07-15 18:31:33 -0700115 vlan_ids = []
116 if o.volt:
117 vlan_ids.append(o.volt.vlan_id)
Scott Baker74e20522015-06-10 16:16:01 -0700118
Andy Bavier64c804f2015-09-09 18:20:39 -0400119 try:
120 full_setup = Config().observer_full_setup
121 except:
122 full_setup = True
Andy Bavier4c7e76d2015-09-09 18:06:30 -0400123
Scott Baker558c1702015-07-21 12:22:39 -0700124 fields = {"vlan_ids": vlan_ids,
Scott Baker2e64a3a2015-05-06 20:06:21 -0700125 "dnsdemux_ip": dnsdemux_ip,
Scott Baker74e20522015-06-10 16:16:01 -0700126 "cdn_prefixes": cdn_prefixes,
Andy Bavier4c7e76d2015-09-09 18:06:30 -0400127 "bbs_addrs": bbs_addrs,
128 "full_setup": full_setup}
Scott Baker64b889b2015-05-05 17:53:12 -0700129
Scott Baker558c1702015-07-21 12:22:39 -0700130 # add in the sync_attributes that come from the SubscriberRoot object
Scott Baker64b889b2015-05-05 17:53:12 -0700131
Scott Baker558c1702015-07-21 12:22:39 -0700132 if o.volt and o.volt.subscriber and hasattr(o.volt.subscriber, "sync_attributes"):
133 for attribute_name in o.volt.subscriber.sync_attributes:
134 fields[attribute_name] = getattr(o.volt.subscriber, attribute_name)
Scott Baker64b889b2015-05-05 17:53:12 -0700135
Scott Baker558c1702015-07-21 12:22:39 -0700136 return fields
Scott Baker64b889b2015-05-05 17:53:12 -0700137
Scott Baker558c1702015-07-21 12:22:39 -0700138 def sync_fields(self, o, fields):
139 # the super causes the playbook to be run
140
141 super(SyncVCPETenant, self).sync_fields(o, fields)
142
143 # now do all of our broadbandshield stuff...
Scott Baker64b889b2015-05-05 17:53:12 -0700144
Scott Baker7e394ce2015-07-15 18:31:33 -0700145 service = self.get_vcpe_service(o)
Scott Baker64b889b2015-05-05 17:53:12 -0700146 if not service:
147 # Ansible uses the service's keypair in order to SSH into the
148 # instance. It would be bad if the slice had no service.
149
Tony Mackd8515472015-08-19 11:58:18 -0400150 raise Exception("Slice %s is not associated with a service" % instance.slice.name)
Scott Baker64b889b2015-05-05 17:53:12 -0700151
Scott Baker7e394ce2015-07-15 18:31:33 -0700152 # Make sure the slice is configured properly
Tony Mackd8515472015-08-19 11:58:18 -0400153 if (service != o.instance.slice.service):
154 raise Exception("Slice %s is associated with some service that is not %s" % (str(instance.slice), str(service)))
Scott Baker7e394ce2015-07-15 18:31:33 -0700155
Scott Baker126ad472015-07-07 17:59:44 -0700156 # only enable filtering if we have a subscriber object (see below)
157 url_filter_enable = False
Scott Bakerd6fbeb42015-07-07 12:14:03 -0700158
159 # for attributes that come from CordSubscriberRoot
Scott Baker558c1702015-07-21 12:22:39 -0700160 if o.volt and o.volt.subscriber:
Scott Bakerd6fbeb42015-07-07 12:14:03 -0700161 url_filter_enable = o.volt.subscriber.url_filter_enable
162 url_filter_level = o.volt.subscriber.url_filter_level
163 url_filter_users = o.volt.subscriber.users
Scott Baker64b889b2015-05-05 17:53:12 -0700164
Scott Baker4ec80a32015-07-20 18:25:17 -0700165 # disable url_filter if there are no bbs_addrs
166 if url_filter_enable and (not fields.get("bbs_addrs",[])):
167 logger.info("disabling url_filter because there are no bbs_addrs")
168 url_filter_enable = False
169
Scott Bakerd6fbeb42015-07-07 12:14:03 -0700170 if url_filter_enable:
Scott Baker7e394ce2015-07-15 18:31:33 -0700171 bbs_hostname = None
172 if service.bbs_api_hostname and service.bbs_api_port:
173 bbs_hostname = service.bbs_api_hostname
Scott Bakerdf877da2015-06-08 18:58:39 -0700174 else:
Scott Baker7e394ce2015-07-15 18:31:33 -0700175 # TODO: extract from slice
176 bbs_hostname = "cordcompute01.onlab.us"
Scott Baker3e6b7052015-06-10 20:37:54 -0700177
Scott Baker7e394ce2015-07-15 18:31:33 -0700178 if service.bbs_api_port:
179 bbs_port = service.bbs_api_port
180 else:
181 bbs_port = 8018
182
183 if not bbs_hostname:
184 logger.info("broadbandshield is not configured")
185 else:
186 tStart = time.time()
187 bbs = BBS(o.bbs_account, "123", bbs_hostname, bbs_port)
188 bbs.sync(url_filter_level, url_filter_users)
189
190 if o.hpc_client_ip:
191 logger.info("associate account %s with ip %s" % (o.bbs_account, o.hpc_client_ip))
192 bbs.associate(o.hpc_client_ip)
193 else:
194 logger.info("no hpc_client_ip to associate")
195
196 logger.info("bbs update time %d" % int(time.time()-tStart))
Scott Bakercdf47142015-06-01 16:15:42 -0700197
Scott Baker558c1702015-07-21 12:22:39 -0700198
199 def run_playbook(self, o, fields):
200 ansible_hash = hashlib.md5(repr(sorted(fields.items()))).hexdigest()
201 quick_update = (o.last_ansible_hash == ansible_hash)
202
203 if quick_update:
204 logger.info("quick_update triggered; skipping ansible recipe")
205 else:
206 super(SyncVCPETenant, self).run_playbook(o, fields)
207
Scott Baker4aa660e2015-06-09 12:22:29 -0700208 o.last_ansible_hash = ansible_hash
Scott Baker64b889b2015-05-05 17:53:12 -0700209
210 def delete_record(self, m):
211 pass