def __init__(self, project, common_config, source_config, dest_config): self.source_config = source_config self.dest_config = dest_config self.common_config = common_config source_auth = generic.Password( auth_url=self.common_config['keystone_url'], username=self.common_config['user'], password=self.common_config['password'], user_domain_name='Default', project_domain_name='Default', project_name=project) source_session = keystone_session.Session(auth=source_auth) self.source_novaclient = client.Client( '2', session=source_session, region_name=source_config['region']) dest_auth = generic.Password( auth_url=self.common_config['keystone_url'], username=self.common_config['user'], password=self.common_config['password'], user_domain_name='Default', project_domain_name='Default', project_name=project) self.dest_session = keystone_session.Session(auth=dest_auth) self.dest_neutronclient = neutronclient.Client( session=self.dest_session, region_name=dest_config['region'])
def make_destination_vm(self, name, image_id, flavor_id): # we need a project-scoped session to create the VM in the right project create_auth = generic.Password( auth_url=self.common_config['keystone_url'], username=self.common_config['user'], password=self.common_config['password'], user_domain_name='Default', project_domain_name='Default', project_name=self.project_id) create_session = keystone_session.Session(auth=create_auth) self.create_novaclient = novaclient.Client( '2', session=create_session, region_name=self.dest_config['region']) nics = [{"net-id": self.dest_config['network_id'], "v4-fixed-ip": ''}] inst = self.create_novaclient.servers.create(name, image_id, flavor_id, nics=nics) self.dest_instance_id = inst.id self.refresh_instance(False) self.wait_for_status('ACTIVE', source=False) self.dest_host = self.dest_instance._info['OS-EXT-SRV-ATTR:host'] print("dest_instance_id: %s on %s" % (self.dest_instance_id, self.dest_host))
def _get_proxy_endpoint(self): if not self.proxy_endpoint: auth = generic.Password( auth_url=cfg.CONF['keystone_authtoken'].auth_uri, username=cfg.CONF['keystone_authtoken'].admin_user, password=cfg.CONF['keystone_authtoken'].admin_password, user_domain_name='Default', project_domain_name='Default', project_name='admin') session = keystone_session.Session(auth=auth) keystone = keystone_client.Client(session=session, interface='public', connect_retries=5) services = keystone.services.list() for service in services: if service.type == 'proxy': serviceid = service.id break endpoints = keystone.endpoints.list(service=serviceid) for endpoint in endpoints: if endpoint.interface == 'public': self.proxy_endpoint = endpoint.url break if not self.proxy_endpoint: raise Exception( "Can't find the public proxy service endpoint.") return self.proxy_endpoint
def new_session(project): auth = generic.Password(auth_url=config['nova_api_url'], username=config['username'], password=config['password'], user_domain_name='Default', project_domain_name='Default', project_name=project) return keystone_session.Session(auth=auth)
def createDomain(url, user, password, project, domain, ttl=120): auth = generic.Password( auth_url=url, username=user, password=password, user_domain_name='Default', project_domain_name='Default', project_id='wmflabsdotorg') createSession = keystone_session.Session(auth=auth) createClient = client.Client(session=createSession, region_name='eqiad') auth = generic.Password( auth_url=url, username=user, password=password, user_domain_name='Default', project_domain_name='Default', tenant_name=project) targetSession = keystone_session.Session(auth=auth) # Fixme: Once we move to a more modern version of designateclient (newton?) # we should pass sudo-project-id=wmflabsdotorg here, change createSession # to use the 'admin' project, and remove novaadmin's 'admin' role from wmflabsdotorg. targetClient = client.Client(session=targetSession, region_name='eqiad') # Create the zone in the initial wmflabsdotorg project. This # is needed since wmflabs.org lives in that project and # designate prevents subdomain creation elsewhere. LOG.info("Creating %s" % domain) zone = createClient.zones.create(domain, email='*****@*****.**', ttl=ttl) status = 'PENDING' # Wait for the domain to actually exist before we transfer it while status == 'PENDING': zone = createClient.zones.get(domain) status = zone['status'] time.sleep(2) transferRequest = createClient.zone_transfers.create_request(domain, project) transferId = transferRequest['id'] transferKey = transferRequest['key'] targetClient.zone_transfers.accept_request(transferId, transferKey)
def _get_keystone_session(self, project_name='admin'): auth = generic.Password( auth_url=cfg.CONF['keystone_authtoken'].www_authenticate_uri, username=cfg.CONF['keystone_authtoken'].username, password=cfg.CONF['keystone_authtoken'].password, user_domain_name='Default', project_domain_name='Default', project_name=project_name) return (keystone_session.Session(auth=auth))
def get_session(env, project): nova_connection = { "username": env["OS_USERNAME"], "password": env["OS_PASSWORD"], "auth_url": env["OS_AUTH_URL"], "project_domain_name": env["OS_PROJECT_DOMAIN_NAME"], "user_domain_name": env["OS_USER_DOMAIN_NAME"], "project_name": project, } auth = generic.Password(**nova_connection) return keystone_session.Session(auth=auth)
def session(self, project=None): if not project: project = self.project if project not in self.sessions: auth = generic.Password(auth_url=self.url, username=self.username, password=self.password, user_domain_name='Default', project_domain_name='Default', project_name=project) self.sessions[project] = keystone_session.Session(auth=auth) return self.sessions[project]
def deleteDomain(url, user, password, project, domain="", delete_all=False): auth = generic.Password( auth_url=url, username=user, password=password, user_domain_name='Default', project_domain_name='Default', project_id=project) targetSession = keystone_session.Session(auth=auth) targetClient = client.Client(session=targetSession, region_name='eqiad') domains = targetClient.zones.list() for thisdomain in domains: if delete_all: LOG.info("Deleting %s" % thisdomain['name']) targetClient.zones.delete(thisdomain['id']) else: if thisdomain['name'] == domain: targetClient.zones.delete(thisdomain['id']) return if not delete_all: LOG.warning("Domain %s not found" % domain)
def handle(self, request, data): proxyip = socket.gethostbyname( urlparse.urlparse(base.url_for(request, 'proxy')).hostname) if data.get('domain') == 'wmflabs.org.': auth = identity_generic.Password( auth_url=base.url_for(request, 'identity'), username=getattr(settings, "WMFLABSDOTORG_ADMIN_USERNAME", ''), password=getattr(settings, "WMFLABSDOTORG_ADMIN_PASSWORD", ''), tenant_name='wmflabsdotorg', user_domain_id='default', project_domain_id='default') c = designateclientv2.Client(session=keystone_session.Session( auth=auth)) LOG.warn('Got create client') # Create the record in the wmflabsdotorg project. This is needed # since wmflabs.org lives in that project and designate prevents # subdomain creation elsewhere. zoneid = None for zone in c.zones.list(): if zone['name'] == 'wmflabs.org.': zoneid = zone['id'] break else: raise Exception("No zone ID") LOG.warn('Got zone ID') c.recordsets.create(zoneid, data.get('record') + '.wmflabs.org.', 'A', [proxyip]) else: # TODO: Move this to designate v2 API, reuse some code c = designateapi.designateclient(request) domainid = None for domain in c.domains.list(): if domain.name == data.get('domain'): domainid = domain.id break else: raise Exception("No domain ID") record = Record(name=data.get('record') + '.' + data.get('domain'), type='A', data=proxyip) c.records.create(domainid, record) d = { "backends": [ 'http://%s:%s' % (data.get('backendInstance'), data.get('backendPort')) ], "domain": data.get('record') + '.' + data.get('domain').rstrip('.') } try: resp = requests.put(base.url_for(request, 'proxy') + '/mapping', data=json.dumps(d)) if resp: return True else: raise Exception("Got status: " + resp.status_code) except Exception: exceptions.handle(self.request, _("Unable to create proxy: " + resp.text)) return False
def main(): argparser = argparse.ArgumentParser() argparser.add_argument( '--debug', help='Turn on debug logging', action='store_true' ) argparser.add_argument( '--project', default='admin-monitoring', help='Set project to test creation for', ) argparser.add_argument( '--keyfile', default='', help='Path to SSH key file for verification', ) argparser.add_argument( '--user', default='', help='Set username (Expected to be the same across all backends)', ) argparser.add_argument( '--prepend', default='test-create', help='String to add to beginning of instance names', ) argparser.add_argument( '--max-pool', default=1, type=int, help='Allow this many instances', ) argparser.add_argument( '--keystone-url', default="http://labcontrol1001.wikimedia.org:35357/v3", help='Auth url for token and service discovery', ) argparser.add_argument( '--interval', default=600, type=int, help='Seconds delay for daemon (default: 600 [10m])', ) argparser.add_argument( '--creation-timeout', default=180, type=int, help='Allow this long for creation to succeed.', ) argparser.add_argument( '--ssh-timeout', default=180, type=int, help='Allow this long for SSH to succeed.', ) argparser.add_argument( '--puppet-timeout', default=120, type=int, help='Allow this long for Puppet to succeed.', ) argparser.add_argument( '--deletion-timeout', default=120, type=int, help='Allow this long for delete to succeed.', ) argparser.add_argument( '--image', default='debian-8.7-jessie', help='Image to use', ) argparser.add_argument( '--flavor', default='m1.small', help='Flavor to use', ) argparser.add_argument( '--skip-puppet', help='Turn off Puppet validation', action='store_true' ) argparser.add_argument( '--skip-dns', help='Turn off DNS validation', action='store_true' ) argparser.add_argument( '--dns-resolvers', help='Comma separated list of nameservers', default='208.80.155.118,208.80.154.20', ) argparser.add_argument( '--skip-ssh', help='Turn off basic SSH validation', action='store_true' ) argparser.add_argument( '--pause-for-deletion', help='Wait for user input before deletion', action='store_true' ) argparser.add_argument( '--skip-deletion', help='Leave instance behind', action='store_true' ) argparser.add_argument( '--adhoc-command', default='', help='Specify a command over SSH prior to deletion', ) argparser.add_argument( '--statsd', default='statsd.eqiad.wmnet', help='Send statistics to statsd endpoint', ) args = argparser.parse_args() logging.basicConfig( format='%(asctime)s %(levelname)s %(message)s', level=logging.DEBUG if args.debug else logging.INFO) if args.adhoc_command and args.skip_ssh: logging.error("cannot skip SSH with adhoc command specified") sys.exit(1) try: with open(args.keyfile, 'r') as f: f.read() except: logging.error("keyfile {} cannot be read".format(args.keyfile)) sys.exit(1) pw = os.environ.get('OS_PASSWORD') user = os.environ.get('OS_USERNAME') or args.user project = os.environ.get('OS_TENANT_NAME') or args.project if not all([user, pw, project]): logging.error('Set username and password environment variables') sys.exit(1) def stat(metric, value): metric_prepend = 'servers.{}.nova'.format(socket.gethostname()) submit_stat(args.statsd, 8125, metric_prepend, metric, value) while True: loop_start = round(time.time(), 2) auth = generic.Password(auth_url=args.keystone_url, username=user, password=pw, user_domain_name='Default', project_domain_name='Default', project_name=project) sess = keystone_session.Session(auth=auth) nova_conn = nova_client.Client('2', session=sess) prepend = args.prepend epoch = int(time.time()) name = '{}-{}'.format(prepend, epoch) exist = nova_conn.servers.list() logging.debug(exist) pexit = map(lambda e: e.human_id.startswith(prepend), exist) if len(filter(bool, pexit)) >= args.max_pool: logging.error("max server(s) with prepend {}".format(prepend)) sys.exit(1) cimage = nova_conn.images.find(name=args.image) cflavor = nova_conn.flavors.find(name=args.flavor) vc, server = verify_create(nova_conn, name, cimage, cflavor, args.creation_timeout) stat('verify.creation', vc) addr = server.addresses['public'][0]['addr'] if not addr.startswith('10.'): raise Exception("Bad address of {}".format(addr)) if not args.skip_dns: host = '{}.{}.eqiad.wmnet'.format(server.name, server.tenant_id) dnsd = args.dns_resolvers.split(',') vdns = verify_dns(host, dnsd, timeout=2.0) stat('verify.dns', vdns) if not args.skip_ssh: vs = verify_ssh(addr, user, args.keyfile, args.ssh_timeout) stat('verify.ssh', vs) if args.adhoc_command: sshout = run_remote(addr, user, args.keyfile, args.adhoc_command, debug=args.debug) logging.debug(sshout) if not args.skip_puppet: ps, puppetrun = verify_puppet(addr, user, args.keyfile, args.puppet_timeout) stat('verify.puppet', ps) categories = ['changes', 'events', 'resources', 'time'] for d in categories: for k, v in puppetrun[d].iteritems(): stat('puppet.{}.{}'.format(d, k), v) if args.pause_for_deletion: logging.info("Pausing for deletion") get_verify('continue with deletion', 'Not a valid response', ['y']) if not args.skip_deletion: vd = verify_deletion(nova_conn, server, args.deletion_timeout) if not args.pause_for_deletion: stat('verify.deletion', vd) loop_end = time.time() stat('verify.fullstack', round(loop_end - loop_start, 2)) if not args.interval: return time.sleep(args.interval)
def getAuthSession(tenant_id): auth = generic.Password(auth_url=shell.env('OS_AUTH_URL'), username=shell.env('OS_USERNAME'), password=shell.env('OS_PASSWORD'), tenant_id=tenant_id) return keystone_session.Session(auth=auth)
def _on_project_create(self, project_id): LOG.warning("Beginning wmf hooks for project creation: %s" % project_id) roledict = self._get_role_dict() if CONF.wmfhooks.observer_role_name not in roledict.keys(): LOG.error("Failed to find id for role %s" % CONF.wmfhooks.observer_role_name) raise exception.NotImplemented() if CONF.wmfhooks.admin_role_name not in roledict.keys(): LOG.error("Failed to find id for role %s" % CONF.wmfhooks.admin_role_name) raise exception.NotImplemented() if CONF.wmfhooks.user_role_name not in roledict.keys(): LOG.error("Failed to find id for role %s" % CONF.wmfhooks.user_role_name) raise exception.NotImplemented() self.assignment_api.add_role_to_user_and_project( CONF.wmfhooks.admin_user, project_id, roledict[CONF.wmfhooks.admin_role_name]) self.assignment_api.add_role_to_user_and_project( CONF.wmfhooks.admin_user, project_id, roledict[CONF.wmfhooks.user_role_name]) self.assignment_api.add_role_to_user_and_project( CONF.wmfhooks.observer_user, project_id, roledict[CONF.wmfhooks.observer_role_name]) # Use the nova api to set up security groups for the new project auth = generic.Password(auth_url=CONF.wmfhooks.auth_url, username=CONF.wmfhooks.admin_user, password=CONF.wmfhooks.admin_pass, user_domain_name='Default', project_domain_name='Default', project_name=project_id) session = keystone_session.Session(auth=auth) client = nova_client.Client('2', session=session, connect_retries=5) allgroups = client.security_groups.list() defaultgroup = filter(lambda group: group.name == 'default', allgroups) if defaultgroup: groupid = defaultgroup[0].id try: client.security_group_rules.create(groupid, ip_protocol='icmp', from_port='-1', to_port='-1', cidr='0.0.0.0/0') except (exceptions.ClientException): LOG.warning("icmp security rule already exists.") try: client.security_group_rules.create(groupid, ip_protocol='tcp', from_port='22', to_port='22', cidr='10.0.0.0/8') except (exceptions.ClientException): LOG.warning("Port 22 security rule already exists.") try: client.security_group_rules.create(groupid, ip_protocol='tcp', from_port='5666', to_port='5666', cidr='10.0.0.0/8') except (exceptions.ClientException): LOG.warning("Port 5666 security rule already exists.") try: client.security_group_rules.create(groupid, ip_protocol='tcp', from_port='1', to_port='65535', cidr='', group_id=groupid) except (exceptions.ClientException): LOG.warning("Project security rule for TCP already exists.") try: client.security_group_rules.create(groupid, ip_protocol='udp', from_port='1', to_port='65535', cidr='', group_id=groupid) except (exceptions.ClientException): LOG.warning("Project security rule for UDP already exists.") try: client.security_group_rules.create(groupid, ip_protocol='icmp', from_port='1', to_port='65535', cidr='', group_id=groupid) except (exceptions.ClientException): LOG.warning("Project security rule for ICMP already exists.") else: LOG.warning( "Failed to find default security group in new project.") assignments = self._get_current_assignments(project_id) ldapgroups.sync_ldap_project_group(project_id, assignments) # Set up default sudoers in ldap ldapgroups.create_sudo_defaults(project_id) self._create_project_page(project_id)
def get_session(auth_url, endpoint, domain_id, domain_name, project_id, project_name, project_domain_name, project_domain_id, username, user_id, password, user_domain_id, user_domain_name, token, insecure, cacert, all_tenants=False, edit_managed=False): # NOTE: all_tenants and edit_managed are here for backwards compat # reasons, do not add additional modifiers here. session = ks_session.Session() # Build + Attach Authentication Plugin auth_args = { 'auth_url': auth_url, 'domain_id': domain_id, 'domain_name': domain_name, 'project_id': project_id, 'project_name': project_name, 'project_domain_name': project_domain_name, 'project_domain_id': project_domain_id, } if token and endpoint: session.auth = token_endpoint.Token(endpoint, token) elif token: auth_args.update({'token': token}) session.auth = generic.Token(**auth_args) else: auth_args.update({ 'username': username, 'user_id': user_id, 'password': password, 'user_domain_id': user_domain_id, 'user_domain_name': user_domain_name, }) session.auth = generic.Password(**auth_args) # SSL/TLS Server Cert Verification if insecure is True: session.verify = False else: session.verify = cacert # NOTE: all_tenants and edit_managed are here for backwards compat # reasons, do not add additional modifiers here. session.all_tenants = all_tenants session.edit_managed = edit_managed return session
def _on_project_create(self, project_id): LOG.warning("Beginning wmf hooks for project creation: %s" % project_id) rolelist = self.role_api.list_roles() roledict = {} # Make a dict to relate role names to ids for role in rolelist: roledict[role['name']] = role['id'] if CONF.wmfhooks.observer_role_name not in roledict.keys(): LOG.error("Failed to find id for role %s" % CONF.wmfhooks.observer_role_name) raise exception.NotImplemented() if CONF.wmfhooks.admin_role_name not in roledict.keys(): LOG.error("Failed to find id for role %s" % CONF.wmfhooks.admin_role_name) raise exception.NotImplemented() if CONF.wmfhooks.user_role_name not in roledict.keys(): LOG.error("Failed to find id for role %s" % CONF.wmfhooks.user_role_name) raise exception.NotImplemented() self.assignment_api.add_role_to_user_and_project(CONF.wmfhooks.admin_user, project_id, roledict[CONF.wmfhooks.admin_role_name]) self.assignment_api.add_role_to_user_and_project(CONF.wmfhooks.admin_user, project_id, roledict[CONF.wmfhooks.user_role_name]) self.assignment_api.add_role_to_user_and_project(CONF.wmfhooks.observer_user, project_id, roledict[CONF.wmfhooks.observer_role_name]) # Use the nova api to set up security groups for the new project auth = generic.Password( auth_url=CONF.wmfhooks.auth_url, username=CONF.wmfhooks.admin_user, password=CONF.wmfhooks.admin_pass, user_domain_name='Default', project_domain_name='Default', project_name=project_id) session = keystone_session.Session(auth=auth) client = nova_client.Client('2', session=session) allgroups = client.security_groups.list() defaultgroup = filter(lambda group: group.name == 'default', allgroups) if defaultgroup: groupid = defaultgroup[0].id try: client.security_group_rules.create(groupid, ip_protocol='icmp', from_port='-1', to_port='-1', cidr='0.0.0.0/0') except (exceptions.ClientException): LOG.warning("icmp security rule already exists.") try: client.security_group_rules.create(groupid, ip_protocol='tcp', from_port='22', to_port='22', cidr='10.0.0.0/8') except (exceptions.ClientException): LOG.warning("Port 22 security rule already exists.") try: client.security_group_rules.create(groupid, ip_protocol='tcp', from_port='5666', to_port='5666', cidr='10.0.0.0/8') except (exceptions.ClientException): LOG.warning("Port 5666 security rule already exists.") try: client.security_group_rules.create(groupid, ip_protocol='tcp', from_port='1', to_port='65535', cidr='', group_id=groupid) except (exceptions.ClientException): LOG.warning("Project security rule already exists.") else: LOG.warning("Failed to find default security group in new project.")
def __init__(self, instance_id, common_config, source_config, dest_config): self.dnsdomain = common_config['dnsdomain'] self.instance_id = instance_id self.source_config = source_config self.dest_config = dest_config self.common_config = common_config source_auth = generic.Password( auth_url=self.common_config['keystone_url'], username=self.common_config['user'], password=self.common_config['password'], user_domain_name='Default', project_domain_name='Default', project_name='admin') source_session = keystone_session.Session(auth=source_auth) self.source_novaclient = novaclient.Client( '2', session=source_session, region_name=source_config['region']) self.refresh_instance() self.project_id = self.source_instance.tenant_id self.user_id = self.source_instance.user_id project_auth = generic.Password( auth_url=self.common_config['keystone_url'], username=self.common_config['user'], password=self.common_config['password'], user_domain_name='Default', project_domain_name='Default', project_name=self.project_id) project_session = keystone_session.Session(auth=project_auth) self.designateclient = designateclient.Client( session=project_session, region_name=source_config['region']) self.novaclient_projectscope = novaclient.Client( '2', session=project_session, region_name=dest_config['region']) wmflabs_auth = generic.Password( auth_url=self.common_config['keystone_url'], username=self.common_config['user'], password=self.common_config['password'], user_domain_name='Default', project_domain_name='Default', project_name='wmflabsdotorg') wmflabs_session = keystone_session.Session(auth=wmflabs_auth) self.wmflabsdesignateclient = designateclient.Client( session=wmflabs_session, region_name=source_config['region']) dest_auth = generic.Password( auth_url=self.common_config['keystone_url'], username=self.common_config['user'], password=self.common_config['password'], user_domain_name='Default', project_domain_name='Default', project_name='admin') self.dest_session = keystone_session.Session(auth=dest_auth) self.dest_novaclient = novaclient.Client( '2', session=self.dest_session, region_name=dest_config['region']) self.dest_neutronclient = neutronclient.Client( session=self.dest_session, region_name=dest_config['region']) self.dest_keystoneclient = keystoneclient.Client( session=self.dest_session, region_name=dest_config['region']) self.proxy_endpoint = self.get_proxy_endpoint(self.dest_keystoneclient, dest_config['region'])
import requests import os import time import datetime from designateclient.v2 import client from designateclient import exceptions from designateclient import shell from keystoneclient.auth.identity import generic from keystoneclient import session as keystone_session # Authenticate auth = generic.Password(auth_url=shell.env('OS_AUTH_URL'), username=shell.env('OS_USERNAME'), password=shell.env('OS_PASSWORD'), tenant_name=shell.env('OS_TENANT_NAME')) session = keystone_session.Session(auth=auth) client = client.Client(session=session) # Check if the public_suffix_list is newer than the last change according to the atom feed? r = requests.head("https://publicsuffix.org/list/public_suffix_list.dat") print "" print "" print r.headers['Last-Modified'] list_mod_date = datetime.datetime.strptime(r.headers['Last-Modified'], "%a, %d %b %Y %H:%M:%S %Z") r.close try:
def create_session(timeout=None): auth = generic.Password(auth_url='', username='', password='', tenant_name='') return keystone_session.Session(auth=auth, timeout=timeout)
args = argparser.parse_args() if args.cleanup and args.leak: logging.error("--leak and --cleanup are mutually exclusive") exit(1) config = ScriptConfig(args.datacenter, args.destination, args.mysql_password, args.nova_db_server, args.nova_db, args.cleanup, args.leak) logging.basicConfig(format="%(filename)s: %(levelname)s: %(message)s", level=logging.INFO, stream=sys.stdout) sshargs = ["ssh", "-i", "/root/.ssh/compute-hosts-key", "nova@%s" % config.destination_fqdn, "true"] r = subprocess.call(sshargs) if r: logging.error("remote execution failed; this whole enterprise is doomed.") exit(1) auth = generic.Password( auth_url=args.nova_url, username=args.nova_user, password=args.nova_pass, user_domain_name='Default', project_domain_name='Default', project_name='admin') session = keystone_session.Session(auth=auth) instance = NovaInstance(session, args.instanceid, region=args.region) instance.migrate(config) logging.shutdown()
def main(): argparser = argparse.ArgumentParser() argparser.add_argument('--debug', help='Turn on debug logging', action='store_true') argparser.add_argument( '--project', default='admin-monitoring', help='Set project to test creation for', ) argparser.add_argument( '--keyfile', default='', help='Path to SSH key file for verification', ) argparser.add_argument( '--certkeyfile', default='', help='Path to SSH key file for puppet cert checking', ) argparser.add_argument( '--user', default='', help='Set username (Expected to be the same across all backends)', ) argparser.add_argument( '--certmanager', default='', help='Set username for the puppetmaster certmanager', ) argparser.add_argument( '--puppetmaster', default='', help='fqdn of the cloud frontend puppetmaster', ) argparser.add_argument( '--prepend', default='test-create', help='String to add to beginning of instance names', ) argparser.add_argument( '--max-pool', default=1, type=int, help='Allow this many instances', ) argparser.add_argument('--preserve-leaks', help='Never delete failed VMs', action='store_true') argparser.add_argument( '--keystone-url', default="http://openstack.eqiad1.wikimediacloud.org:35357/v3", help='Auth url for token and service discovery', ) argparser.add_argument( '--interval', default=600, type=int, help='Seconds delay for daemon (default: 600 [10m])', ) argparser.add_argument( '--creation-timeout', default=180, type=int, help='Allow this long for creation to succeed.', ) argparser.add_argument( '--ssh-timeout', default=180, type=int, help='Allow this long for SSH to succeed.', ) argparser.add_argument( '--puppet-timeout', default=120, type=int, help='Allow this long for Puppet to succeed.', ) argparser.add_argument( '--deletion-timeout', default=120, type=int, help='Allow this long for delete to succeed.', ) argparser.add_argument( '--image', default='debian-10.0-buster', help='Image to use', ) argparser.add_argument( '--flavor', default='m1.small', help='Flavor to use', ) argparser.add_argument('--skip-puppet', help='Turn off Puppet validation', action='store_true') argparser.add_argument('--skip-dns', help='Turn off DNS validation', action='store_true') argparser.add_argument( '--dns-resolvers', help='Comma separated list of nameservers', default='208.80.154.143,208.80.154.24', ) argparser.add_argument('--skip-ssh', help='Turn off basic SSH validation', action='store_true') argparser.add_argument('--pause-for-deletion', help='Wait for user input before deletion', action='store_true') argparser.add_argument('--skip-deletion', help='Leave instance behind', action='store_true') argparser.add_argument( '--virthost', default=None, help='Specify a particular host to launch on, e.g. labvirt1001. Default' 'behavior is to use the standard scheduling pool.', ) argparser.add_argument( '--adhoc-command', default='', help='Specify a command over SSH prior to deletion', ) argparser.add_argument( '--network', default='', help='Specify a Neutron network for VMs', ) argparser.add_argument( '--statsd', default='statsd.eqiad.wmnet', help='Send statistics to statsd endpoint', ) args = argparser.parse_args() logging.basicConfig(format='%(asctime)s %(levelname)s %(message)s', level=logging.DEBUG if args.debug else logging.INFO) if args.adhoc_command and args.skip_ssh: logging.error("cannot skip SSH with adhoc command specified") sys.exit(1) try: with open(args.keyfile, 'r') as f: f.read() except: logging.error("keyfile {} cannot be read".format(args.keyfile)) sys.exit(1) pw = os.environ.get('OS_PASSWORD') region = os.environ.get('OS_REGION_NAME') user = os.environ.get('OS_USERNAME') or args.user project = os.environ.get('OS_PROJECT_ID') or args.project if not all([user, pw, project]): logging.error('Set username and password environment variables') sys.exit(1) def stat(metric, value): metric_prepend = 'cloudvps.novafullstack.{}'.format( socket.gethostname()) submit_stat(args.statsd, 8125, metric_prepend, metric, value) while True: loop_start = round(time.time(), 2) auth = generic.Password(auth_url=args.keystone_url, username=user, password=pw, user_domain_name='Default', project_domain_name='Default', project_name=project) sess = keystone_session.Session(auth=auth) nova_conn = nova_client.Client('2', session=sess, region_name=region) prepend = args.prepend date = int(datetime.today().strftime('%Y%m%d%H%M%S')) name = '{}-{}'.format(prepend, date) exist = nova_conn.servers.list() logging.debug(exist) prependinstances = [ server for server in exist if server.human_id.startswith(prepend) ] pexist_count = len(prependinstances) stat('instances.count', pexist_count) stat('instances.max', args.max_pool) # If we're pushing up against max_pool, delete the oldest server if not args.preserve_leaks and pexist_count >= args.max_pool - 1: logging.warning("There are {} leaked instances with prepend {}; " "cleaning up".format(pexist_count, prepend)) servers = sorted(prependinstances, key=lambda server: server.human_id) servers[0].delete() if pexist_count >= args.max_pool: # If the cleanup in the last two cycles didn't get us anywhere, # best to just bail out so we stop trampling on the API. logging.error( "max server(s) with prepend {} -- skipping creation".format( prepend)) continue cimage = nova_conn.glance.find_image(args.image) cflavor = nova_conn.flavors.find(name=args.flavor) try: vc, server = verify_create(nova_conn, name, cimage, cflavor, args.creation_timeout, args.network, args.virthost) stat('verify.creation', vc) if 'public' in server.addresses: addr = server.addresses['public'][0]['addr'] if not addr.startswith('10.'): raise Exception("Bad address of {}".format(addr)) else: addr = server.addresses['lan-flat-cloudinstances2b'][0]['addr'] if not addr.startswith('172.'): raise Exception("Bad address of {}".format(addr)) if not args.skip_dns: host = '{}.{}.eqiad.wmflabs'.format(server.name, server.tenant_id) dnsd = args.dns_resolvers.split(',') vdns = verify_dns(host, dnsd, timeout=2.0) stat('verify.dns', vdns) if not args.skip_ssh: vs = verify_ssh(addr, user, args.keyfile, args.ssh_timeout) stat('verify.ssh', vs) if args.adhoc_command: sshout = run_remote(addr, user, args.keyfile, args.adhoc_command, debug=args.debug) logging.debug(sshout) if not args.skip_puppet: ps, puppetrun = verify_puppet(addr, user, args.keyfile, args.puppet_timeout) stat('verify.puppet', ps) categories = ['changes', 'events', 'resources', 'time'] for d in categories: for k, v in puppetrun[d].iteritems(): stat('puppet.{}.{}'.format(d, k), v) if args.pause_for_deletion: logging.info("Pausing for deletion") get_verify('continue with deletion', 'Not a valid response', ['y']) if not args.skip_deletion: vd = verify_deletion(nova_conn, server, args.deletion_timeout) if not args.pause_for_deletion: stat('verify.deletion', vd) loop_end = time.time() stat('verify.fullstack', round(loop_end - loop_start, 2)) if not args.skip_dns: host = '{}.{}.eqiad.wmflabs'.format(server.name, server.tenant_id) dnsd = args.dns_resolvers.split(',') vdns = verify_dns_cleanup(host, dnsd, timeout=60.0) stat('verify.dns-cleanup', vdns) if not args.skip_puppet: host = '{}.{}.eqiad.wmflabs'.format(server.name, server.tenant_id) certs = verify_puppet_cert_cleanup(args.puppetmaster, host, args.certmanager, args.certkeyfile, timeout=20.0) stat('verify.puppet-cert-cleanup', certs) if not args.interval: return stat('verify.success', 1) except: logging.exception("{} failed, leaking".format(name)) stat('verify.success', 0) time.sleep(args.interval)
def delete(self, request, obj_id): record = obj_id[:obj_id.find('.')] domain = obj_id[obj_id.find('.') + 1:] if not domain.endswith('.'): domain += '.' # First let's make sure that this proxy is really ours to delete. existing_domains = [proxy.domain for proxy in get_proxy_list(request)] if obj_id not in existing_domains: raise Exception( "Proxy \'%s\' is to be deleted but is not owned by this view." % obj_id) if domain == 'wmflabs.org.': auth = identity_generic.Password( auth_url=base.url_for(request, 'identity'), username=getattr(settings, "WMFLABSDOTORG_ADMIN_USERNAME", ''), password=getattr(settings, "WMFLABSDOTORG_ADMIN_PASSWORD", ''), tenant_name='wmflabsdotorg', user_domain_id='default', project_domain_id='default') c = designateclientv2.Client(session=keystone_session.Session( auth=auth)) # Delete the record from the wmflabsdotorg project. This is needed # since wmflabs.org lives in that project and designate (quite # reasonably) prevents subdomain deletion elsewhere. zoneid = None for zone in c.zones.list(): if zone['name'] == 'wmflabs.org.': zoneid = zone['id'] break else: raise Exception("No zone ID") recordsetid = None for recordset in c.recordsets.list(zoneid): if recordset['type'] == 'A' and recordset[ 'name'] == record + '.' + domain: recordsetid = recordset['id'] break else: raise Exception("No recordset ID") c.recordsets.delete(zoneid, recordsetid) else: c = designateapi.designateclient(request) domainid = None for d in c.domains.list(): if d.name == domain: domainid = d.id break else: LOG.warn('Woops! Failed domain ID for domain ' + domain) raise Exception("No domain ID") recordid = None for r in c.records.list(domainid): if r.name == obj_id and r.type == 'A': recordid = r.id break else: LOG.warn('Woops! Failed record ID for record ' + record) raise Exception("No record ID") c.records.delete(domainid, recordid) resp = requests.delete( base.url_for(request, 'proxy') + '/mapping/' + obj_id) if not resp: raise Exception("Got status " + resp.status_code)