[hopem, r=gnuoy]
Fixes network-vlan-ranges parsing Partially-Closes-Bug: 1451095
This commit is contained in:
commit
12eff6345e
@ -153,8 +153,10 @@ options:
|
|||||||
type: string
|
type: string
|
||||||
default: "physnet1:1000:2000"
|
default: "physnet1:1000:2000"
|
||||||
description: |
|
description: |
|
||||||
Space-delimited list of Neutron network-provider & vlan-id-ranges using
|
Space-delimited list of <physical_network>:<vlan_min>:<vlan_max> or
|
||||||
format "<provider>:<start>:<end> ...".
|
<physical_network> specifying physical_network names usable for VLAN
|
||||||
|
provider and tenant networks, as well as ranges of VLAN tags on each
|
||||||
|
available for allocation to tenant networks.
|
||||||
# Network configuration options
|
# Network configuration options
|
||||||
# by default all access is over 'private-address'
|
# by default all access is over 'private-address'
|
||||||
os-data-network:
|
os-data-network:
|
||||||
|
@ -52,6 +52,8 @@ from charmhelpers.core.strutils import (
|
|||||||
bool_from_string,
|
bool_from_string,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
DC_RESOURCE_NAME = 'DC'
|
||||||
|
|
||||||
|
|
||||||
class HAIncompleteConfig(Exception):
|
class HAIncompleteConfig(Exception):
|
||||||
pass
|
pass
|
||||||
@ -95,6 +97,27 @@ def is_clustered():
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def is_crm_dc():
|
||||||
|
"""
|
||||||
|
Determine leadership by querying the pacemaker Designated Controller
|
||||||
|
"""
|
||||||
|
cmd = ['crm', 'status']
|
||||||
|
try:
|
||||||
|
status = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
|
||||||
|
if not isinstance(status, six.text_type):
|
||||||
|
status = six.text_type(status, "utf-8")
|
||||||
|
except subprocess.CalledProcessError:
|
||||||
|
return False
|
||||||
|
current_dc = ''
|
||||||
|
for line in status.split('\n'):
|
||||||
|
if line.startswith('Current DC'):
|
||||||
|
# Current DC: juju-lytrusty-machine-2 (168108163) - partition with quorum
|
||||||
|
current_dc = line.split(':')[1].split()[0]
|
||||||
|
if current_dc == get_unit_hostname():
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
@retry_on_exception(5, base_delay=2, exc_type=CRMResourceNotFound)
|
@retry_on_exception(5, base_delay=2, exc_type=CRMResourceNotFound)
|
||||||
def is_crm_leader(resource, retry=False):
|
def is_crm_leader(resource, retry=False):
|
||||||
"""
|
"""
|
||||||
@ -104,6 +127,8 @@ def is_crm_leader(resource, retry=False):
|
|||||||
We allow this operation to be retried to avoid the possibility of getting a
|
We allow this operation to be retried to avoid the possibility of getting a
|
||||||
false negative. See LP #1396246 for more info.
|
false negative. See LP #1396246 for more info.
|
||||||
"""
|
"""
|
||||||
|
if resource == DC_RESOURCE_NAME:
|
||||||
|
return is_crm_dc()
|
||||||
cmd = ['crm', 'resource', 'show', resource]
|
cmd = ['crm', 'resource', 'show', resource]
|
||||||
try:
|
try:
|
||||||
status = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
|
status = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
|
||||||
|
@ -256,11 +256,14 @@ def network_manager():
|
|||||||
def parse_mappings(mappings):
|
def parse_mappings(mappings):
|
||||||
parsed = {}
|
parsed = {}
|
||||||
if mappings:
|
if mappings:
|
||||||
mappings = mappings.split(' ')
|
mappings = mappings.split()
|
||||||
for m in mappings:
|
for m in mappings:
|
||||||
p = m.partition(':')
|
p = m.partition(':')
|
||||||
if p[1] == ':':
|
key = p[0].strip()
|
||||||
parsed[p[0].strip()] = p[2].strip()
|
if p[1]:
|
||||||
|
parsed[key] = p[2].strip()
|
||||||
|
else:
|
||||||
|
parsed[key] = ''
|
||||||
|
|
||||||
return parsed
|
return parsed
|
||||||
|
|
||||||
@ -283,13 +286,13 @@ def parse_data_port_mappings(mappings, default_bridge='br-data'):
|
|||||||
Returns dict of the form {bridge:port}.
|
Returns dict of the form {bridge:port}.
|
||||||
"""
|
"""
|
||||||
_mappings = parse_mappings(mappings)
|
_mappings = parse_mappings(mappings)
|
||||||
if not _mappings:
|
if not _mappings or list(_mappings.values()) == ['']:
|
||||||
if not mappings:
|
if not mappings:
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
# For backwards-compatibility we need to support port-only provided in
|
# For backwards-compatibility we need to support port-only provided in
|
||||||
# config.
|
# config.
|
||||||
_mappings = {default_bridge: mappings.split(' ')[0]}
|
_mappings = {default_bridge: mappings.split()[0]}
|
||||||
|
|
||||||
bridges = _mappings.keys()
|
bridges = _mappings.keys()
|
||||||
ports = _mappings.values()
|
ports = _mappings.values()
|
||||||
@ -309,6 +312,8 @@ def parse_vlan_range_mappings(mappings):
|
|||||||
|
|
||||||
Mappings must be a space-delimited list of provider:start:end mappings.
|
Mappings must be a space-delimited list of provider:start:end mappings.
|
||||||
|
|
||||||
|
The start:end range is optional and may be omitted.
|
||||||
|
|
||||||
Returns dict of the form {provider: (start, end)}.
|
Returns dict of the form {provider: (start, end)}.
|
||||||
"""
|
"""
|
||||||
_mappings = parse_mappings(mappings)
|
_mappings = parse_mappings(mappings)
|
||||||
|
@ -21,12 +21,14 @@
|
|||||||
# Charm Helpers Developers <juju@lists.ubuntu.com>
|
# Charm Helpers Developers <juju@lists.ubuntu.com>
|
||||||
|
|
||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
|
from functools import wraps
|
||||||
import os
|
import os
|
||||||
import json
|
import json
|
||||||
import yaml
|
import yaml
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import errno
|
import errno
|
||||||
|
import tempfile
|
||||||
from subprocess import CalledProcessError
|
from subprocess import CalledProcessError
|
||||||
|
|
||||||
import six
|
import six
|
||||||
@ -58,15 +60,17 @@ def cached(func):
|
|||||||
|
|
||||||
will cache the result of unit_get + 'test' for future calls.
|
will cache the result of unit_get + 'test' for future calls.
|
||||||
"""
|
"""
|
||||||
|
@wraps(func)
|
||||||
def wrapper(*args, **kwargs):
|
def wrapper(*args, **kwargs):
|
||||||
global cache
|
global cache
|
||||||
key = str((func, args, kwargs))
|
key = str((func, args, kwargs))
|
||||||
try:
|
try:
|
||||||
return cache[key]
|
return cache[key]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
res = func(*args, **kwargs)
|
pass # Drop out of the exception handler scope.
|
||||||
cache[key] = res
|
res = func(*args, **kwargs)
|
||||||
return res
|
cache[key] = res
|
||||||
|
return res
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
@ -178,7 +182,7 @@ def local_unit():
|
|||||||
|
|
||||||
def remote_unit():
|
def remote_unit():
|
||||||
"""The remote unit for the current relation hook"""
|
"""The remote unit for the current relation hook"""
|
||||||
return os.environ['JUJU_REMOTE_UNIT']
|
return os.environ.get('JUJU_REMOTE_UNIT', None)
|
||||||
|
|
||||||
|
|
||||||
def service_name():
|
def service_name():
|
||||||
@ -250,6 +254,12 @@ class Config(dict):
|
|||||||
except KeyError:
|
except KeyError:
|
||||||
return (self._prev_dict or {})[key]
|
return (self._prev_dict or {})[key]
|
||||||
|
|
||||||
|
def get(self, key, default=None):
|
||||||
|
try:
|
||||||
|
return self[key]
|
||||||
|
except KeyError:
|
||||||
|
return default
|
||||||
|
|
||||||
def keys(self):
|
def keys(self):
|
||||||
prev_keys = []
|
prev_keys = []
|
||||||
if self._prev_dict is not None:
|
if self._prev_dict is not None:
|
||||||
@ -353,14 +363,29 @@ def relation_set(relation_id=None, relation_settings=None, **kwargs):
|
|||||||
"""Set relation information for the current unit"""
|
"""Set relation information for the current unit"""
|
||||||
relation_settings = relation_settings if relation_settings else {}
|
relation_settings = relation_settings if relation_settings else {}
|
||||||
relation_cmd_line = ['relation-set']
|
relation_cmd_line = ['relation-set']
|
||||||
|
accepts_file = "--file" in subprocess.check_output(
|
||||||
|
relation_cmd_line + ["--help"])
|
||||||
if relation_id is not None:
|
if relation_id is not None:
|
||||||
relation_cmd_line.extend(('-r', relation_id))
|
relation_cmd_line.extend(('-r', relation_id))
|
||||||
for k, v in (list(relation_settings.items()) + list(kwargs.items())):
|
settings = relation_settings.copy()
|
||||||
if v is None:
|
settings.update(kwargs)
|
||||||
relation_cmd_line.append('{}='.format(k))
|
if accepts_file:
|
||||||
else:
|
# --file was introduced in Juju 1.23.2. Use it by default if
|
||||||
relation_cmd_line.append('{}={}'.format(k, v))
|
# available, since otherwise we'll break if the relation data is
|
||||||
subprocess.check_call(relation_cmd_line)
|
# too big. Ideally we should tell relation-set to read the data from
|
||||||
|
# stdin, but that feature is broken in 1.23.2: Bug #1454678.
|
||||||
|
with tempfile.NamedTemporaryFile(delete=False) as settings_file:
|
||||||
|
settings_file.write(yaml.safe_dump(settings).encode("utf-8"))
|
||||||
|
subprocess.check_call(
|
||||||
|
relation_cmd_line + ["--file", settings_file.name])
|
||||||
|
os.remove(settings_file.name)
|
||||||
|
else:
|
||||||
|
for key, value in settings.items():
|
||||||
|
if value is None:
|
||||||
|
relation_cmd_line.append('{}='.format(key))
|
||||||
|
else:
|
||||||
|
relation_cmd_line.append('{}={}'.format(key, value))
|
||||||
|
subprocess.check_call(relation_cmd_line)
|
||||||
# Flush cache of any relation-gets for local unit
|
# Flush cache of any relation-gets for local unit
|
||||||
flush(local_unit())
|
flush(local_unit())
|
||||||
|
|
||||||
@ -509,6 +534,11 @@ def unit_get(attribute):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def unit_public_ip():
|
||||||
|
"""Get this unit's public IP address"""
|
||||||
|
return unit_get('public-address')
|
||||||
|
|
||||||
|
|
||||||
def unit_private_ip():
|
def unit_private_ip():
|
||||||
"""Get this unit's private IP address"""
|
"""Get this unit's private IP address"""
|
||||||
return unit_get('private-address')
|
return unit_get('private-address')
|
||||||
@ -605,3 +635,49 @@ def action_fail(message):
|
|||||||
|
|
||||||
The results set by action_set are preserved."""
|
The results set by action_set are preserved."""
|
||||||
subprocess.check_call(['action-fail', message])
|
subprocess.check_call(['action-fail', message])
|
||||||
|
|
||||||
|
|
||||||
|
def status_set(workload_state, message):
|
||||||
|
"""Set the workload state with a message
|
||||||
|
|
||||||
|
Use status-set to set the workload state with a message which is visible
|
||||||
|
to the user via juju status. If the status-set command is not found then
|
||||||
|
assume this is juju < 1.23 and juju-log the message unstead.
|
||||||
|
|
||||||
|
workload_state -- valid juju workload state.
|
||||||
|
message -- status update message
|
||||||
|
"""
|
||||||
|
valid_states = ['maintenance', 'blocked', 'waiting', 'active']
|
||||||
|
if workload_state not in valid_states:
|
||||||
|
raise ValueError(
|
||||||
|
'{!r} is not a valid workload state'.format(workload_state)
|
||||||
|
)
|
||||||
|
cmd = ['status-set', workload_state, message]
|
||||||
|
try:
|
||||||
|
ret = subprocess.call(cmd)
|
||||||
|
if ret == 0:
|
||||||
|
return
|
||||||
|
except OSError as e:
|
||||||
|
if e.errno != errno.ENOENT:
|
||||||
|
raise
|
||||||
|
log_message = 'status-set failed: {} {}'.format(workload_state,
|
||||||
|
message)
|
||||||
|
log(log_message, level='INFO')
|
||||||
|
|
||||||
|
|
||||||
|
def status_get():
|
||||||
|
"""Retrieve the previously set juju workload state
|
||||||
|
|
||||||
|
If the status-set command is not found then assume this is juju < 1.23 and
|
||||||
|
return 'unknown'
|
||||||
|
"""
|
||||||
|
cmd = ['status-get']
|
||||||
|
try:
|
||||||
|
raw_status = subprocess.check_output(cmd, universal_newlines=True)
|
||||||
|
status = raw_status.rstrip()
|
||||||
|
return status
|
||||||
|
except OSError as e:
|
||||||
|
if e.errno == errno.ENOENT:
|
||||||
|
return 'unknown'
|
||||||
|
else:
|
||||||
|
raise
|
||||||
|
@ -90,7 +90,7 @@ def service_available(service_name):
|
|||||||
['service', service_name, 'status'],
|
['service', service_name, 'status'],
|
||||||
stderr=subprocess.STDOUT).decode('UTF-8')
|
stderr=subprocess.STDOUT).decode('UTF-8')
|
||||||
except subprocess.CalledProcessError as e:
|
except subprocess.CalledProcessError as e:
|
||||||
return 'unrecognized service' not in e.output
|
return b'unrecognized service' not in e.output
|
||||||
else:
|
else:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import json
|
import json
|
||||||
from collections import Iterable
|
from collections import Iterable, OrderedDict
|
||||||
|
|
||||||
from charmhelpers.core import host
|
from charmhelpers.core import host
|
||||||
from charmhelpers.core import hookenv
|
from charmhelpers.core import hookenv
|
||||||
@ -119,7 +119,7 @@ class ServiceManager(object):
|
|||||||
"""
|
"""
|
||||||
self._ready_file = os.path.join(hookenv.charm_dir(), 'READY-SERVICES.json')
|
self._ready_file = os.path.join(hookenv.charm_dir(), 'READY-SERVICES.json')
|
||||||
self._ready = None
|
self._ready = None
|
||||||
self.services = {}
|
self.services = OrderedDict()
|
||||||
for service in services or []:
|
for service in services or []:
|
||||||
service_name = service['service']
|
service_name = service['service']
|
||||||
self.services[service_name] = service
|
self.services[service_name] = service
|
||||||
|
@ -158,7 +158,7 @@ def filter_installed_packages(packages):
|
|||||||
|
|
||||||
def apt_cache(in_memory=True):
|
def apt_cache(in_memory=True):
|
||||||
"""Build and return an apt cache"""
|
"""Build and return an apt cache"""
|
||||||
import apt_pkg
|
from apt import apt_pkg
|
||||||
apt_pkg.init()
|
apt_pkg.init()
|
||||||
if in_memory:
|
if in_memory:
|
||||||
apt_pkg.config.set("Dir::Cache::pkgcache", "")
|
apt_pkg.config.set("Dir::Cache::pkgcache", "")
|
||||||
|
@ -930,7 +930,8 @@ class TestQuantumAgentReallocation(CharmTestCase):
|
|||||||
'process_name': 'neutron-nvsd-agent',
|
'process_name': 'neutron-nvsd-agent',
|
||||||
'executable_name': '/usr/local/bin/neutron-nvsd-agent',
|
'executable_name': '/usr/local/bin/neutron-nvsd-agent',
|
||||||
'config_files': ['/etc/neutron/neutron.conf',
|
'config_files': ['/etc/neutron/neutron.conf',
|
||||||
'/etc/neutron/plugins/oneconvergence/nvsdplugin.ini'],
|
'/etc/neutron/plugins/oneconvergence/'
|
||||||
|
'nvsdplugin.ini'],
|
||||||
'log_file': '/var/log/neutron/nvsd-agent.log',
|
'log_file': '/var/log/neutron/nvsd-agent.log',
|
||||||
}
|
}
|
||||||
neutron_plugin_openflow_context = {
|
neutron_plugin_openflow_context = {
|
||||||
|
Loading…
Reference in New Issue
Block a user