def _get_certs_from_pkcs7_substrate(substrate): """Extracts DER-encoded X509 certificates from a PKCS7 ASN1 DER substrate :param substrate: The substrate to be processed :returns: A list of DER-encoded X509 certificates """ try: contentInfo, _ = der_decoder.decode(substrate, asn1Spec=rfc2315.ContentInfo()) contentType = contentInfo.getComponentByName('contentType') except Exception: LOG.exception(_LE('Unreadable Certificate.')) raise exceptions.UnreadableCert if contentType != rfc2315.signedData: LOG.exception(_LE('Unreadable Certificate.')) raise exceptions.UnreadableCert try: content, _ = der_decoder.decode( contentInfo.getComponentByName('content'), asn1Spec=rfc2315.SignedData()) except Exception: LOG.exception(_LE('Unreadable Certificate.')) raise exceptions.UnreadableCert for cert in content.getComponentByName('certificates'): yield der_encoder.encode(cert)
def unplug_network(self, compute_id, network_id, ip_address=None): interfaces = self.get_plugged_networks(compute_id) if not interfaces: msg = ('Amphora with compute id {compute_id} does not have any ' 'plugged networks').format(compute_id=compute_id) raise base.AmphoraNotFound(msg) unpluggers = self._get_interfaces_to_unplug(interfaces, network_id, ip_address=ip_address) try: for index, unplugger in enumerate(unpluggers): self.nova_client.servers.interface_detach( server=compute_id, port_id=unplugger.port_id) except Exception: message = _LE('Error unplugging amphora {amphora_id} from network ' '{network_id}.').format(amphora_id=compute_id, network_id=network_id) if len(unpluggers) > 1: message = _LE('{base} Other interfaces have been successfully ' 'unplugged: ').format(base=message) unpluggeds = unpluggers[:index] for unplugged in unpluggeds: message = _LE('{base} neutron port ' '{port_id} ').format( base=message, port_id=unplugged.port_id) else: message = _LE('{base} No other networks were ' 'unplugged.').format(base=message) LOG.exception(message) raise base.UnplugNetworkException(message)
def get_cert(cert_ref, **kwargs): """Retrieves the specified cert. :param cert_ref: the UUID of the cert to retrieve :return: octavia.certificates.common.Cert representation of the certificate data :raises CertificateStorageException: if certificate retrieval fails """ LOG.info(_LI( "Loading certificate {0} from the local filesystem." ).format(cert_ref)) filename_base = os.path.join(CONF.certificates.storage_path, cert_ref) filename_certificate = "{0}.crt".format(filename_base, cert_ref) filename_private_key = "{0}.key".format(filename_base, cert_ref) filename_intermediates = "{0}.int".format(filename_base, cert_ref) filename_pkp = "{0}.pass".format(filename_base, cert_ref) cert_data = dict() try: with open(filename_certificate, 'r') as cert_file: cert_data['certificate'] = cert_file.read() except IOError: LOG.error(_LE( "Failed to read certificate for {0}." ).format(cert_ref)) raise exceptions.CertificateStorageException( msg="Certificate could not be read." ) try: with open(filename_private_key, 'r') as key_file: cert_data['private_key'] = key_file.read() except IOError: LOG.error(_LE( "Failed to read private key for {0}." ).format(cert_ref)) raise exceptions.CertificateStorageException( msg="Private Key could not be read." ) try: with open(filename_intermediates, 'r') as int_file: cert_data['intermediates'] = int_file.read() except IOError: pass try: with open(filename_pkp, 'r') as pass_file: cert_data['private_key_passphrase'] = pass_file.read() except IOError: pass return local_common.LocalCert(**cert_data)
def unplug_vip(self, load_balancer, vip): try: subnet = self.get_subnet(vip.subnet_id) except base.SubnetNotFound: msg = _LE("Can't unplug vip because vip subnet {0} was not " "found").format(vip.subnet_id) LOG.exception(msg) raise base.PluggedVIPNotFound(msg) for amphora in six.moves.filter( lambda amp: amp.status == constants.AMPHORA_ALLOCATED, load_balancer.amphorae): interface = self._get_plugged_interface(amphora.compute_id, subnet.network_id) if not interface: # Thought about raising PluggedVIPNotFound exception but # then that wouldn't evaluate all amphorae, so just continue LOG.debug(_LI('Cannot get amphora %s interface, skipped'), amphora.compute_id) continue try: self.unplug_network(amphora.compute_id, subnet.network_id) except Exception: pass try: aap_update = {'port': { 'allowed_address_pairs': [] }} self.neutron_client.update_port(interface.port_id, aap_update) except Exception: message = _LE('Error unplugging VIP. Could not clear ' 'allowed address pairs from port ' '{port_id}.').format(port_id=vip.port_id) LOG.exception(message) raise base.UnplugVIPException(message) # Delete the VRRP port if we created it try: port = self.get_port(amphora.vrrp_port_id) if port.name.startswith('octavia-lb-vrrp-'): self.neutron_client.delete_port(amphora.vrrp_port_id) except base.PortNotFound: pass except Exception as e: LOG.error(_LE('Failed to delete port. Resources may still ' 'be in use for port: %(port)s due to ' 'error: %s(except)s'), {'port': amphora.vrrp_port_id, 'except': e})
def plug_port(self, amphora, port): plugged_interface = None try: interface = self.nova_client.servers.interface_attach( server=amphora.compute_id, net_id=None, fixed_ip=None, port_id=port.id) plugged_interface = self._nova_interface_to_octavia_interface( amphora.compute_id, interface) except nova_client_exceptions.NotFound as e: if 'Instance' in e.message: raise base.AmphoraNotFound(e.message) elif 'Network' in e.message: raise base.NetworkNotFound(e.message) else: raise base.PlugNetworkException(e.message) except nova_client_exceptions.Conflict: LOG.info(_LI('Port %(portid)s is already plugged, ' 'skipping') % {'portid': port.id}) plugged_interface = n_data_models.Interface( compute_id=amphora.compute_id, network_id=port.network_id, port_id=port.id, fixed_ips=port.fixed_ips) except Exception: message = _LE('Error plugging amphora (compute_id: ' '{compute_id}) into port ' '{port_id}.').format( compute_id=amphora.compute_id, port_id=port.id) LOG.exception(message) raise base.PlugNetworkException(message) return plugged_interface
def deallocate_vip(self, vip): """Delete the vrrp_port (instance port) in case nova didn't This can happen if a failover has occurred. """ try: for amphora in six.moves.filter(self._filter_amphora, vip.load_balancer.amphorae): self.neutron_client.delete_port(amphora.vrrp_port_id) except (neutron_client_exceptions.NotFound, neutron_client_exceptions.PortNotFoundClient): LOG.debug('VIP instance port {0} already deleted. ' 'Skipping.'.format(amphora.vrrp_port_id)) try: port = self.get_port(vip.port_id) except base.PortNotFound: msg = ("Can't deallocate VIP because the vip port {0} cannot be " "found in neutron".format(vip.port_id)) raise base.VIPConfigurationNotFound(msg) self._delete_security_group(vip, port) if port.device_owner == OCTAVIA_OWNER: try: self.neutron_client.delete_port(vip.port_id) except Exception: message = _LE('Error deleting VIP port_id {port_id} from ' 'neutron').format(port_id=vip.port_id) LOG.exception(message) raise base.DeallocateVIPException(message) else: LOG.info(_LI("Port %s will not be deleted by Octavia as it was " "not created by Octavia."), vip.port_id)
def get_glance_client(cls, region, service_name=None, endpoint=None, endpoint_type='publicURL', insecure=False, cacert=None): """Create glance client object. :param region: The region of the service :param service_name: The name of the glance service in the catalog :param endpoint: The endpoint of the service :param endpoint_type: The endpoint_type of the service :param insecure: Turn off certificate validation :param cacert: CA Cert file path :return: a Glance Client object. :raises Exception: if the client cannot be created """ if not cls.glance_client: kwargs = {'region_name': region, 'session': keystone.get_session(), 'interface': endpoint_type} if service_name: kwargs['service_name'] = service_name if endpoint: kwargs['endpoint'] = endpoint if endpoint.startswith("https"): kwargs['insecure'] = insecure kwargs['cacert'] = cacert try: cls.glance_client = glance_client.Client( GLANCE_VERSION, **kwargs) except Exception: with excutils.save_and_reraise_exception(): LOG.exception(_LE("Error creating Glance client.")) return cls.glance_client
def unplug_vip(self, load_balancer, vip): try: subnet = self.get_subnet(vip.subnet_id) except base.SubnetNotFound: msg = ("Can't unplug vip because vip subnet {0} was not " "found").format(vip.subnet_id) raise base.PluggedVIPNotFound(msg) for amphora in six.moves.filter( lambda amp: amp.status == constants.AMPHORA_ALLOCATED, load_balancer.amphorae): interface = self._get_plugged_interface(amphora.compute_id, subnet.network_id) if not interface: # Thought about raising PluggedVIPNotFound exception but # then that wouldn't evaluate all amphorae, so just continue continue try: self.unplug_network(amphora.compute_id, subnet.network_id) except Exception: pass try: aap_update = {'port': { 'allowed_address_pairs': [] }} self.neutron_client.update_port(interface.port_id, aap_update) except Exception: message = _LE('Error unplugging VIP. Could not clear ' 'allowed address pairs from port ' '{port_id}.').format(port_id=vip.port_id) LOG.exception(message) raise base.UnplugVIPException(message)
def allocate_vip(self, load_balancer): if not load_balancer.vip.port_id and not load_balancer.vip.subnet_id: raise base.AllocateVIPException('Cannot allocate a vip ' 'without a port_id or ' 'a subnet_id.') if load_balancer.vip.port_id: LOG.info(_LI('Port %s already exists. Nothing to be done.'), load_balancer.vip.port_id) port = self.get_port(load_balancer.vip.port_id) return self._port_to_vip(port, load_balancer) # Must retrieve the network_id from the subnet subnet = self.get_subnet(load_balancer.vip.subnet_id) # It can be assumed that network_id exists port = {'port': {'name': 'octavia-lb-' + load_balancer.id, 'network_id': subnet.network_id, 'admin_state_up': False, 'device_id': 'lb-{0}'.format(load_balancer.id), 'device_owner': OCTAVIA_OWNER}} try: new_port = self.neutron_client.create_port(port) except Exception: message = _LE('Error creating neutron port on network ' '{network_id}.').format( network_id=subnet.network_id) LOG.exception(message) raise base.AllocateVIPException(message) new_port = utils.convert_port_dict_to_model(new_port) return self._port_to_vip(new_port, load_balancer)
def _actually_delete_cert(cert_ref): """Deletes the specified cert. Very dangerous. Do not recommend. :param cert_ref: the UUID of the cert to delete :raises Exception: if certificate deletion fails """ connection = barbican_common.BarbicanAuth.get_barbican_client() LOG.info(_LI( "Recursively deleting certificate container {0} from Barbican." ).format(cert_ref)) try: certificate_container = connection.containers.get(cert_ref) certificate_container.certificate.delete() if certificate_container.intermediates: certificate_container.intermediates.delete() if certificate_container.private_key_passphrase: certificate_container.private_key_passphrase.delete() certificate_container.private_key.delete() certificate_container.delete() except Exception as e: with excutils.save_and_reraise_exception(): LOG.error(_LE( "Error recursively deleting container {0}: {1}" ).format(cert_ref, str(e)))
def get_cert(cert_ref, resource_ref=None, check_only=False, service_name='Octavia'): """Retrieves the specified cert and registers as a consumer. :param cert_ref: the UUID of the cert to retrieve :param resource_ref: Full HATEOAS reference to the consuming resource :param check_only: Read Certificate data without registering :param service_name: Friendly name for the consuming service :return: octavia.certificates.common.Cert representation of the certificate data :raises Exception: if certificate retrieval fails """ connection = barbican_common.BarbicanAuth.get_barbican_client() LOG.info(_LI( "Loading certificate container {0} from Barbican." ).format(cert_ref)) try: if check_only: cert_container = connection.containers.get( container_ref=cert_ref ) else: cert_container = connection.containers.register_consumer( container_ref=cert_ref, name=service_name, url=resource_ref ) return barbican_common.BarbicanCert(cert_container) except Exception as e: with excutils.save_and_reraise_exception(): LOG.error(_LE( "Error getting {0}: {1}" ).format(cert_ref, str(e)))
def delete_cert(cert_ref, resource_ref=None, service_name='Octavia'): """Deregister as a consumer for the specified cert. :param cert_ref: the UUID of the cert to retrieve :param resource_ref: Full HATEOAS reference to the consuming resource :param service_name: Friendly name for the consuming service :raises Exception: if deregistration fails """ connection = barbican_common.BarbicanAuth.get_barbican_client() LOG.info(_LI( "Deregistering as a consumer of {0} in Barbican." ).format(cert_ref)) try: connection.containers.remove_consumer( container_ref=cert_ref, name=service_name, url=resource_ref ) except Exception as e: with excutils.save_and_reraise_exception(): LOG.error(_LE( "Error deregistering as a consumer of {0}: {1}" ).format(cert_ref, str(e)))
def get_create_load_balancer_flow(self, topology): """Creates a conditional graph flow that allocates a loadbalancer to two spare amphorae. :raises InvalidTopology: Invalid topology specified :return: The graph flow for creating an active_standby loadbalancer. """ f_name = constants.CREATE_LOADBALANCER_FLOW lb_create_flow = unordered_flow.Flow(f_name) if topology == constants.TOPOLOGY_ACTIVE_STANDBY: master_amp_sf = self.amp_flows.get_amphora_for_lb_subflow( prefix=constants.ROLE_MASTER, role=constants.ROLE_MASTER) backup_amp_sf = self.amp_flows.get_amphora_for_lb_subflow( prefix=constants.ROLE_BACKUP, role=constants.ROLE_BACKUP) lb_create_flow.add(master_amp_sf, backup_amp_sf) elif topology == constants.TOPOLOGY_SINGLE: amphora_sf = self.amp_flows.get_amphora_for_lb_subflow( prefix=constants.ROLE_STANDALONE, role=constants.ROLE_STANDALONE) lb_create_flow.add(amphora_sf) else: LOG.error(_LE("Unknown topology: %s. Unable to build load " "balancer."), topology) raise exceptions.InvalidTopology(topology=topology) return lb_create_flow
def get_nova_client(cls, region, service_name=None, endpoint=None, endpoint_type='publicURL', insecure=False, cacert=None): """Create nova client object. :param region: The region of the service :param service_name: The name of the nova service in the catalog :param endpoint: The endpoint of the service :param endpoint_type: The type of the endpoint :param insecure: Turn off certificate validation :param cacert: CA Cert file path :return: a Nova Client object. :raises Exception: if the client cannot be created """ if not cls.nova_client: kwargs = {'region_name': region, 'session': keystone.get_session(), 'endpoint_type': endpoint_type, 'insecure': insecure} if service_name: kwargs['service_name'] = service_name if endpoint: kwargs['endpoint_override'] = endpoint if cacert: kwargs['cacert'] = cacert try: cls.nova_client = nova_client.Client( NOVA_VERSION, **kwargs) except Exception: with excutils.save_and_reraise_exception(): LOG.exception(_LE("Error creating Nova client.")) return cls.nova_client
def execute(self, deltas): """Handle network plugging based off deltas.""" added_ports = {} for amp_id, delta in six.iteritems(deltas): added_ports[amp_id] = [] for nic in delta.add_nics: interface = self.network_driver.plug_network(delta.compute_id, nic.network_id) port = self.network_driver.get_port(interface.port_id) port.network = self.network_driver.get_network(port.network_id) for fixed_ip in port.fixed_ips: fixed_ip.subnet = self.network_driver.get_subnet( fixed_ip.subnet_id) added_ports[amp_id].append(port) for nic in delta.delete_nics: try: self.network_driver.unplug_network(delta.compute_id, nic.network_id) except base.NetworkNotFound: LOG.debug("Network %d not found ", nic.network_id) except Exception as e: LOG.error( _LE("Unable to unplug network - exception: %s"), e) return added_ports
def get_neutron_client(cls, region, service_name=None, endpoint=None, endpoint_type='publicURL'): """Create neutron client object. :param region: The region of the service :param service_name: The name of the neutron service in the catalog :param endpoint: The endpoint of the service :param endpoint_type: The endpoint_type of the service :return: a Neutron Client object. :raises Exception: if the client cannot be created """ if not cls.neutron_client: kwargs = {'region_name': region, 'session': keystone.get_session(), 'endpoint_type': endpoint_type} if service_name: kwargs['service_name'] = service_name if endpoint: kwargs['endpoint_override'] = endpoint try: cls.neutron_client = neutron_client.Client( NEUTRON_VERSION, **kwargs) except Exception: with excutils.save_and_reraise_exception(): LOG.exception(_LE("Error creating Neutron client.")) return cls.neutron_client
def get_subnet(self, subnet_id): try: subnet = self.neutron_client.show_subnet(subnet_id) return utils.convert_subnet_dict_to_model(subnet) except neutron_client_exceptions.NotFound: message = _LE('Subnet not found ' '(subnet id: {subnet_id}.').format( subnet_id=subnet_id) LOG.exception(message) raise base.SubnetNotFound(message) except Exception: message = _LE('Error retrieving subnet ' '(subnet id: {subnet_id}.').format( subnet_id=subnet_id) LOG.exception(message) raise base.NetworkException(message)
def deallocate_vip(self, vip): try: port = self.get_port(vip.port_id) except base.PortNotFound: msg = ("Can't deallocate VIP because the vip port {0} cannot be " "found in neutron".format(vip.port_id)) raise base.VIPConfigurationNotFound(msg) if port.device_owner != OCTAVIA_OWNER: LOG.info(_LI("Port %s will not be deleted by Octavia as it was " "not created by Octavia."), vip.port_id) if self.sec_grp_enabled: sec_grp = self._get_lb_security_group(vip.load_balancer.id) sec_grp = sec_grp.get('id') LOG.info( _LI("Removing security group %(sg)s from port %(port)s"), {'sg': sec_grp, 'port': vip.port_id}) raw_port = self.neutron_client.show_port(port.id) sec_grps = raw_port.get('port', {}).get('security_groups', []) if sec_grp in sec_grps: sec_grps.remove(sec_grp) port_update = {'port': {'security_groups': sec_grps}} self.neutron_client.update_port(port.id, port_update) self._delete_vip_security_group(sec_grp) return try: self.neutron_client.delete_port(vip.port_id) except Exception: message = _LE('Error deleting VIP port_id {port_id} from ' 'neutron').format(port_id=vip.port_id) LOG.exception(message) raise base.DeallocateVIPException(message) if self.sec_grp_enabled: sec_grp = self._get_lb_security_group(vip.load_balancer.id) sec_grp = sec_grp.get('id') self._delete_vip_security_group(sec_grp)
def get_glance_client(cls, region, service_name=None, endpoint=None, endpoint_type='publicURL'): """Create glance client object. :param region: The region of the service :param service_name: The name of the glance service in the catalog :param endpoint: The endpoint of the service :param endpoint_type: The endpoint_type of the service :return: a Glance Client object. :raises Exception: if the client cannot be created """ if not cls.glance_client: kwargs = {'region_name': region, 'session': keystone.get_session(), 'interface': endpoint_type} if service_name: kwargs['service_name'] = service_name if endpoint: kwargs['endpoint'] = endpoint try: cls.glance_client = glance_client.Client( GLANCE_VERSION, **kwargs) except Exception: with excutils.save_and_reraise_exception(): LOG.exception(_LE("Error creating Glance client.")) return cls.glance_client
def get_port(self, port_id): try: port = self.neutron_client.show_port(port_id) return utils.convert_port_dict_to_model(port) except neutron_client_exceptions.NotFound: message = _LE('Port not found ' '(port id: {port_id}.').format( port_id=port_id) LOG.exception(message) raise base.PortNotFound(message) except Exception: message = _LE('Error retrieving port ' '(port id: {port_id}.').format( port_id=port_id) LOG.exception(message) raise base.NetworkException(message)
def get_host_names(certificate): """Extract the host names from the Pem encoded X509 certificate :param certificate: A PEM encoded certificate :returns: A dictionary containing the following keys: ['cn', 'dns_names'] where 'cn' is the CN from the SubjectName of the certificate, and 'dns_names' is a list of dNSNames (possibly empty) from the SubjectAltNames of the certificate. """ try: certificate = certificate.encode("ascii") cert = x509.load_pem_x509_certificate(certificate, backends.default_backend()) cn = cert.subject.get_attributes_for_oid(x509.OID_COMMON_NAME)[0] host_names = {"cn": cn.value.lower(), "dns_names": []} try: ext = cert.extensions.get_extension_for_oid(x509.OID_SUBJECT_ALTERNATIVE_NAME) host_names["dns_names"] = ext.value.get_values_for_type(x509.DNSName) except x509.ExtensionNotFound: LOG.debug("%s extension not found", x509.OID_SUBJECT_ALTERNATIVE_NAME) return host_names except Exception: LOG.exception(_LE("Unreadable certificate.")) raise exceptions.UnreadableCert
def delete_cert(project_id, cert_ref, **kwargs): """Deletes the specified cert. :param project_id: Ignored in this implementation :param cert_ref: the UUID of the cert to delete :raises CertificateStorageException: if certificate deletion fails """ LOG.info(_LI("Deleting certificate %s from the local filesystem."), cert_ref) filename_base = os.path.join(CONF.certificates.storage_path, cert_ref) filename_certificate = "{0}.crt".format(filename_base) filename_private_key = "{0}.key".format(filename_base) filename_intermediates = "{0}.int".format(filename_base) filename_pkp = "{0}.pass".format(filename_base) try: os.remove(filename_certificate) os.remove(filename_private_key) os.remove(filename_intermediates) os.remove(filename_pkp) except IOError as ioe: LOG.error(_LE("Failed to delete certificate %s"), cert_ref) raise exceptions.CertificateStorageException(message=ioe.message)
def _delete_vip_security_group(self, sec_grp): """Deletes a security group in neutron. Retries upon an exception because removing a security group from a neutron port does not happen immediately. """ attempts = 0 while attempts <= CONF.networking.max_retries: try: self.neutron_client.delete_security_group(sec_grp) LOG.info(_LI("Deleted security group %s"), sec_grp) return except neutron_client_exceptions.NotFound: LOG.info(_LI("Security group %s not found, will assume it is " "already deleted"), sec_grp) return except Exception: LOG.warning(_LW("Attempt %(attempt)s to remove security group " "%(sg)s failed."), {'attempt': attempts + 1, 'sg': sec_grp}) attempts += 1 time.sleep(CONF.networking.retry_interval) message = _LE("All attempts to remove security group {0} have " "failed.").format(sec_grp) LOG.exception(message) raise base.DeallocateVIPException(message)
def failover_amphora(self, amphora_id): """Perform failover operations for an amphora. :param amphora_id: ID for amphora to failover :returns: None :raises AmphoraNotFound: The referenced amphora was not found """ try: amp = self._amphora_repo.get(db_apis.get_session(), id=amphora_id) failover_amphora_tf = self._taskflow_load( self._amphora_flows.get_failover_flow(role=amp.role), store={constants.FAILED_AMPHORA: amp, constants.LOADBALANCER_ID: amp.load_balancer_id}) with tf_logging.DynamicLoggingListener( failover_amphora_tf, log=LOG, hide_inputs_outputs_of=self._exclude_result_logging_tasks): failover_amphora_tf.run() except Exception as e: with excutils.save_and_reraise_exception(): LOG.error(_LE("Failover exception: %s") % e)
def get_network(self, network_id): try: network = self.neutron_client.show_network(network_id) return utils.convert_network_dict_to_model(network) except neutron_client_exceptions.NotFound: message = _LE('Network not found ' '(network id: {network_id}.').format( network_id=network_id) LOG.exception(message) raise base.NetworkNotFound(message) except Exception: message = _LE('Error retrieving network ' '(network id: {network_id}.').format( network_id=network_id) LOG.exception(message) raise base.NetworkException(message)
def __init__(self, cert_container): if not isinstance(cert_container, barbican_client.containers.CertificateContainer): raise TypeError(_LE( "Retrieved Barbican Container is not of the correct type " "(certificate).")) self._cert_container = cert_container
def execute(self, amphora_id, ports=None, config_drive_files=None): """Create an amphora :returns: an amphora """ ports = ports or [] config_drive_files = config_drive_files or {} LOG.debug("Compute create execute for amphora with id %s", amphora_id) ssh_access = CONF.controller_worker.amp_ssh_access_allowed ssh_key = CONF.controller_worker.amp_ssh_key_name key_name = None if not ssh_access else ssh_key try: agent_cfg = agent_jinja_cfg.AgentJinjaTemplater() config_drive_files['/etc/octavia/amphora-agent.conf'] = ( agent_cfg.build_agent_config(amphora_id)) compute_id = self.compute.build( name="amphora-" + amphora_id, amphora_flavor=CONF.controller_worker.amp_flavor_id, image_id=CONF.controller_worker.amp_image_id, key_name=key_name, sec_groups=CONF.controller_worker.amp_secgroup_list, network_ids=[CONF.controller_worker.amp_network], port_ids=[port.id for port in ports], config_drive_files=config_drive_files) LOG.debug("Server created with id: %s for amphora id: %s", compute_id, amphora_id) return compute_id except Exception: LOG.exception(_LE("Compute create for amphora id: %s failed"), amphora_id) raise
def execute(self, amphora_id, ports=None, config_drive_files=None): """Create an amphora :returns: an amphora """ ports = ports or [] config_drive_files = config_drive_files or {} LOG.debug("Nova Create execute for amphora with id %s" % amphora_id) try: agent_cfg = agent_jinja_cfg.AgentJinjaTemplater() config_drive_files['/etc/octavia/amphora-agent.conf'] = ( agent_cfg.build_agent_config(amphora_id)) compute_id = self.compute.build( name="amphora-" + amphora_id, amphora_flavor=CONF.controller_worker.amp_flavor_id, image_id=CONF.controller_worker.amp_image_id, key_name=CONF.controller_worker.amp_ssh_key_name, sec_groups=CONF.controller_worker.amp_secgroup_list, network_ids=[CONF.controller_worker.amp_network], port_ids=[port.id for port in ports], config_drive_files=config_drive_files) LOG.debug("Server created with id: %s for amphora id: %s" % (compute_id, amphora_id)) return compute_id except Exception as e: LOG.error(_LE("Nova create for amphora id: %(amp)s " "failed: %(exp)s"), {'amp': amphora_id, 'exp': e}) raise e
def sign_cert(cls, csr, validity=None, **kwargs): """Signs a certificate using Anchor based on the specified CSR :param csr: A Certificate Signing Request :param validity: Will be ignored for now :param kwargs: Will be ignored for now :return: Signed certificate :raises Exception: if certificate signing fails """ LOG.debug("Signing a certificate request using Anchor") try: LOG.debug('Certificate: %s', csr) r = requests.post(CONF.anchor.url, data={ 'user': CONF.anchor.username, 'secret': CONF.anchor.password, 'encoding': 'pem', 'csr': csr}) if r.status_code != 200: LOG.debug('Anchor returned: %s', r.content) raise AnchorException("Anchor returned Status Code : " + str(r.status_code)) return r.content except Exception as e: LOG.error(_LE("Unable to sign certificate.")) raise exceptions.CertificateGenerationException(msg=e)
def build( self, name="amphora_name", amphora_flavor=None, image_id=None, key_name=None, sec_groups=None, network_ids=None, port_ids=None, config_drive_files=None, user_data=None, ): """Create a new virtual machine. :param name: optional name for amphora :param amphora_flavor: image flavor for virtual machine :param image_id: image ID for virtual machine :param key_name: keypair to add to the virtual machine :param sec_groups: Security group IDs for virtual machine :param network_ids: Network IDs to include on virtual machine :param port_ids: Port IDs to include on virtual machine :param config_drive_files: An optional dict of files to overwrite on the server upon boot. Keys are file names (i.e. /etc/passwd) and values are the file contents (either as a string or as a file-like object). A maximum of five entries is allowed, and each file must be 10k or less. :param user_data: Optional user data to pass to be exposed by the metadata server this can be a file type object as well or a string :raises NovaBuildException: if nova failed to build virtual machine :returns: UUID of amphora """ try: network_ids = network_ids or [] port_ids = port_ids or [] nics = [] if network_ids: nics.extend([{"net-id": net_id} for net_id in network_ids]) if port_ids: nics.extend([{"port-id": port_id} for port_id in port_ids]) amphora = self.manager.create( name=name, image=image_id, flavor=amphora_flavor, key_name=key_name, security_groups=sec_groups, nics=nics, files=config_drive_files, userdata=user_data, config_drive=True, ) return amphora.id except Exception: LOG.exception(_LE("Error building nova virtual machine.")) raise exceptions.ComputeBuildException()
def mark_amphora_status_error(self, amphora_id): """Sets an amphora status to ERROR. NOTE: This should only be called from revert methods. :param amphora_id: Amphora ID to set the status to ERROR """ try: self.amphora_repo.update(db_apis.get_session(), id=amphora_id, status=constants.ERROR) except Exception as e: LOG.error( _LE("Failed to update amphora %(amp)s " "status to ERROR due to: " "%(except)s"), { 'amp': amphora_id, 'except': e })
def mark_health_mon_prov_status_error(self, health_mon_id): """Sets a health monitor provisioning status to ERROR. NOTE: This should only be called from revert methods. :param health_mon_id: Health Monitor ID to set prov status to ERROR """ try: self.health_mon_repo.update(db_apis.get_session(), pool_id=health_mon_id, provisioning_status=constants.ERROR) except Exception as e: LOG.error( _LE("Failed to update health monitor %(health)s " "provisioning status to ERROR due to: " "%(except)s"), { 'health': health_mon_id, 'except': e })
def mark_l7rule_prov_status_error(self, l7rule_id): """Sets a L7 rule provisioning status to ERROR. NOTE: This should only be called from revert methods. :param l7rule_id: L7 Rule ID to set provisioning status to ERROR """ try: self.l7rule_repo.update(db_apis.get_session(), id=l7rule_id, provisioning_status=constants.ERROR) except Exception as e: LOG.error( _LE("Failed to update l7rule %(l7r)s " "provisioning status to ERROR due to: " "%(except)s"), { 'l7r': l7rule_id, 'except': e })
def mark_pool_prov_status_error(self, pool_id): """Sets a pool provisioning status to ERROR. NOTE: This should only be called from revert methods. :param pool_id: Pool ID to set provisioning status to ERROR """ try: self.pool_repo.update(db_apis.get_session(), id=pool_id, provisioning_status=constants.ERROR) except Exception as e: LOG.error( _LE("Failed to update pool %(pool)s " "provisioning status to ERROR due to: " "%(except)s"), { 'pool': pool_id, 'except': e })
def mark_listener_prov_status_error(self, listener_id): """Sets a listener provisioning status to ERROR. NOTE: This should only be called from revert methods. :param listener_id: Listener ID to set provisioning status to ERROR """ try: self.listener_repo.update(db_apis.get_session(), id=listener_id, provisioning_status=constants.ERROR) except Exception as e: LOG.error( _LE("Failed to update listener %(list)s " "provisioning status to ERROR due to: " "%(except)s"), { 'list': listener_id, 'except': e })
def _parse_pkcs7_bundle(pkcs7): """Parse a PKCS7 certificate bundle in DER or PEM format :param pkcs7: A pkcs7 bundle in DER or PEM format :returns: A list of individual DER-encoded certificates """ # Look for PEM encoding if PKCS7_BEG in pkcs7: try: for substrate in _read_pem_blocks(pkcs7): for cert in _get_certs_from_pkcs7_substrate(substrate): yield cert except Exception: LOG.exception(_LE('Unreadable Certificate.')) raise exceptions.UnreadableCert # If no PEM encoding, assume this is DER encoded and try to decode else: for cert in _get_certs_from_pkcs7_substrate(pkcs7): yield cert
def mark_loadbalancer_prov_status_active(self, loadbalancer_id): """Sets a load balancer provisioning status to ACTIVE. NOTE: This should only be called from revert methods. :param loadbalancer_id: Load balancer ID to set provisioning status to ACTIVE """ try: self.loadbalancer_repo.update(db_apis.get_session(), id=loadbalancer_id, provisioning_status=constants.ACTIVE) except Exception as e: LOG.error( _LE("Failed to update load balancer %(lb)s " "provisioning status to ACTIVE due to: " "%(except)s"), { 'lb': loadbalancer_id, 'except': e })
def get_session(): """Initializes a Keystone session. :return: a Keystone Session object :raises Exception: if the session cannot be established """ global _SESSION if not _SESSION: kwargs = { 'auth_url': cfg.CONF.keystone_authtoken.auth_uri, 'username': cfg.CONF.keystone_authtoken.admin_user, 'password': cfg.CONF.keystone_authtoken.admin_password } if cfg.CONF.keystone_authtoken.auth_version == '2': client = v2_client kwargs['tenant_name'] = ( cfg.CONF.keystone_authtoken.admin_tenant_name) elif cfg.CONF.keystone_authtoken.auth_version == '3': client = v3_client kwargs['project_name'] = ( cfg.CONF.keystone_authtoken.admin_tenant_name) kwargs['user_domain_name'] = ( cfg.CONF.keystone_authtoken_v3.admin_user_domain) kwargs['project_domain_name'] = ( cfg.CONF.keystone_authtoken_v3.admin_project_domain) else: raise Exception('Unknown keystone version!') try: kc = client.Password(**kwargs) _SESSION = session.Session( auth=kc, verify=(cfg.CONF.keystone_authtoken.cafile or not cfg.CONF.keystone_authtoken.insecure)) except Exception: with excutils.save_and_reraise_exception(): LOG.exception(_LE("Error creating Keystone session.")) return _SESSION
def _translate_amphora(self, nova_response): '''Convert a nova virtual machine into an amphora object. :param nova_response: JSON response from nova :returns: an amphora object ''' # Extract interfaces of virtual machine to populate desired amphora # fields lb_network_ip = None try: inf_list = nova_response.interface_list() for interface in inf_list: if (getattr(interface, 'net_id') == CONF.controller_worker.amp_network): lb_network_ip = getattr( interface, 'fixed_ips')[0]['ip_address'] break except Exception: LOG.debug('Extracting virtual interfaces through nova ' 'os-interfaces extension failed.') if not lb_network_ip: # Try os-networks extension # TODO(bharath) Remove when RAX doesn't need that any longer try: net_name = self._nova_client.networks.get( CONF.controller_worker.amp_network).label if net_name in nova_response.addresses: lb_network_ip = nova_response.addresses[ net_name][0]['addr'] except Exception: LOG.exception(_LE('Error retrieving nova virtual interfaces')) response = models.Amphora( compute_id=getattr(nova_response, 'id'), status=getattr(nova_response, 'status'), lb_network_ip=lb_network_ip ) return response
def revert(self, result, loadbalancer, *args, **kwargs): """Handle a failed amphora vip plug notification.""" if isinstance(result, failure.Failure): return LOG.warning(_LW("Reverting Get Amphora VRRP Interface.")) for amp in six.moves.filter( lambda amp: amp.status == constants.AMPHORA_ALLOCATED, loadbalancer.amphorae): try: self.amphora_repo.update(db_apis.get_session(), amp.id, vrrp_interface=None) except Exception as e: LOG.error( _LE("Failed to update amphora %(amp)s " "VRRP interface to None due to: " "%(except)s"), { 'amp': amp.id, 'except': e })
def plug_network(self, compute_id, network_id, ip_address=None): try: interface = self.nova_client.servers.interface_attach( server=compute_id, net_id=network_id, fixed_ip=ip_address, port_id=None) except nova_client_exceptions.NotFound as e: if 'Instance' in e.message: raise base.AmphoraNotFound(e.message) elif 'Network' in e.message: raise base.NetworkNotFound(e.message) else: raise base.PlugNetworkException(e.message) except Exception: message = _LE('Error plugging amphora (compute_id: {compute_id}) ' 'into network {network_id}.').format( compute_id=compute_id, network_id=network_id) LOG.exception(message) raise base.PlugNetworkException(message) return self._nova_interface_to_octavia_interface(compute_id, interface)
def failover_amphora(self, amphora_id): """Perform failover operations for an amphora. :param amphora_id: ID for amphora to failover :returns: None :raises AmphoraNotFound: The referenced amphora was not found """ try: amp = self._amphora_repo.get(db_apis.get_session(), id=amphora_id) stored_params = {constants.FAILED_AMPHORA: amp, constants.LOADBALANCER_ID: amp.load_balancer_id, constants.BUILD_TYPE_PRIORITY: constants.LB_CREATE_FAILOVER_PRIORITY} # if we run with anti-affinity we need to set the server group # as well if CONF.nova.enable_anti_affinity: lb = self._amphora_repo.get_all_lbs_on_amphora( db_apis.get_session(), amp.id) if lb: stored_params[constants.SERVER_GROUP_ID] = ( lb[0].server_group_id) failover_amphora_tf = self._taskflow_load( self._amphora_flows.get_failover_flow( role=amp.role, status=amp.status), store=stored_params) with tf_logging.DynamicLoggingListener( failover_amphora_tf, log=LOG, hide_inputs_outputs_of=self._exclude_result_logging_tasks): failover_amphora_tf.run() except Exception as e: with excutils.save_and_reraise_exception(): LOG.error(_LE("Failover exception: %s") % e)
def dosend(self, obj): envelope_str = status_message.wrap_envelope(obj, self.key) addrinfo = round_robin_addr(self.dests) # dest = (family, socktype, proto, canonname, sockaddr) # e.g. 0 = sock family, 4 = sockaddr - what we actually need if addrinfo is None: LOG.error( _LE('No controller address found. ' 'Unable to send heartbeat.')) return try: if addrinfo[0] == socket.AF_INET: self.v4sock.sendto(envelope_str, addrinfo[4]) elif addrinfo[0] == socket.AF_INET6: self.v6sock.sendto(envelope_str, addrinfo[4]) except socket.error: # Pass here as on amp boot it will get one or more # error: [Errno 101] Network is unreachable # while the networks are coming up # No harm in trying to send as it will still failover # if the message isn't received pass
def get_neutron_client(cls, region, service_name=None, endpoint=None, endpoint_type='publicURL', insecure=False, ca_cert=None): """Create neutron client object. :param region: The region of the service :param service_name: The name of the neutron service in the catalog :param endpoint: The endpoint of the service :param endpoint_type: The endpoint_type of the service :param insecure: Turn off certificate validation :param ca_cert: CA Cert file path :return: a Neutron Client object. :raises Exception: if the client cannot be created """ ksession = keystone.KeystoneSession() if not cls.neutron_client: kwargs = { 'region_name': region, 'session': ksession.get_session(), 'endpoint_type': endpoint_type, 'insecure': insecure } if service_name: kwargs['service_name'] = service_name if endpoint: kwargs['endpoint_override'] = endpoint if ca_cert: kwargs['ca_cert'] = ca_cert try: cls.neutron_client = neutron_client.Client( NEUTRON_VERSION, **kwargs) except Exception: with excutils.save_and_reraise_exception(): LOG.exception(_LE("Error creating Neutron client.")) return cls.neutron_client
def get_glance_client(cls, region, service_name=None, endpoint=None, endpoint_type='publicURL', insecure=False, cacert=None): """Create glance client object. :param region: The region of the service :param service_name: The name of the glance service in the catalog :param endpoint: The endpoint of the service :param endpoint_type: The endpoint_type of the service :param insecure: Turn off certificate validation :param cacert: CA Cert file path :return: a Glance Client object. :raises Exception: if the client cannot be created """ ksession = keystone.KeystoneSession() if not cls.glance_client: kwargs = { 'region_name': region, 'session': ksession.get_session(), 'interface': endpoint_type } if service_name: kwargs['service_name'] = service_name if endpoint: kwargs['endpoint'] = endpoint if endpoint.startswith("https"): kwargs['insecure'] = insecure kwargs['cacert'] = cacert try: cls.glance_client = glance_client.Client( GLANCE_VERSION, **kwargs) except Exception: with excutils.save_and_reraise_exception(): LOG.exception(_LE("Error creating Glance client.")) return cls.glance_client
def deallocate_vip(self, vip): try: port = self.get_port(vip.port_id) except base.PortNotFound: msg = ("Can't deallocate VIP because the vip port {0} cannot be " "found in neutron".format(vip.port_id)) raise base.VIPConfigurationNotFound(msg) if port.device_owner != OCTAVIA_OWNER: LOG.info( _LI("Port %s will not be deleted by Octavia as it was " "not created by Octavia."), vip.port_id) if self.sec_grp_enabled: sec_grp = self._get_lb_security_group(vip.load_balancer.id) sec_grp = sec_grp.get('id') LOG.info( _LI("Removing security group %(sg)s from port %(port)s"), { 'sg': sec_grp, 'port': vip.port_id }) raw_port = self.neutron_client.show_port(port.id) sec_grps = raw_port.get('port', {}).get('security_groups', []) if sec_grp in sec_grps: sec_grps.remove(sec_grp) port_update = {'port': {'security_groups': sec_grps}} self.neutron_client.update_port(port.id, port_update) self._delete_vip_security_group(sec_grp) return try: self.neutron_client.delete_port(vip.port_id) except Exception: message = _LE('Error deleting VIP port_id {port_id} from ' 'neutron').format(port_id=vip.port_id) LOG.exception(message) raise base.DeallocateVIPException(message) if self.sec_grp_enabled: sec_grp = self._get_lb_security_group(vip.load_balancer.id) sec_grp = sec_grp.get('id') self._delete_vip_security_group(sec_grp)
def delete_cert(cert_ref, resource_ref=None, service_name='Octavia'): """Deregister as a consumer for the specified cert. :param cert_ref: the UUID of the cert to retrieve :param resource_ref: Full HATEOAS reference to the consuming resource :param service_name: Friendly name for the consuming service :raises Exception: if deregistration fails """ connection = barbican_common.BarbicanAuth.get_barbican_client() LOG.info( _LI("Deregistering as a consumer of {0} in Barbican.").format( cert_ref)) try: connection.containers.remove_consumer(container_ref=cert_ref, name=service_name, url=resource_ref) except Exception as e: with excutils.save_and_reraise_exception(): LOG.error( _LE("Error deregistering as a consumer of {0}: {1}"). format(cert_ref, str(e)))
def execute(self, deltas): """Handle network plugging based off deltas.""" added_ports = {} for amp_id, delta in six.iteritems(deltas): added_ports[amp_id] = [] for nic in delta.add_nics: interface = self.network_driver.plug_network( delta.compute_id, nic.network_id) port = self.network_driver.get_port(interface.port_id) port.network = self.network_driver.get_network(port.network_id) for fixed_ip in port.fixed_ips: fixed_ip.subnet = self.network_driver.get_subnet( fixed_ip.subnet_id) added_ports[amp_id].append(port) for nic in delta.delete_nics: try: self.network_driver.unplug_network(delta.compute_id, nic.network_id) except base.NetworkNotFound: LOG.debug("Network %d not found ", nic.network_id) except Exception: LOG.exception(_LE("Unable to unplug network")) return added_ports
def store_cert(certificate, private_key, intermediates=None, private_key_passphrase=None, expiration=None, name='Octavia TLS Cert'): """Stores a certificate in the certificate manager. :param certificate: PEM encoded TLS certificate :param private_key: private key for the supplied certificate :param intermediates: ordered and concatenated intermediate certs :param private_key_passphrase: optional passphrase for the supplied key :param expiration: the expiration time of the cert in ISO 8601 format :param name: a friendly name for the cert :returns: the container_ref of the stored cert :raises Exception: if certificate storage fails """ connection = barbican_common.BarbicanAuth.get_barbican_client() LOG.info( _LI("Storing certificate container '{0}' in Barbican.").format( name)) certificate_secret = None private_key_secret = None intermediates_secret = None pkp_secret = None try: certificate_secret = connection.secrets.create( payload=certificate, expiration=expiration, name="Certificate") private_key_secret = connection.secrets.create( payload=private_key, expiration=expiration, name="Private Key") certificate_container = connection.containers.create_certificate( name=name, certificate=certificate_secret, private_key=private_key_secret) if intermediates: intermediates_secret = connection.secrets.create( payload=intermediates, expiration=expiration, name="Intermediates") certificate_container.intermediates = intermediates_secret if private_key_passphrase: pkp_secret = connection.secrets.create( payload=private_key_passphrase, expiration=expiration, name="Private Key Passphrase") certificate_container.private_key_passphrase = pkp_secret certificate_container.store() return certificate_container.container_ref except Exception as e: for i in [ certificate_secret, private_key_secret, intermediates_secret, pkp_secret ]: if i and i.secret_ref: old_ref = i.secret_ref try: i.delete() LOG.info( _LI("Deleted secret {0} ({1}) during rollback."). format(i.name, old_ref)) except Exception: LOG.warning( _LW("Failed to delete {0} ({1}) during rollback. This " "might not be a problem.").format( i.name, old_ref)) with excutils.save_and_reraise_exception(): LOG.error( _LE("Error storing certificate data: {0}").format(str(e)))
def update_health(self, health): """This function is to update db info based on amphora status :param health: map object that contains amphora, listener, member info :type map: string :returns: null The input health data structure is shown as below: health = { "id": self.FAKE_UUID_1, "listeners": { "listener-id-1": {"status": constants.OPEN, "pools": { "pool-id-1": {"status": constants.UP, "members": {"member-id-1": constants.ONLINE} } } } } } """ session = db_api.get_session() # We need to see if all of the listeners are reporting in expected_listener_count = 0 lbs_on_amp = self.amphora_repo.get_all_lbs_on_amphora( session, health['id']) for lb in lbs_on_amp: listener_count = self.listener_repo.count(session, load_balancer_id=lb.id) expected_listener_count += listener_count listeners = health['listeners'] # Do not update amphora health if the reporting listener count # does not match the expected listener count if len(listeners) == expected_listener_count: # if the input amphora is healthy, we update its db info self.amphora_health_repo.replace( session, health['id'], last_update=(datetime.datetime.utcnow())) else: LOG.warn( _LW('Amphora %(id)s health message reports %(found)i ' 'listeners when %(expected)i expected'), { 'id': health['id'], 'found': len(listeners), 'expected': expected_listener_count }) # We got a heartbeat so lb is healthy until proven otherwise lb_status = constants.ONLINE # update listener and nodes db information for listener_id, listener in six.iteritems(listeners): listener_status = None # OPEN = HAProxy listener status nbconn < maxconn if listener.get('status') == constants.OPEN: listener_status = constants.ONLINE # FULL = HAProxy listener status not nbconn < maxconn elif listener.get('status') == constants.FULL: listener_status = constants.DEGRADED if lb_status == constants.ONLINE: lb_status = constants.DEGRADED else: LOG.warn( _LW('Listener %(list)s reported status of ' '%(status)s'), { 'list': listener_id, 'status': listener.get('status') }) try: if listener_status is not None: self._update_status_and_emit_event(session, self.listener_repo, constants.LISTENER, listener_id, listener_status) except sqlalchemy.orm.exc.NoResultFound: LOG.error(_LE("Listener %s is not in DB"), listener_id) pools = listener['pools'] for pool_id, pool in six.iteritems(pools): pool_status = None # UP = HAProxy backend has working or no servers if pool.get('status') == constants.UP: pool_status = constants.ONLINE # DOWN = HAProxy backend has no working servers elif pool.get('status') == constants.DOWN: pool_status = constants.ERROR lb_status = constants.ERROR else: LOG.warn( _LW('Pool %(pool)s reported status of ' '%(status)s'), { 'pool': pool_id, 'status': pool.get('status') }) members = pool['members'] for member_id, status in six.iteritems(members): member_status = None if status == constants.UP: member_status = constants.ONLINE elif status == constants.DOWN: member_status = constants.ERROR if pool_status == constants.ONLINE: pool_status = constants.DEGRADED if lb_status == constants.ONLINE: lb_status = constants.DEGRADED elif status == constants.NO_CHECK: member_status = constants.NO_MONITOR else: LOG.warn( _LW('Member %(mem)s reported status of ' '%(status)s'), { 'mem': member_id, 'status': status }) try: if member_status is not None: self._update_status_and_emit_event( session, self.member_repo, constants.MEMBER, member_id, member_status) except sqlalchemy.orm.exc.NoResultFound: LOG.error( _LE("Member %s is not able to update " "in DB"), member_id) try: if pool_status is not None: self._update_status_and_emit_event( session, self.pool_repo, constants.POOL, pool_id, pool_status) except sqlalchemy.orm.exc.NoResultFound: LOG.error(_LE("Pool %s is not in DB"), pool_id) # Update the load balancer status last # TODO(sbalukoff): This logic will need to be adjusted if we # start supporting multiple load balancers per amphora lb_id = self.amphora_repo.get(session, id=health['id']).load_balancer_id if lb_id is not None: try: self._update_status_and_emit_event(session, self.loadbalancer_repo, constants.LOADBALANCER, lb_id, lb_status) except sqlalchemy.orm.exc.NoResultFound: LOG.error(_LE("Load balancer %s is not in DB"), lb_id)
def delete_listener(self, listener_id): self._check_listener_exists(listener_id) # check if that haproxy is still running and if stop it if os.path.exists(util.pid_path(listener_id)) and os.path.exists( os.path.join('/proc', util.get_haproxy_pid(listener_id))): cmd = "/usr/sbin/service haproxy-{0} stop".format(listener_id) try: subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT) except subprocess.CalledProcessError as e: LOG.error(_LE("Failed to stop HAProxy service: %s"), e) return flask.make_response( flask.jsonify( dict(message="Error stopping haproxy", details=e.output)), 500) # parse config and delete stats socket try: cfg = self._parse_haproxy_file(listener_id) os.remove(cfg['stats_socket']) except Exception: pass # delete the ssl files try: shutil.rmtree(self._cert_dir(listener_id)) except Exception: pass # disable the service init_system = util.get_os_init_system() init_path = util.init_path(listener_id, init_system) if init_system == consts.INIT_SYSTEMD: init_disable_cmd = "systemctl disable haproxy-{list}".format( list=listener_id) elif init_system == consts.INIT_SYSVINIT: init_disable_cmd = "insserv -r {file}".format(file=init_path) elif init_system != consts.INIT_UPSTART: raise util.UnknownInitError() if init_system != consts.INIT_UPSTART: try: subprocess.check_output(init_disable_cmd.split(), stderr=subprocess.STDOUT) except subprocess.CalledProcessError as e: LOG.error( _LE("Failed to disable haproxy-%(list)s " "service: %(err)s"), { 'list': listener_id, 'err': e }) return flask.make_response( flask.jsonify( dict(message="Error disabling haproxy-{0} service". format(listener_id), details=e.output)), 500) # delete the directory + init script for that listener shutil.rmtree(util.haproxy_dir(listener_id)) if os.path.exists(init_path): os.remove(init_path) return flask.jsonify({'message': 'OK'})
def upload_haproxy_config(self, amphora_id, listener_id): """Upload the haproxy config :param amphora_id: The id of the amphora to update :param listener_id: The id of the listener """ stream = Wrapped(flask.request.stream) # We have to hash here because HAProxy has a string length limitation # in the configuration file "peer <peername>" lines peer_name = octavia_utils.base64_sha1_string(amphora_id).rstrip('=') if not os.path.exists(util.haproxy_dir(listener_id)): os.makedirs(util.haproxy_dir(listener_id)) name = os.path.join(util.haproxy_dir(listener_id), 'haproxy.cfg.new') flags = os.O_WRONLY | os.O_CREAT | os.O_TRUNC # mode 00600 mode = stat.S_IRUSR | stat.S_IWUSR with os.fdopen(os.open(name, flags, mode), 'wb') as file: b = stream.read(BUFFER) while (b): file.write(b) b = stream.read(BUFFER) # use haproxy to check the config cmd = "haproxy -c -L {peer} -f {config_file}".format(config_file=name, peer=peer_name) try: subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT) except subprocess.CalledProcessError as e: LOG.error(_LE("Failed to verify haproxy file: %s"), e) os.remove(name) # delete file return flask.make_response( flask.jsonify(dict(message="Invalid request", details=e.output)), 400) # file ok - move it os.rename(name, util.config_path(listener_id)) try: init_system = util.get_os_init_system() LOG.debug('Found init system: {0}'.format(init_system)) init_path = util.init_path(listener_id, init_system) if init_system == consts.INIT_SYSTEMD: template = SYSTEMD_TEMPLATE init_enable_cmd = "systemctl enable haproxy-{list}".format( list=listener_id) elif init_system == consts.INIT_UPSTART: template = UPSTART_TEMPLATE elif init_system == consts.INIT_SYSVINIT: template = SYSVINIT_TEMPLATE init_enable_cmd = "insserv {file}".format(file=init_path) else: raise util.UnknownInitError() except util.UnknownInitError: LOG.error(_LE("Unknown init system found.")) return flask.make_response( flask.jsonify( dict( message="Unknown init system in amphora", details="The amphora image is running an unknown init " "system. We can't create the init configuration " "file for the load balancing process.")), 500) if init_system == consts.INIT_SYSTEMD: # mode 00644 mode = (stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IROTH) else: # mode 00755 mode = (stat.S_IRWXU | stat.S_IRGRP | stat.S_IXGRP | stat.S_IROTH | stat.S_IXOTH) if not os.path.exists(init_path): with os.fdopen(os.open(init_path, flags, mode), 'w') as text_file: text = template.render( peer_name=peer_name, haproxy_pid=util.pid_path(listener_id), haproxy_cmd=util.CONF.haproxy_amphora.haproxy_cmd, haproxy_cfg=util.config_path(listener_id), respawn_count=util.CONF.haproxy_amphora.respawn_count, respawn_interval=( util.CONF.haproxy_amphora.respawn_interval), amphora_nsname=consts.AMPHORA_NAMESPACE, HasIFUPAll=self._osutils.has_ifup_all()) text_file.write(text) # Make sure the new service is enabled on boot if init_system != consts.INIT_UPSTART: try: subprocess.check_output(init_enable_cmd.split(), stderr=subprocess.STDOUT) except subprocess.CalledProcessError as e: LOG.error( _LE("Failed to enable haproxy-%(list)s " "service: %(err)s"), { 'list': listener_id, 'err': e }) return flask.make_response( flask.jsonify( dict(message="Error enabling haproxy-{0} service". format(listener_id), details=e.output)), 500) res = flask.make_response(flask.jsonify({'message': 'OK'}), 202) res.headers['ETag'] = stream.get_md5() return res
class InvalidString(OctaviaException): message = _LE('Invalid characters in %(what)s')
class InvalidL7PolicyAction(APIException): message = _LE('Invalid L7 Policy action specified: %(action)s') code = 400
class ComputeBuildQueueTimeoutException(OctaviaException): message = _LE('Failed to get an amphora build slot.')
class InvalidL7PolicyArgs(APIException): message = _LE('Invalid L7 Policy arguments: %(msg)s') code = 400
class InvalidRegex(OctaviaException): message = _LE('Unable to parse regular expression: %(e)s')
class ServerGroupObjectDeleteException(OctaviaException): message = _LE('Failed to delete server group object.')
class InvalidURL(OctaviaException): message = _LE('Not a valid URL: %(url)s')
class InvalidL7Rule(OctaviaException): message = _LE('Invalid L7 Rule: $(msg)s')